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