git ssb

16+

Dominic / patchbay



Tree: 368b6fabc6c25007c87d6e7a601888a0dd21c561

Files: 368b6fabc6c25007c87d6e7a601888a0dd21c561 / app / page / calendar.js

6502 bytesRaw
1const nest = require('depnest')
2const { h, Array: MutantArray, map, Struct, computed, watch, throttle, resolve } = require('mutant')
3const pull = require('pull-stream')
4const { isMsg } = require('ssb-ref')
5
6exports.gives = nest('app.page.calendar')
7
8exports.needs = nest({
9 'message.html.render': 'first',
10 'sbot.async.get': 'first',
11 'sbot.pull.stream': 'first'
12})
13
14exports.create = (api) => {
15 return nest('app.page.calendar', calendarPage)
16
17 function calendarPage (location) {
18 const d = new Date()
19 const state = Struct({
20 today: new Date(d.getFullYear(), d.getMonth(), d.getDate()),
21 year: d.getFullYear(),
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 })
27 })
28
29 watch(state.year, year => getEvents(year, state.events))
30
31 const page = h('CalendarPage', { title: '/calendar' }, [
32 Calendar(state),
33 Events(state)
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
44 }
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
70 function getEvents (year, events) {
71 const query = [{
72 $filter: {
73 value: {
74 timestamp: {$gt: Number(new Date(year, 0, 1))}, // ordered by published time
75 content: {
76 type: 'about',
77 startDateTime: {
78 epoch: {$gt: 0}
79 }
80 }
81 }
82 }
83 }, {
84 $map: {
85 key: ['value', 'content', 'about'], // gathering
86 date: ['value', 'content', 'startDateTime', 'epoch']
87 }
88 }]
89
90 const opts = {
91 reverse: false,
92 live: true,
93 query
94 }
95
96 pull(
97 api.sbot.pull.stream(server => server.query.read(opts)),
98 pull.filter(m => !m.sync),
99 pull.filter(r => isMsg(r.key) && Number.isInteger(r.date)),
100 pull.map(r => {
101 return { key: r.key, date: new Date(r.date) }
102 }),
103 pull.drain(({ key, date }) => {
104 var target = events.find(ev => ev.data.key === key)
105 if (target && target.date <= date) events.delete(target)
106
107 events.push({ date, data: { key } })
108 })
109 )
110 }
111}
112
113// ////////////////// extract below into a module ///////////////////////
114
115// Thanks to nomand for the inspiration and code (https://github.com/nomand/Letnice),
116// they formed the foundation of this work
117
118const MONTHS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]
119const DAYS = [ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' ]
120
121function Calendar (state) {
122 // TODO assert events is an Array of object
123 // of form { date, data }
124
125 const setRange = state.range.set
126
127 return h('Calendar', [
128 Header(state.year),
129 h('div.months', computed(throttle(state, 100), ({ today, year, events, range }) => {
130 return MONTHS.map((month, monthIndex) => {
131 return Month({ month, monthIndex, today, year, events, range, setRange })
132 })
133 }))
134 ])
135}
136
137function Header (year) {
138 return h('div.header', [
139 h('div.year', [
140 year,
141 h('a', { 'ev-click': () => year.set(year() - 1) }, '-'),
142 h('a', { 'ev-click': () => year.set(year() + 1) }, '+')
143 ])
144 ])
145}
146
147function Month ({ month, monthIndex, today, year, events, range, setRange }) {
148 const monthLength = new Date(year, monthIndex + 1, 0).getDate()
149 // NOTE Date takes month as a monthIndex i.e. april = 3
150 // and day = 0 goes back a day
151 const days = Array(monthLength).fill().map((_, i) => i + 1)
152
153 var weekday
154 var week
155 var offset = getDay(new Date(year, monthIndex, 1)) - 1
156
157 const setMonthRange = () => setRange({
158 gte: new Date(year, monthIndex, 1),
159 lt: new Date(year, monthIndex + 1, 1)
160 })
161
162 return h('CalendarMonth', [
163 h('div.month-name', { 'ev-click': setMonthRange }, month.substr(0, 2)),
164 h('div.days', { style: {display: 'grid'} }, [
165 DAYS.map((day, i) => DayName(day, i)),
166 days.map(Day)
167 ])
168 ])
169
170 function Day (day) {
171 const date = new Date(year, monthIndex, day)
172 const dateEnd = new Date(year, monthIndex, day + 1)
173 weekday = getDay(date)
174 week = Math.ceil((day + offset) / 7)
175
176 const eventsOnDay = events.filter(e => {
177 return e.date >= date && e.date < dateEnd
178 })
179
180 const opts = {
181 attributes: {
182 'title': `${year}-${monthIndex + 1}-${day}`,
183 'data-date': `${year}-${monthIndex + 1}-${day}`
184 },
185 style: {
186 'grid-row': `${weekday} / ${weekday + 1}`,
187 'grid-column': `${week + 1} / ${week + 2}`
188 // column moved by 1 to make space for labels
189 },
190 classList: [
191 date < today ? '-past' : '-future',
192 eventsOnDay.length ? '-events' : '',
193 date >= range.gte && date < range.lt ? '-selected' : ''
194 ]
195 }
196
197 if (!eventsOnDay.length) return h('CalendarDay', opts)
198
199 opts['ev-click'] = () => setRange({
200 gte: date,
201 lt: dateEnd
202 })
203 opts['ev-hover'] = () => console.log(date)
204
205 return h('CalendarDay', opts, [
206 // TODO add awareness of whether I'm going to events
207 // TODO try a FontAwesome circle
208 h('div.dot', [
209 // Math.random() > 0.3 ? h('div') : ''
210 ])
211 ])
212 }
213}
214
215function DayName (day, index) {
216 return h('CalendarDayName', {
217 style: {
218 'grid-row': `${index + 1} / ${index + 2}`,
219 'grid-column': '1 / 2'
220 }
221 }, day.substr(0, 1))
222}
223
224function getDay (date) {
225 const dayIndex = date.getDay()
226 return dayIndex === 0 ? 7 : dayIndex
227
228 // Weeks run 0...6 (Sun - Sat)
229 // this shifts those days around by 1
230}
231

Built with git-ssb-web