git ssb

0+

mixmix / patchbay-scry



Tree: edcc95e36db325ce7977d1fe7522bf3633615898

Files: edcc95e36db325ce7977d1fe7522bf3633615898 / views / show.js

6391 bytesRaw
1const { h, Struct, computed, resolve } = require('mutant')
2const pull = require('pull-stream')
3const printTime = require('../lib/print-time')
4
5module.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 ScryShowSummary(rows),
69 rows.map(ScryShowRow)
70 ])
71 ]
72 })
73 }
74
75 function ScryShowRow ({ author, position }) {
76 if (author !== myFeedId) {
77 return [
78 h('div.about', [
79 avatar(author),
80 name(author)
81 ]),
82 position.map(pos => pos
83 ? h('div.position.-yes', tick())
84 : h('div.position.-no')
85 )
86 ]
87 }
88
89 const toggleEditing = () => {
90 const isEditing = !resolve(state.next.isEditing)
91 state.next.isEditing.set(isEditing)
92 }
93
94 return [
95 h('div.about', [
96 avatar(author),
97 name(author),
98 h('i.fa.fa-pencil', { 'ev-click': toggleEditing })
99 ]),
100 computed([state.next, state.now.position], ({ isEditing, position }, currentPosition) => {
101 if (isEditing) {
102 return position.map((pos, i) => {
103 return h('div.position.-edit',
104 {
105 'ev-click': () => {
106 const nextPosition = resolve(state.next.position)
107 nextPosition[i] = !pos
108 state.next.position.set(nextPosition)
109 }
110 },
111 pos ? checkedBox() : uncheckedBox()
112 )
113 })
114 }
115
116 return currentPosition.map(pos => pos
117 ? h('div.position.-yes', tick())
118 : h('div.position.-no')
119 )
120 })
121 ]
122 }
123
124 function ScryShowSummary (rows) {
125 if (!rows.length) return
126
127 const participants = rows.filter(r => r.position[0] !== null).length
128
129 const counts = rows[0].position.map((_, i) => {
130 return rows.reduce((acc, row) => {
131 if (row.position[i] === true) acc += 1
132 return acc
133 }, 0)
134 })
135 return [
136 h('div.participants', participants === 1
137 ? `${participants} participant`
138 : `${participants} participants`
139 ),
140 counts.map(n => h('div.count', `${n}${tick()}`))
141 ]
142 }
143
144 function fetchState () {
145 scuttle.poll.async.get(poll.key, (err, doc) => {
146 if (err) return console.error(err)
147
148 const { title, closesAt, positions } = doc
149 const times = doc.results.map(result => result.choice)
150 const results = times.map(t => doc.results.find(result => result.choice === t))
151 // this ensures results Array is in same order as a times Array
152
153 const rows = positions
154 .reduce((acc, pos) => {
155 if (!acc.includes(pos.value.author)) acc.push(pos.value.author)
156 return acc
157 }, [])
158 .map(author => {
159 const position = times.map((time, i) => {
160 return results[i].voters.hasOwnProperty(author)
161 })
162 return { author, position }
163 })
164
165 const myRow = rows.find(r => r.author === myFeedId)
166 const myPosition = myRow ? myRow.position : Array(times.length).fill(null)
167
168 var isEditing = false
169 if (!myRow) {
170 rows.push({ author: myFeedId, position: myPosition })
171 isEditing = true
172 }
173
174 state.now.set({
175 title,
176 closesAt,
177 times,
178 rows,
179 position: myPosition
180 })
181 state.next.set({
182 position: Array.from(myPosition),
183 isEditing,
184 isPublishing: false
185 })
186 })
187 }
188
189 function watchForUpdates (cb) {
190 // TODO check if isEditing before calling cb
191 // start a loop to trigger cb after finished editing
192 pull(
193 scuttle.poll.pull.updates(poll.key),
194 pull.filter(m => !m.sync),
195 pull.drain(m => {
196 cb()
197 })
198 )
199 }
200
201 function tick () { return '✔' }
202 function checkedBox () { return testing ? '☑' : h('i.fa.fa-check-square-o') }
203 function uncheckedBox () { return testing ? '☐' : h('i.fa.fa-square-o') }
204}
205
206function initialState () {
207 return {
208 now: Struct({
209 title: '',
210 times: [],
211 closesAt: undefined,
212 rows: [],
213 position: []
214 }),
215 next: Struct({
216 position: [],
217 isEditing: false,
218 isPublishing: false
219 })
220 }
221}
222
223// component: show-time
224
225function ScryShowTime (time, i) {
226 const style = { 'grid-column': i + 2 }
227
228 return h('ScryShowTime', { style }, [
229 h('div.month', month(time)),
230 h('div.date', time.getDate()),
231 h('div.day', day(time)),
232 h('div.time', printTime(time))
233 ])
234}
235
236function month (date) {
237 const months = ['Jan', 'Feb', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
238
239 return months[date.getMonth()]
240}
241
242function day (date) {
243 const days = ['Sun', 'Mon', 'Tues', 'Wed', 'Thu', 'Fri', 'Sat']
244
245 return days[date.getDay()]
246}
247

Built with git-ssb-web