git ssb

2+

mixmix / ticktack



Tree: db1b91bf9f68e6db25b325fd506058608428302e

Files: db1b91bf9f68e6db25b325fd506058608428302e / app / page / home.js

5217 bytesRaw
1const nest = require('depnest')
2const { h, computed } = require('mutant')
3const {threadReduce} = require('ssb-reduce-stream')
4const pull = require('pull-stream')
5const isObject = require('lodash/isObject')
6const isString = require('lodash/isString')
7const last = require('lodash/last')
8const get = require('lodash/get')
9
10exports.gives = nest('app.page.home')
11
12exports.needs = nest({
13 'about.html.image': 'first',
14 'about.obs.name': 'first',
15 'app.html.nav': 'first',
16 'feed.pull.public': 'first',
17 'history.sync.push': 'first',
18 'keys.sync.id': 'first',
19 'message.sync.unbox': 'first',
20 'message.html.markdown': 'first'
21})
22
23function firstLine (text) {
24 if(text.length < 80 && !~text.indexOf('\n')) return text
25
26 var line = ''
27 var lineNumber = 0
28 while (line.length === 0) {
29 const rawLine = text.split('\n')[lineNumber]
30 line = trimLeadingMentions(rawLine)
31
32 lineNumber++
33 }
34
35 var sample = line.substring(0, 80)
36 if (hasBrokenLink(sample))
37 sample = sample + line.substring(81).match(/[^\)]*\)/)[0]
38
39 return sample
40
41 function trimLeadingMentions (str) {
42 return str.replace(/^(\s*\[@[^\)]+\)\s*)*/, '')
43 // deletes any number of pattern " [@...) " from start of line
44 }
45
46 function hasBrokenLink (str) {
47 return /\[[^\]]*\]\([^\)]*$/.test(str)
48 // matches "[name](start_of_link"
49 }
50}
51
52exports.create = (api) => {
53 return nest('app.page.home', home)
54
55 function home (location) {
56 // location here can expected to be: { page: 'home' }
57
58 var container = h('div.container', [])
59
60 function subject (msg) {
61 const { subject, text } = msg.value.content
62 return api.message.html.markdown(firstLine(subject|| text))
63 }
64
65 function link(location) {
66 return {'ev-click': () => api.history.sync.push(location)}
67 }
68
69 function item (context, thread, opts = {}) {
70 if(!thread.value) return
71
72 const subjectEl = h('div.subject', [
73 opts.nameRecipients
74 ? h('div.recps', buildRecipientNames(thread).map(recp => h('div.recp', recp)))
75 : null,
76 subject(thread)
77 ])
78
79 const lastReply = thread.replies && last(thread.replies)
80 const replyEl = lastReply
81 ? h('div.reply', [
82 h('div.replySymbol', '► '),
83 subject(lastReply)
84 ])
85 : null
86
87
88 // REFACTOR: move this to a template?
89 function buildRecipientNames (thread) {
90 const myId = api.keys.sync.id()
91
92 return thread.value.content.recps
93 .map(link => isString(link) ? link : link.link)
94 .filter(link => link !== myId)
95 .map(api.about.obs.name)
96 }
97
98 return h('div.thread', link(thread), [
99 h('div.context', context),
100 h('div.content', [
101 subjectEl,
102 replyEl
103 ])
104 ])
105 }
106
107 function threadGroup (threads, obj, toContext, opts) {
108 // threads = a state object for all the types of threads
109 // obj = a map of keys to root ids, where key ∈ (channel | group | concatenated list of pubkeys)
110 // toContext = fn that derives the context of the group
111 // opts = { nameRecipients }
112
113 var groupEl = h('div.threads')
114 for(var k in obj) {
115 var id = obj[k]
116 var thread = get(threads, ['roots', id])
117 if(thread && thread.value) {
118 var el = item(toContext(k, thread), thread, opts)
119 if(el) groupEl.appendChild(el)
120 }
121 }
122 return groupEl
123 }
124
125 pull(
126 api.feed.pull.public({reverse: true, limit: 1000}),
127 pull.collect(function (err, messages) {
128
129 var threads = messages
130 .map(function (data) {
131 if(isObject(data.value.content)) return data
132 return api.message.sync.unbox(data)
133 })
134 .filter(Boolean)
135 .reduce(threadReduce, null)
136
137 const privateUpdatesSection = h('section.updates -directMessage', [
138 h('h2', 'Direct Messages'),
139 threadGroup(
140 threads,
141 threads.private,
142 function (_, msg)
143 // NB: msg passed in is actually a 'thread', but only care about root msg
144 const myId = api.keys.sync.id()
145
146 return msg.value.content.recps
147 .map(link => isString(link) ? link : link.link)
148 .filter(link => link !== myId)
149 .map(api.about.html.image)
150 },
151 { nameRecipients: true }
152 )
153 ])
154
155 const channelUpdatesSection = h('section.updates -channel', [
156 h('h2', 'Channels'),
157 threadGroup(
158 threads,
159 threads.channels,
160 ch => '#'+ch
161 )
162 ])
163
164 const groupUpdatesSection = h('section.updates -group', [
165 h('h2', 'Groups'),
166 'TODO: complete + enable when groups are live'
167 // threadGroup(
168 // threads,
169 // threads.groups,
170 // toName ...
171 // )
172 ])
173
174 container.appendChild(privateUpdatesSection)
175 container.appendChild(channelUpdatesSection)
176 container.appendChild(groupUpdatesSection)
177 })
178 )
179
180 return h('Page -home', [
181 h('h1', 'Home'),
182 api.app.html.nav(),
183 container
184 ])
185 }
186}
187
188

Built with git-ssb-web