git ssb

2+

mixmix / ticktack



Tree: e3dfe163bff09f8ef2d3a5f6da4544ac33f96c9c

Files: e3dfe163bff09f8ef2d3a5f6da4544ac33f96c9c / app / page / home.js

5403 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')
9const More = require('hypermore')
10const morphdom = require('morphdom')
11
12exports.gives = nest('app.page.home')
13
14exports.needs = nest({
15 'about.html.image': 'first',
16 'about.obs.name': 'first',
17 'app.html.nav': 'first',
18 'sbot.pull.log': 'first',
19 'history.sync.push': 'first',
20 'keys.sync.id': 'first',
21 'message.sync.unbox': 'first',
22 'message.html.markdown': 'first',
23 'translations.sync.strings': 'first',
24 'state.obs.threads': 'first'
25})
26
27function firstLine (text) {
28 if(text.length < 80 && !~text.indexOf('\n')) return text
29
30 var line = ''
31 var lineNumber = 0
32 while (line.length === 0) {
33 const rawLine = text.split('\n')[lineNumber]
34 line = trimLeadingMentions(rawLine)
35
36 lineNumber++
37 }
38
39 var sample = line.substring(0, 80)
40 if (hasBrokenLink(sample))
41 sample = sample + line.substring(81).match(/[^\)]*\)/)[0]
42
43 return sample
44
45 function trimLeadingMentions (str) {
46 return str.replace(/^(\s*\[@[^\)]+\)\s*)*/, '')
47 // deletes any number of pattern " [@...) " from start of line
48 }
49
50 function hasBrokenLink (str) {
51 return /\[[^\]]*\]\([^\)]*$/.test(str)
52 // matches "[name](start_of_link"
53 }
54}
55
56exports.create = (api) => {
57 return nest('app.page.home', function (location) {
58 var strings = api.translations.sync.strings()
59 // location here can expected to be: { page: 'home' }
60
61 var container = h('div.container', [])
62
63 function subject (msg) {
64 const { subject, text } = msg.value.content
65 return api.message.html.markdown(firstLine(subject|| text))
66 }
67
68 function link(location) {
69 return {'ev-click': () => api.history.sync.push(location)}
70 }
71
72 function item (context, thread, opts = {}) {
73 if(!thread.value) return
74
75 const subjectEl = h('div.subject', [
76 opts.nameRecipients
77 ? h('div.recps', buildRecipientNames(thread).map(recp => h('div.recp', recp)))
78 : null,
79 subject(thread)
80 ])
81
82 const lastReply = thread.replies && last(thread.replies)
83 const replyEl = lastReply
84 ? h('div.reply', [
85 h('div.replySymbol', strings.replySymbol),
86 subject(lastReply)
87 ])
88 : null
89
90
91 // REFACTOR: move this to a template?
92 function buildRecipientNames (thread) {
93 const myId = api.keys.sync.id()
94
95 return thread.value.content.recps
96 .map(link => isString(link) ? link : link.link)
97 .filter(link => link !== myId)
98 .map(api.about.obs.name)
99 }
100
101 return h('div.thread', link(thread), [
102 h('div.context', context),
103 h('div.content', [
104 subjectEl,
105 replyEl
106 ])
107 ])
108 }
109
110 function threadGroup (threads, obj, toContext, opts) {
111 // threads = a state object for all the types of threads
112 // obj = a map of keys to root ids, where key (channel | group | concatenated list of pubkeys)
113 // toContext = fn that derives the context of the group
114 // opts = { nameRecipients }
115
116 var groupEl = h('div.threads')
117 for(var k in obj) {
118 var id = obj[k]
119 var thread = get(threads, ['roots', id])
120 if(thread && thread.value) {
121 var el = item(toContext(k, thread), thread, opts)
122 if(el) groupEl.appendChild(el)
123 }
124 }
125 return groupEl
126 }
127
128 var threadsObs = api.state.obs.threads()
129
130 var threadsHtmlObs = More(
131 threadsObs,
132 function render (threads) {
133 console.log('RENDER', JSON.stringify(threads).length)
134 morphdom(container,
135 h('div.container', [
136 //private section
137 h('section.updates -directMessage', [
138 h('h2', strings.directMessages),
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 //channels section
155 h('section.updates -channel', [
156 h('h2', strings.channels),
157 threadGroup(
158 threads,
159 threads.channels,
160 ch => '#'+ch
161 )
162 ]),
163 //group section
164 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 )
175 return container
176 }
177 )
178
179
180 return h('Page -home', [
181 h('h1', 'Home'),
182 api.app.html.nav(),
183 threadsHtmlObs,
184 h('button', {'ev-click': threadsHtmlObs.more}, [strings.showMore])
185 ])
186 })
187}
188
189
190

Built with git-ssb-web