git ssb

2+

mixmix / ticktack



Tree: ab05441c8dcd6cad002d31650852562da523e0c2

Files: ab05441c8dcd6cad002d31650852562da523e0c2 / app / html / thread-card.js

3210 bytesRaw
1var nest = require('depnest')
2var h = require('mutant/h')
3var isString= require('lodash/isString')
4var maxBy= require('lodash/maxBy')
5
6exports.gives = nest('app.html.threadCard', true)
7
8exports.needs = nest({
9 'keys.sync.id': 'first',
10 'history.sync.push': 'first',
11 'about.obs.name': 'first',
12 'about.html.image': 'first',
13 'message.html.markdown': 'first',
14 'translations.sync.strings': 'first',
15 'unread.sync.isUnread': 'first'
16})
17
18function firstLine (text) {
19 if(text.length < 80 && !~text.indexOf('\n')) return text
20
21 //get the first non-empty line
22 var line = text.trim().split('\n').shift().trim()
23
24 //always break on a space, so that links are preserved.
25 const leadingMentionsLength = countLeadingMentions(line)
26 const i = line.indexOf(' ', leadingMentionsLength + 80)
27 var sample = line.substring(0, ~i ? i : line.length)
28
29 const ellipsis = (sample.length < line.length) ? '...' : ''
30 return sample + ellipsis
31}
32
33function countLeadingMentions (str) {
34 return str.match(/^(\s*\[@[^\)]+\)\s*)*/)[0].length
35 // matches any number of pattern " [@...) " from start of line
36}
37
38exports.create = function (api) {
39
40 //render the icon for a thread.
41 //it would be more depjecty to split this
42 //into two methods, one in a private plugin
43 //one in a channel plugin
44 function threadIcon (msg) {
45 if(msg.value.private) {
46 const myId = api.keys.sync.id()
47
48 return msg.value.content.recps
49 .map(link => isString(link) ? link : link.link)
50 .filter(link => link !== myId)
51 .map(api.about.html.image)
52 }
53 else if(msg.value.content.channel)
54 return '#'+msg.value.content.channel
55 }
56
57
58 // REFACTOR: move this to a template?
59 function buildRecipientNames (thread) {
60 const myId = api.keys.sync.id()
61
62 return thread.value.content.recps
63 .map(link => isString(link) ? link : link.link)
64 .filter(link => link !== myId)
65 .map(api.about.obs.name)
66 }
67
68 function subject (msg) {
69 const { subject, text } = msg.value.content
70 if(!(subject || text)) return
71 return api.message.html.markdown(firstLine(subject|| text))
72 }
73
74 return nest('app.html.threadCard', (thread, opts = {}) => {
75 var strings = api.translations.sync.strings()
76
77 if(!thread.value) return
78 if(!thread.value.content.text) return
79
80 const subjectEl = h('div.subject', [
81 opts.nameRecipients
82 ? h('div.recps', buildRecipientNames(thread).map(recp => h('div.recp', recp)))
83 : null,
84 subject(thread)
85 ])
86
87 const lastReply = thread.replies && maxBy(thread.replies, r => r.timestamp)
88 const replySample = lastReply ? subject(lastReply) : null
89
90 const onClick = opts.onClick || function () { api.history.sync.push(thread) }
91 const id = `${thread.key.replace(/[^a-z0-9]/gi, '')}` //-${JSON.stringify(opts)}`
92 // id is only here to help morphdom morph accurately
93
94 var className = thread.unread ? '-unread': ''
95
96 return h('ThreadCard', { id, className, 'ev-click': onClick, }, [
97 h('div.context', threadIcon(thread)),
98 h('div.content', [
99 subjectEl,
100 replySample ? h('div.reply', [
101 h('i.fa.fa-caret-left'),
102 replySample
103 ]) : null
104 ])
105 ])
106 })
107}
108
109
110

Built with git-ssb-web