Files: 6071577daff8cc01e7241b5799fb112902aec545 / views / show.js
5824 bytesRaw
1 | const { h, Struct, computed, resolve } = require('mutant') |
2 | const pull = require('pull-stream') |
3 | const printTime = require('../lib/print-time') |
4 | |
5 | module.exports = function ScryShow (opts) { |
6 | const { |
7 | poll, |
8 | myFeedId, |
9 | scuttle, |
10 | name = k => k.slice(0, 9), |
11 | avatar = k => h('img'), |
12 | testing = false |
13 | } = opts |
14 | |
15 | const state = Struct(initialState()) |
16 | fetchState() |
17 | watchForUpdates(fetchState) |
18 | |
19 | return h('ScryShow', [ |
20 | h('h1', state.now.title), |
21 | h('div.closes-at', [ |
22 | 'Closes at ', |
23 | computed(state.now.closesAt, t => t ? t.toString() : '') |
24 | ]), |
25 | ScryShowResults(), |
26 | h('div.actions', [ |
27 | PublishBtn() |
28 | ]) |
29 | ]) |
30 | |
31 | function PublishBtn () { |
32 | return computed([state.now, state.next], (current, next) => { |
33 | if (!next.isEditing) return |
34 | if (next.isPublishing) return h('button', h('i.fa.fa-spin.fa-pulse')) |
35 | |
36 | const newPosition = current.position.join() !== next.position.join() |
37 | return h('button', |
38 | { |
39 | className: newPosition ? '-primary' : '', |
40 | disabled: !newPosition, |
41 | 'ev-click': () => { |
42 | state.next.isPublishing.set(true) |
43 | const choices = next.position.reduce((acc, el, i) => { |
44 | if (el) acc.push(i) |
45 | return acc |
46 | }, []) |
47 | |
48 | scuttle.position.async.publishMeetingTime({ poll, choices }, (err, data) => { |
49 | if (err) throw err |
50 | console.log(data) |
51 | }) |
52 | } |
53 | }, 'Publish' |
54 | ) |
55 | }) |
56 | } |
57 | |
58 | function ScryShowResults () { |
59 | return computed(state.now, ({ title, closesAt, times, rows }) => { |
60 | const style = { |
61 | display: 'grid', |
62 | 'grid-template-columns': `minmax(10rem, auto) repeat(${times.length}, 4rem)` |
63 | } |
64 | |
65 | return [ |
66 | h('ScryShowResults', { style }, [ |
67 | times.map(ScryShowTime), |
68 | rows.map(ScryShowRow) |
69 | ]) |
70 | ] |
71 | }) |
72 | } |
73 | |
74 | function ScryShowRow ({ author, position }) { |
75 | if (author !== myFeedId) { |
76 | return [ |
77 | h('div.about', [ |
78 | avatar(author), |
79 | name(author) |
80 | ]), |
81 | position.map(pos => pos |
82 | ? h('div.position.-yes', tick()) |
83 | : h('div.position.-no') |
84 | ) |
85 | ] |
86 | } |
87 | |
88 | const toggleEditing = () => { |
89 | const isEditing = !resolve(state.next.isEditing) |
90 | state.next.isEditing.set(isEditing) |
91 | } |
92 | |
93 | return [ |
94 | h('div.about', [ |
95 | avatar(author), |
96 | name(author), |
97 | h('i.fa.fa-pencil', { 'ev-click': toggleEditing }) |
98 | ]), |
99 | computed([state.next, state.now.position], ({ isEditing, position }, currentPosition) => { |
100 | if (isEditing) { |
101 | return position.map((pos, i) => { |
102 | return h('div.position.-edit', |
103 | { |
104 | 'ev-click': () => { |
105 | const nextPosition = resolve(state.next.position) |
106 | nextPosition[i] = !pos |
107 | state.next.position.set(nextPosition) |
108 | } |
109 | }, |
110 | pos ? checkedBox() : uncheckedBox() |
111 | ) |
112 | }) |
113 | } |
114 | |
115 | return currentPosition.map(pos => pos |
116 | ? h('div.position.-yes', tick()) |
117 | : h('div.position.-no') |
118 | ) |
119 | }) |
120 | ] |
121 | } |
122 | |
123 | function fetchState () { |
124 | scuttle.poll.async.get(poll.key, (err, doc) => { |
125 | if (err) return console.error(err) |
126 | |
127 | const { title, closesAt, positions } = doc |
128 | const times = doc.results.map(result => result.choice) |
129 | const results = times.map(t => doc.results.find(result => result.choice === t)) |
130 | // this ensures results Array is in same order as a times Array |
131 | |
132 | const rows = positions |
133 | .reduce((acc, pos) => { |
134 | if (!acc.includes(pos.value.author)) acc.push(pos.value.author) |
135 | return acc |
136 | }, []) |
137 | .map(author => { |
138 | const position = times.map((time, i) => { |
139 | return results[i].voters.hasOwnProperty(author) |
140 | }) |
141 | return { author, position } |
142 | }) |
143 | |
144 | const myRow = rows.find(r => r.author === myFeedId) |
145 | const myPosition = myRow ? myRow.position : Array(times.length).fill(null) |
146 | |
147 | var isEditing = false |
148 | if (!myRow) { |
149 | rows.push({ author: myFeedId, position: myPosition }) |
150 | isEditing = true |
151 | } |
152 | |
153 | state.now.set({ |
154 | title, |
155 | closesAt, |
156 | times, |
157 | rows, |
158 | position: myPosition |
159 | }) |
160 | state.next.set({ |
161 | position: Array.from(myPosition), |
162 | isEditing, |
163 | isPublishing: false |
164 | }) |
165 | }) |
166 | } |
167 | |
168 | function watchForUpdates (cb) { |
169 | // TODO check if isEditing before calling cb |
170 | // start a loop to trigger cb after finished editing |
171 | pull( |
172 | scuttle.poll.pull.updates(poll.key), |
173 | pull.filter(m => !m.sync), |
174 | pull.drain(m => { |
175 | cb() |
176 | }) |
177 | ) |
178 | } |
179 | |
180 | function tick () { return '✔' } |
181 | function checkedBox () { return testing ? '☑' : h('i.fa.fa-check-square-o') } |
182 | function uncheckedBox () { return testing ? '☐' : h('i.fa.fa-square-o') } |
183 | } |
184 | |
185 | function initialState () { |
186 | return { |
187 | now: Struct({ |
188 | title: '', |
189 | times: [], |
190 | closesAt: undefined, |
191 | rows: [], |
192 | position: [] |
193 | }), |
194 | next: Struct({ |
195 | position: [], |
196 | isEditing: false, |
197 | isPublishing: false |
198 | }) |
199 | } |
200 | } |
201 | |
202 | // component: show-time |
203 | |
204 | function ScryShowTime (time, i) { |
205 | const style = { 'grid-column': i + 2 } |
206 | |
207 | return h('ScryShowTime', { style }, [ |
208 | h('div.month', month(time)), |
209 | h('div.date', time.getDate()), |
210 | h('div.day', day(time)), |
211 | h('div.time', printTime(time)) |
212 | ]) |
213 | } |
214 | |
215 | function month (date) { |
216 | const months = ['Jan', 'Feb', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] |
217 | |
218 | return months[date.getMonth()] |
219 | } |
220 | |
221 | function day (date) { |
222 | const days = ['Sun', 'Mon', 'Tues', 'Wed', 'Thu', 'Fri', 'Sat'] |
223 | |
224 | return days[date.getDay()] |
225 | } |
226 |
Built with git-ssb-web