Skip to content

Commit 3118281

Browse files
authored
Merge pull request #356 from webpack/develop
Deploy
2 parents babb915 + 7377eb2 commit 3118281

File tree

11 files changed

+1130
-1
lines changed

11 files changed

+1130
-1
lines changed

antwar.config.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@ module.exports = {
106106
/^\.\/.*\.md$/
107107
);
108108
}
109+
),
110+
vote: voteList(
111+
'Voting',
112+
{
113+
index: true,
114+
feedback: true,
115+
moneyDistribution: true,
116+
}
109117
)
110118
}
111119
};
@@ -167,3 +175,45 @@ function processPage() {
167175
}
168176
};
169177
}
178+
179+
function voteList(title, lists) {
180+
return {
181+
title: title,
182+
path: function() {
183+
function context(request) {
184+
var name = /^\.\/(.*)\.md$/.exec(request)[1];
185+
return {
186+
name: name,
187+
__content: '' // make antwar happy
188+
};
189+
}
190+
context.keys = function() {
191+
return Object.keys(lists).map(k => './' + k + '.md');
192+
};
193+
return context;
194+
},
195+
processPage: {
196+
url: function(o) {
197+
return 'vote/' + o.file.name;
198+
},
199+
name: function(o) {
200+
return o.file.name;
201+
},
202+
anchors: function(o) {
203+
return [];
204+
},
205+
content: function(o) {
206+
return '';
207+
}
208+
},
209+
layouts: {
210+
index: function() {
211+
return require('./components/vote/list.jsx').default
212+
},
213+
page: function() {
214+
return require('./components/vote/list.jsx').default
215+
}
216+
},
217+
redirects: {} // <from>: <to>
218+
};
219+
}

components/vote/api.dev.js

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
let usedCurrencies = {
2+
influence: 100,
3+
goldenInfluence: 100
4+
};
5+
let totalCurrencies = {
6+
influence: 1000,
7+
goldenInfluence: 300
8+
};
9+
let lists = {
10+
todo: {
11+
possibleVotes: [
12+
{
13+
name: "influence",
14+
currency: "influence",
15+
maximum: 30,
16+
score: 1,
17+
step: 2,
18+
color: "blue"
19+
},
20+
{
21+
name: "golden",
22+
currency: "goldenInfluence",
23+
score: 1,
24+
step: 10,
25+
color: "#bfa203"
26+
},
27+
{
28+
name: "thumb",
29+
minimum: -5,
30+
maximum: 5,
31+
score: 5,
32+
step: 1,
33+
color: "#535353"
34+
}
35+
],
36+
items: [
37+
{ id: "1234", list: "todo", title: "Finish up MVP documentation", description: "Take care for the remaining issues in the webpack.js.org repo which are relevant for the MVP.", influence: 15 },
38+
{ id: "2345", list: "todo", title: "Review whole documentation", description: "Read over **all** of the documentation to find errors.", golden: 20 },
39+
]
40+
}
41+
};
42+
let allItems = {
43+
"1234": lists.todo.items[0],
44+
"2345": lists.todo.items[1],
45+
};
46+
47+
function delay(time) {
48+
return new Promise(function (fulfill) {
49+
setTimeout(fulfill, time);
50+
});
51+
}
52+
53+
function clone(json) {
54+
return JSON.parse(JSON.stringify(json));
55+
}
56+
57+
export function isLoginActive() {
58+
return /^\?login=/.test(window.location.search);
59+
}
60+
61+
export function startLogin(callbackUrl) {
62+
window.location.search = "?login=" + encodeURIComponent(callbackUrl);
63+
return Promise.resolve();
64+
}
65+
66+
export function continueLogin() {
67+
if(/^\?login=/.test(window.location.search)) {
68+
return delay(2000).then(() => {
69+
setTimeout(() => window.location = decodeURIComponent(window.location.search.substr(7), 100));
70+
return "developer";
71+
});
72+
}
73+
return Promise.resolve();
74+
}
75+
76+
export function getSelf(token) {
77+
if(token !== "developer")
78+
return Promise.reject(new Error("Not logged in as developer"));
79+
return delay(500).then(() => ({
80+
login: "dev",
81+
name: "Developer",
82+
avatar: "https://github.com/webpack.png",
83+
currencies: [
84+
{ name: "influence", displayName: "Influence", description: "Some **description**", value: totalCurrencies.influence, used: usedCurrencies.influence, remaining: totalCurrencies.influence - usedCurrencies.influence },
85+
{ name: "goldenInfluence", displayName: "Golden Influence", description: "Some **description**", value: totalCurrencies.goldenInfluence, used: usedCurrencies.goldenInfluence, remaining: totalCurrencies.goldenInfluence - usedCurrencies.goldenInfluence }
86+
]
87+
}));
88+
}
89+
90+
export function getList(token, name) {
91+
const loggedIn = token === "developer";
92+
const listData = lists[name];
93+
return delay(500).then(() => ({
94+
name: name,
95+
displayName: "DEV: " + name,
96+
description: "Some **description**",
97+
lockable: true,
98+
deletable: true,
99+
archivable: true,
100+
isAdmin: true,
101+
possibleVotes: listData.possibleVotes,
102+
items: lists[name].items.map(item => {
103+
const votes = listData.possibleVotes.map(pv => ({
104+
name: pv.name,
105+
votes: (item[pv.name] || 0) + Math.floor(Math.random() * 100)
106+
}));
107+
const score = listData.possibleVotes.map((pv, i) => {
108+
return pv.score * votes[i].votes;
109+
}).reduce((a, b) => a + b, 0);
110+
return {
111+
id: item.id,
112+
list: item.list,
113+
title: item.title,
114+
description: item.description,
115+
votes,
116+
userVotes: loggedIn ? listData.possibleVotes.map(pv => ({
117+
name: pv.name,
118+
votes: item[pv.name] || 0
119+
})) : undefined,
120+
score
121+
};
122+
}).sort((a, b) => b.score - a.score)
123+
}));
124+
}
125+
126+
export function createItem(token, list, title, description) {
127+
if(token !== "developer")
128+
return Promise.reject(new Error("Not logged in as developer"));
129+
let newItem = {
130+
id: Math.random() + "",
131+
list,
132+
title,
133+
description
134+
};
135+
allItems[newItem.id] = newItem;
136+
lists[list].items.push(newItem);
137+
return delay(500).then(() => ({
138+
...newItem,
139+
votes: lists[list].possibleVotes.map(pv => ({
140+
name: pv.name,
141+
votes: 0
142+
})),
143+
userVotes: lists[list].possibleVotes.map(pv => ({
144+
name: pv.name,
145+
votes: 0
146+
})),
147+
score: 0
148+
}));
149+
}
150+
151+
export function vote(token, itemId, voteName, value) {
152+
if(token !== "developer")
153+
return Promise.reject(new Error("Not logged in as developer"));
154+
var listId = allItems[itemId].list;
155+
let listData = lists[listId];
156+
let pv = listData.possibleVotes.filter(pv => pv.name === voteName)[0];
157+
if(pv.currency) {
158+
usedCurrencies[pv.currency] += value;
159+
}
160+
allItems[itemId][voteName] = (allItems[itemId][voteName] || 0) + value;
161+
return delay(500).then(() => ({
162+
ok: true
163+
}));
164+
}

components/vote/api.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import {
2+
isLoginActive as devIsLoginActive,
3+
startLogin as devStartLogin,
4+
continueLogin as devContinueLogin,
5+
getSelf as devGetSelf,
6+
getList as devGetList,
7+
createItem as devCreateItem,
8+
vote as devVote
9+
} from "./api.dev";
10+
11+
const API_URL = "https://ipttmcpme6.execute-api.us-east-1.amazonaws.com/production";
12+
const GITHUB_CLIENT_ID = "4d355e2799cb8926c665";
13+
const PRODUCTION_HOST = "webpack.js.org";
14+
15+
// You can test the production mode with a host entry,
16+
// or by setting PRODUCTION_HOST to "localhost:3000" and stealing localStorage.voteAppToken from the production side.
17+
18+
export function isLoginActive() {
19+
if(window.location.host !== PRODUCTION_HOST)
20+
return devIsLoginActive();
21+
return /^\?code=([^&]*)&state=([^&]*)/.test(window.location.search);
22+
}
23+
24+
export function startLogin(callbackUrl) {
25+
if(window.location.host !== PRODUCTION_HOST)
26+
return devStartLogin(callbackUrl);
27+
let state = "" + Math.random();
28+
window.localStorage.githubState = state;
29+
window.location = "https://github.com/login/oauth/authorize?client_id=" + GITHUB_CLIENT_ID + "&scope=user:email&state=" + state + "&allow_signup=false&redirect_uri=" + encodeURIComponent(callbackUrl);
30+
return Promise.resolve();
31+
}
32+
33+
export function continueLogin() {
34+
if(window.location.host !== PRODUCTION_HOST)
35+
return devContinueLogin();
36+
const match = /^\?code=([^&]*)&state=([^&]*)/.exec(window.location.search);
37+
if(match) {
38+
return login(match[1], match[2]).then(result => {
39+
setTimeout(() => {
40+
let href = window.location.href;
41+
window.location = href.substr(0, href.length - window.location.search.length);
42+
}, 100);
43+
return result;
44+
});
45+
}
46+
return Promise.resolve();
47+
}
48+
49+
function login(code, state) {
50+
if(state !== window.localStorage.githubState)
51+
return Promise.reject(new Error("Request state doesn't match (Login was triggered by 3rd party)"));
52+
delete window.localStorage.githubState;
53+
return fetch(API_URL + "/login", {
54+
headers: {
55+
"Content-Type": "application/json"
56+
},
57+
method: "POST",
58+
body: JSON.stringify({
59+
code,
60+
state
61+
})
62+
}).then((res) => res.json()).then(result => {
63+
return result.token;
64+
});
65+
}
66+
67+
export function getSelf(token) {
68+
if(window.location.host !== PRODUCTION_HOST)
69+
return devGetSelf(token);
70+
return fetch(API_URL + "/self?token=" + token, {
71+
mode: "cors"
72+
}).then((res) => res.json());
73+
}
74+
75+
export function getList(token, name) {
76+
if(window.location.host !== PRODUCTION_HOST)
77+
return devGetList(token, name);
78+
return fetch(API_URL + "/list/" + name + (token ? "?token=" + token : ""), {
79+
mode: "cors"
80+
}).then((res) => res.json());
81+
}
82+
83+
export function createItem(token, list, title, description) {
84+
if(window.location.host !== PRODUCTION_HOST)
85+
return devCreateItem(token, list, title, description);
86+
return fetch(API_URL + "/list/" + list + "?token=" + token, {
87+
headers: {
88+
"Content-Type": "application/json"
89+
},
90+
body: JSON.stringify({
91+
title,
92+
description
93+
}),
94+
method: "POST"
95+
}).then((res) => res.json());
96+
}
97+
98+
export function vote(token, itemId, voteName, value) {
99+
if(window.location.host !== PRODUCTION_HOST)
100+
return devVote(token, itemId, voteName, value);
101+
return fetch(API_URL + "/vote/" + itemId + "/" + voteName + "?token=" + token, {
102+
headers: {
103+
"Content-Type": "application/json"
104+
},
105+
body: JSON.stringify({
106+
count: value
107+
}),
108+
method: "POST"
109+
}).then((res) => res.json()).then(result => {
110+
return true;
111+
});
112+
}

0 commit comments

Comments
 (0)