app/page/calendar.jsView |
---|
1 | 1 … | const nest = require('depnest') |
2 | | -const { h, Array: MutantArray, Struct, computed, watch, throttle } = require('mutant') |
| 2 … | +const { h, Array: MutantArray, map, Struct, computed, watch, throttle, resolve } = require('mutant') |
3 | 3 … | const pull = require('pull-stream') |
4 | 4 … | const { isMsg } = require('ssb-ref') |
5 | 5 … | |
6 | 6 … | exports.gives = nest('app.page.calendar') |
7 | 7 … | |
8 | 8 … | exports.needs = nest({ |
| 9 … | + 'message.html.render': 'first', |
| 10 … | + 'sbot.async.get': 'first', |
9 | 11 … | 'sbot.pull.stream': 'first' |
10 | 12 … | }) |
11 | 13 … | |
12 | 14 … | exports.create = (api) => { |
16 | 18 … | const d = new Date() |
17 | 19 … | const state = Struct({ |
18 | 20 … | today: new Date(d.getFullYear(), d.getMonth(), d.getDate()), |
19 | 21 … | year: d.getFullYear(), |
20 | | - events: MutantArray([]) |
| 22 … | + events: MutantArray([]), |
| 23 … | + range: Struct({ |
| 24 … | + gte: new Date(d.getFullYear(), d.getMonth(), d.getDate()), |
| 25 … | + lt: new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1) |
| 26 … | + }) |
21 | 27 … | }) |
22 | 28 … | |
23 | 29 … | watch(state.year, year => getEvents(year, state.events)) |
24 | 30 … | |
25 | | - return h('CalendarPage', { title: '/calendar' }, [ |
26 | | - Calendar(state) |
| 31 … | + const page = h('CalendarPage', { title: '/calendar' }, [ |
| 32 … | + Calendar(state), |
| 33 … | + Events(state) |
27 | 34 … | ]) |
| 35 … | + |
| 36 … | + page.scroll = i => { |
| 37 … | + const gte = resolve(state.range.gte) |
| 38 … | + state.range.gte.set(new Date(gte.getFullYear(), gte.getMonth(), gte.getDate() + i)) |
| 39 … | + const lt = resolve(state.range.lt) |
| 40 … | + state.range.lt.set(new Date(lt.getFullYear(), lt.getMonth(), lt.getDate() + i)) |
| 41 … | + } |
| 42 … | + |
| 43 … | + return page |
28 | 44 … | } |
29 | 45 … | |
| 46 … | + function Events (state) { |
| 47 … | + return h('CalendarEvents', computed([state.events, state.range], (events, range) => { |
| 48 … | + const keys = events |
| 49 … | + .filter(ev => ev.date >= range.gte && ev.date < range.lt) |
| 50 … | + .sort((a, b) => a.date - b.date) |
| 51 … | + .map(ev => ev.data.key) |
| 52 … | + |
| 53 … | + const gatherings = MutantArray([]) |
| 54 … | + |
| 55 … | + pull( |
| 56 … | + pull.values(keys), |
| 57 … | + pull.asyncMap((key, cb) => { |
| 58 … | + api.sbot.async.get(key, (err, value) => { |
| 59 … | + if (err) return cb(err) |
| 60 … | + cb(null, {key, value}) |
| 61 … | + }) |
| 62 … | + }), |
| 63 … | + pull.drain(msg => gatherings.push(msg)) |
| 64 … | + ) |
| 65 … | + |
| 66 … | + return map(gatherings, g => api.message.html.render(g)) |
| 67 … | + })) |
| 68 … | + } |
| 69 … | + |
30 | 70 … | function getEvents (year, events) { |
31 | 71 … | const query = [{ |
32 | 72 … | $filter: { |
33 | 73 … | value: { |
81 | 121 … | function Calendar (state) { |
82 | 122 … | |
83 | 123 … | |
84 | 124 … | |
| 125 … | + const setRange = state.range.set |
| 126 … | + |
85 | 127 … | return h('Calendar', [ |
86 | 128 … | Header(state.year), |
87 | | - h('div.months', computed(throttle(state, 100), ({ today, year, events }) => { |
| 129 … | + h('div.months', computed(throttle(state, 100), ({ today, year, events, range }) => { |
88 | 130 … | return MONTHS.map((month, monthIndex) => { |
89 | | - return Month({ month, monthIndex, today, year, events }) |
| 131 … | + return Month({ month, monthIndex, today, year, events, range, setRange }) |
90 | 132 … | }) |
91 | 133 … | })) |
92 | 134 … | ]) |
93 | 135 … | } |
101 | 143 … | ]) |
102 | 144 … | ]) |
103 | 145 … | } |
104 | 146 … | |
105 | | -function Month ({ month, monthIndex, today, year, events }) { |
| 147 … | +function Month ({ month, monthIndex, today, year, events, range, setRange }) { |
106 | 148 … | const monthLength = new Date(year, monthIndex + 1, 0).getDate() |
107 | 149 … | |
108 | 150 … | |
109 | 151 … | const days = Array(monthLength).fill().map((_, i) => i + 1) |
110 | 152 … | |
111 | | - var date |
112 | | - var dateEnd |
113 | 153 … | var weekday |
114 | 154 … | var week |
115 | 155 … | var offset = getDay(new Date(year, monthIndex, 1)) - 1 |
116 | 156 … | |
| 157 … | + const setMonthRange = () => setRange({ |
| 158 … | + gte: new Date(year, monthIndex, 1), |
| 159 … | + lt: new Date(year, monthIndex + 1, 1) |
| 160 … | + }) |
| 161 … | + |
117 | 162 … | return h('CalendarMonth', [ |
118 | | - h('div.month-name', month.substr(0, 2)), |
| 163 … | + h('div.month-name', { 'ev-click': setMonthRange }, month.substr(0, 2)), |
119 | 164 … | h('div.days', { style: {display: 'grid'} }, [ |
120 | 165 … | DAYS.map((day, i) => DayName(day, i)), |
121 | 166 … | days.map(Day) |
122 | 167 … | ]) |
123 | 168 … | ]) |
124 | 169 … | |
125 | 170 … | function Day (day) { |
126 | | - date = new Date(year, monthIndex, day) |
127 | | - dateEnd = new Date(year, monthIndex, day + 1) |
| 171 … | + const date = new Date(year, monthIndex, day) |
| 172 … | + const dateEnd = new Date(year, monthIndex, day + 1) |
128 | 173 … | weekday = getDay(date) |
129 | 174 … | week = Math.ceil((day + offset) / 7) |
130 | 175 … | |
131 | 176 … | const eventsOnDay = events.filter(e => { |
143 | 188 … | |
144 | 189 … | }, |
145 | 190 … | classList: [ |
146 | 191 … | date < today ? '-past' : '-future', |
147 | | - eventsOnDay.length ? '-events' : '' |
| 192 … | + eventsOnDay.length ? '-events' : '', |
| 193 … | + date >= range.gte && date < range.lt ? '-selected' : '' |
148 | 194 … | ] |
149 | 195 … | } |
150 | 196 … | |
151 | 197 … | if (!eventsOnDay.length) return h('CalendarDay', opts) |
152 | 198 … | |
| 199 … | + opts['ev-click'] = () => setRange({ |
| 200 … | + gte: date, |
| 201 … | + lt: dateEnd |
| 202 … | + }) |
| 203 … | + opts['ev-hover'] = () => console.log(date) |
| 204 … | + |
153 | 205 … | return h('CalendarDay', opts, [ |
| 206 … | + |
154 | 207 … | |
155 | | - h('div', [ |
| 208 … | + h('div.dot', [ |
156 | 209 … | |
157 | 210 … | ]) |
158 | 211 … | ]) |
159 | 212 … | } |
174 | 227 … | |
175 | 228 … | |
176 | 229 … | |
177 | 230 … | } |
178 | | - |