git ssb

2+

mixmix / ticktack



Tree: 0e154203de9454a9f8f6a7ecc08aa6f661819f6d

Files: 0e154203de9454a9f8f6a7ecc08aa6f661819f6d / app / html / context.js

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

Built with git-ssb-web