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