git ssb

2+

mixmix / ticktack



Tree: e39763f409d19a3fb6f6c9052dccd36946f8be25

Files: e39763f409d19a3fb6f6c9052dccd36946f8be25 / app / html / context.js

5722 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 'about.html.avatar': 'first',
12 'about.obs.name': 'first',
13 'feed.pull.private': 'first',
14 'feed.pull.rollup': 'first',
15 'feed.pull.public': '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 'unread.sync.isUnread': 'first'
22})
23
24
25exports.create = (api) => {
26 return nest('app.html.context', (location) => {
27
28 const strings = api.translations.sync.strings()
29 const myKey = api.keys.sync.id()
30
31 var nearby = api.sbot.obs.localPeers()
32 var recentPeersContacted = Dict()
33 var recentThreads = Dict()
34 // TODO - extract as contact.obs.recentPrivate or something
35
36 var m = {}
37
38 pull(
39 next(api.feed.pull.private, {reverse: true, limit: 100, live: false}, ['value', 'timestamp']),
40 pull.filter(msg => msg.value.content.type === 'post'), // TODO is this the best way to protect against votes?
41 pull.filter(msg => msg.value.content.recps),
42 pull.drain(msg => {
43 var author = msg.value.author
44 if(api.unread.sync.isUnread(msg)) {
45 var seen = author === myKey ? 0 : 1
46 recentPeersContacted
47 .put(author, (recentPeersContacted.get(author)||0)+seen)
48 }
49 else
50 recentPeersContacted.put(author, 0)
51 })
52 )
53
54//TODO: calculate unread state for public threads/blogs
55// pull(
56// next(api.feed.pull.public, {reverse: true, limit: 100, live: false}, ['value', 'timestamp']),
57// pull.drain(msg => {
58//
59// })
60// )
61
62 return h('Context -feed', [
63 LevelOneContext(),
64 LevelTwoContext()
65 ])
66
67 function LevelOneContext () {
68 //the "discovery" button
69 const PAGES_UNDER_DISCOVER = ['blogIndex', 'blogShow', 'home']
70
71 return h('div.level.-one', [
72 // Nearby
73 computed(nearby, n => !isEmpty(n) ? h('header', strings.peopleNearby) : null),
74 map(nearby, feedId => Option({
75 notifications: Math.random() > 0.7 ? Math.floor(Math.random()*9+1) : 0, // TODO
76 imageEl: api.about.html.avatar(feedId),
77 label: api.about.obs.name(feedId),
78 selected: location.feed === feedId,
79 location: computed(recentPeersContacted, recent => {
80 const lastMsg = recent[feedId]
81 return lastMsg
82 ? Object.assign(lastMsg, { feed: feedId })
83 : { page: 'threadNew', feed: feedId }
84 }),
85 })),
86 computed(nearby, n => !isEmpty(n) ? h('hr') : null),
87
88 // Discover
89 Option({
90 //XXX not a random number of notifications!
91 notifications: Math.floor(Math.random()*5+1),
92 imageEl: h('i.fa.fa-binoculars'),
93 label: strings.blogIndex.title,
94 selected: PAGES_UNDER_DISCOVER.includes(location.page),
95 location: { page: 'blogIndex' },
96 }),
97
98 // Recent Messages
99 map(dictToCollection(recentPeersContacted), ({ key, value }) => {
100 const feedId = key()
101 const lastMsg = value()
102 if (nearby.has(feedId)) return
103
104 return Option({
105 //the number of threads with each peer
106 notifications: value,
107 //Math.random() > 0.7 ? Math.floor(Math.random()*9+1) : 0, // TODO
108 imageEl: api.about.html.avatar(feedId),
109 label: api.about.obs.name(feedId),
110 selected: location.feed === feedId,
111 location: Object.assign({}, lastMsg, { feed: feedId }) // TODO make obs?
112 })
113 })
114 ])
115 }
116
117 function LevelTwoContext () {
118 const { key, value, feed: targetUser, page } = location
119 const root = get(value, 'content.root', key)
120 if (!targetUser) return
121
122 var threads = MutantArray()
123
124 pull(
125 next(api.feed.pull.private, {reverse: true, limit: 100, live: false}, ['value', 'timestamp']),
126 pull.filter(msg => msg.value.content.recps),
127 pull.filter(msg => msg.value.content.recps
128 .map(recp => typeof recp === 'object' ? recp.link : recp)
129 .some(recp => recp === targetUser)
130 ),
131 api.feed.pull.rollup(),
132 pull.drain(thread => threads.push(thread))
133 )
134
135 return h('div.level.-two', [
136 Option({
137 selected: page === 'threadNew',
138 location: {page: 'threadNew', feed: targetUser},
139 label: h('Button', strings.threadNew.action.new),
140 }),
141 map(threads, thread => {
142 return Option({
143 label: api.message.html.subject(thread),
144 selected: thread.key === root,
145 location: Object.assign(thread, { feed: targetUser }),
146 })
147 })
148 ])
149 }
150
151 function Option ({ notifications = 0, imageEl, label, location, selected }) {
152 const className = selected ? '-selected' : ''
153 const goToLocation = (e) => {
154 e.preventDefault()
155 e.stopPropagation()
156 api.history.sync.push(resolve(location))
157 }
158
159 if (!imageEl) {
160 return h('Option', { className, 'ev-click': goToLocation }, [
161 h('div.label', label)
162 ])
163 }
164
165 return h('Option', { className }, [
166 h('div.circle', [
167 when(notifications, h('div.alert', notifications)),
168 imageEl
169 ]),
170 h('div.label', { 'ev-click': goToLocation }, label)
171 ])
172 }
173 })
174}
175
176
177
178
179
180

Built with git-ssb-web