git ssb

2+

mixmix / ticktack



Tree: f30be9436ba56489c01de7d4c5dfef6669137201

Files: f30be9436ba56489c01de7d4c5dfef6669137201 / app / html / context.js

5814 bytesRaw
1const nest = require('depnest')
2const { h, computed, map, when, Dict, dictToCollection, Array: MutantArray, resolve } = require('mutant')
3const pull = require('pull-stream')
4const next = require('pull-next-step')
5const get = require('lodash/get')
6const isEmpty = require('lodash/isEmpty')
7
8exports.gives = nest('app.html.context')
9
10exports.needs = nest({
11 'app.html.scroller': 'first',
12 'about.html.avatar': 'first',
13 'about.obs.name': 'first',
14 'feed.pull.private': 'first',
15 'feed.pull.rollup': 'first',
16 'keys.sync.id': 'first',
17 'history.sync.push': 'first',
18 'message.html.subject': 'first',
19 'sbot.obs.localPeers': 'first',
20 'translations.sync.strings': 'first',
21})
22
23
24exports.create = (api) => {
25 return nest('app.html.context', (location) => {
26
27 const strings = api.translations.sync.strings()
28 const myKey = api.keys.sync.id()
29
30 var nearby = api.sbot.obs.localPeers()
31 var recentMsgLog = Dict ()
32 function updateRecentMsgLog (msg) {
33 const { author, timestamp } = msg.value
34
35 if (!recentMsgLog.has(author)) {
36 recentMsgLog.put(author, msg)
37 return
38 }
39
40 const currentWinner = recentMsgLog.get(author)
41 if (timestamp > currentWinner.value.timestamp) {
42 recentMsgLog.put(author, msg)
43 }
44 }
45 function isLatestMsg (msg) {
46 const { author, timestamp } = msg.value
47 return recentMsgLog.get(author).value.timestamp === timestamp
48 }
49
50 return h('Context -feed', [
51 LevelOneContext(),
52 LevelTwoContext()
53 ])
54
55 function LevelOneContext () {
56 function isDiscoverContext (loc) {
57 const PAGES_UNDER_DISCOVER = ['blogIndex', 'blogShow', 'home']
58
59 return PAGES_UNDER_DISCOVER.includes(location.page)
60 || get(location, 'value.private') === undefined
61 }
62
63 const prepend = [
64 // Nearby
65 computed(nearby, n => !isEmpty(n) ? h('header', strings.peopleNearby) : null),
66 map(nearby, feedId => Option({
67 notifications: Math.random() > 0.7 ? Math.floor(Math.random()*9+1) : 0, // TODO
68 imageEl: api.about.html.avatar(feedId, 'small'),
69 label: api.about.obs.name(feedId),
70 selected: location.feed === feedId,
71 location: computed(recentMsgLog, recent => {
72 const lastMsg = recent[feedId]
73 return lastMsg
74 ? Object.assign(lastMsg, { feed: feedId })
75 : { page: 'threadNew', feed: feedId }
76 }),
77 }), { comparer: (a, b) => a === b }),
78
79 // ---------------------
80 computed(nearby, n => !isEmpty(n) ? h('hr') : null),
81
82 // Discover
83 Option({
84 notifications: Math.floor(Math.random()*5+1),
85 imageEl: h('i.fa.fa-binoculars'),
86 label: strings.blogIndex.title,
87 selected: isDiscoverContext(location),
88 location: { page: 'blogIndex' },
89 })
90 ]
91
92 const { scroller } = api.app.html.scroller({
93 classList: [ 'level', '-one' ],
94 prepend,
95 streamBottom: api.feed.pull.private,
96 filter: pull(
97 pull.filter(msg => msg.value.content.type === 'post'), // TODO is this the best way to protect against votes?
98 pull.filter(msg => msg.value.author != myKey),
99 pull.filter(msg => msg.value.content.recps),
100 pull.through(updateRecentMsgLog),
101 pull.filter(isLatestMsg)
102 //pull.through( // trim exisiting from content up Top case)
103 ),
104 renderer: (msg) => {
105 const { author } = msg.value
106 if (nearby.has(author)) return
107
108 return Option({
109 notifications: Math.random() > 0.7 ? Math.floor(Math.random()*9+1) : 0, // TODO
110 imageEl: api.about.html.avatar(author),
111 label: api.about.obs.name(author),
112 selected: location.feed === author,
113 location: Object.assign({}, msg, { feed: author }) // TODO make obs?
114 })
115 }
116 })
117
118 return scroller
119 }
120
121 function LevelTwoContext () {
122 const { key, value, feed: targetUser, page } = location
123 const root = get(value, 'content.root', key)
124 if (!targetUser) return
125
126 var threads = MutantArray()
127
128 pull(
129 next(api.feed.pull.private, {reverse: true, limit: 100, live: false}, ['value', 'timestamp']),
130 pull.filter(msg => msg.value.content.recps),
131 pull.filter(msg => msg.value.content.recps
132 .map(recp => typeof recp === 'object' ? recp.link : recp)
133 .some(recp => recp === targetUser)
134 ),
135 api.feed.pull.rollup(),
136 pull.drain(thread => threads.push(thread))
137 )
138
139 return h('div.level.-two', [
140 Option({
141 selected: page === 'threadNew',
142 location: {page: 'threadNew', feed: targetUser},
143 label: h('Button', strings.threadNew.action.new),
144 }),
145 map(threads, thread => {
146 return Option({
147 label: api.message.html.subject(thread),
148 selected: thread.key === root,
149 location: Object.assign(thread, { feed: targetUser }),
150 })
151 }, { comparer: (a, b) => a === b })
152 ])
153 }
154
155 function Option ({ notifications = 0, imageEl, label, location, selected }) {
156 const className = selected ? '-selected' : ''
157 const goToLocation = (e) => {
158 e.preventDefault()
159 e.stopPropagation()
160 api.history.sync.push(resolve(location))
161 }
162
163 if (!imageEl) {
164 return h('Option', { className, 'ev-click': goToLocation }, [
165 h('div.label', label)
166 ])
167 }
168
169 return h('Option', { className }, [
170 h('div.circle', [
171 when(notifications, h('div.alert', notifications)),
172 imageEl
173 ]),
174 h('div.label', { 'ev-click': goToLocation }, label)
175 ])
176 }
177 })
178}
179
180

Built with git-ssb-web