git ssb

2+

mixmix / ticktack



Tree: 7e24f761d4772ef1ac78ab33da6743bb7e1d821d

Files: 7e24f761d4772ef1ac78ab33da6743bb7e1d821d / app / html / context.js

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

Built with git-ssb-web