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