Files: 4d9f238ff73f7136cd292da88f06c17fe1a8c445 / lib / depject / message / html / layout / default.js
4925 bytesRaw
1 | const { h, computed, Value, when } = require('mutant') |
2 | const nest = require('depnest') |
3 | const ref = require('ssb-ref') |
4 | const ExpanderHook = require('../../../../expander-hook') |
5 | const timestamp = require('../../../../message/html/timestamp') |
6 | |
7 | exports.needs = nest({ |
8 | 'profile.html.person': 'first', |
9 | 'contact.obs.following': 'first', |
10 | 'keys.sync.id': 'first', |
11 | 'message.html': { |
12 | link: 'first', |
13 | metas: 'first', |
14 | actions: 'first', |
15 | references: 'first', |
16 | forks: 'first' |
17 | }, |
18 | 'about.html.image': 'first', |
19 | 'intl.sync.i18n': 'first' |
20 | }) |
21 | |
22 | exports.gives = nest('message.html.layout') |
23 | |
24 | exports.create = function (api) { |
25 | const i18n = api.intl.sync.i18n |
26 | let yourFollows = null |
27 | |
28 | // to get sync follows |
29 | setImmediate(() => { |
30 | const yourId = api.keys.sync.id() |
31 | yourFollows = api.contact.obs.following(yourId) |
32 | }) |
33 | |
34 | return nest('message.html.layout', layout) |
35 | |
36 | function layout (msg, { layout, priority, content, includeReferences = false, includeForks = true, compact = false, hooks, forkedFrom, outOfContext }) { |
37 | if (!(layout === undefined || layout === 'default')) return |
38 | |
39 | const classList = ['Message'] |
40 | let replyInfo = null |
41 | |
42 | const needsExpand = Value(false) |
43 | const expanded = Value(false) |
44 | |
45 | // new message previews shouldn't contract |
46 | if (!msg.key) expanded.set(true) |
47 | |
48 | if (msg.value.content.root) { |
49 | classList.push('-reply') |
50 | if (forkedFrom) { |
51 | replyInfo = h('span', [i18n('forked from parent thread '), api.message.html.link(forkedFrom)]) |
52 | } else if (outOfContext) { |
53 | replyInfo = h('span', [i18n('in reply to '), api.message.html.link(msg.value.content.root)]) |
54 | } |
55 | } else if (msg.value.content.project) { |
56 | replyInfo = h('span', [i18n('on '), api.message.html.link(msg.value.content.project)]) |
57 | } |
58 | |
59 | if (yourFollows && yourFollows().includes(msg.value.author)) { |
60 | classList.push('-following') |
61 | } |
62 | |
63 | if (compact) { |
64 | classList.push('-compact') |
65 | } |
66 | |
67 | if (priority === 2) { |
68 | classList.push('-new') |
69 | } |
70 | |
71 | if (priority === 1) { |
72 | classList.push('-unread') |
73 | } |
74 | |
75 | let cw = msg.value.content.contentWarning |
76 | if (typeof cw !== 'string' || cw.length === 0) { |
77 | cw = undefined |
78 | } else { |
79 | // TODO: emoji shortcodes? |
80 | // TODO: truncate very long content warning, maybe use max-height? |
81 | classList.push('-hasContentWarning') |
82 | } |
83 | |
84 | return h('div', { |
85 | classList, |
86 | hooks |
87 | }, [ |
88 | messageHeader(msg, { replyInfo, priority }), |
89 | cw |
90 | ? h('a.contentWarning', |
91 | { |
92 | href: '#', |
93 | 'ev-click': toggleAndTrack(expanded) |
94 | }, |
95 | cw |
96 | ) |
97 | : undefined, |
98 | h('section.content', { |
99 | classList: [when(expanded, '-expanded')], |
100 | hooks: [ExpanderHook(needsExpand)] |
101 | }, content), |
102 | computed(msg.key, (key) => { |
103 | if (ref.isMsg(key)) { |
104 | return h('footer', [ |
105 | when(needsExpand, h('div.expander', { |
106 | classList: when(expanded, null, '-truncated') |
107 | }, [ |
108 | h('a', { |
109 | href: '#', |
110 | 'ev-click': toggleAndTrack(expanded) |
111 | }, when(expanded, i18n('See less'), i18n('See more'))) |
112 | ])), |
113 | h('div.actions', [ |
114 | api.message.html.actions(msg) |
115 | ]) |
116 | ]) |
117 | } |
118 | }), |
119 | includeReferences ? api.message.html.references(msg) : null, |
120 | includeForks ? api.message.html.forks(msg) : null |
121 | ]) |
122 | |
123 | // scoped |
124 | |
125 | function messageHeader (msg, { replyInfo, priority }) { |
126 | const yourId = api.keys.sync.id() |
127 | const additionalMeta = [] |
128 | if (priority === 2) { |
129 | additionalMeta.push(h('span.flag -new', { title: i18n('New Message') })) |
130 | } else if (priority === 1) { |
131 | additionalMeta.push(h('span.flag -unread', { title: i18n('Unread Message') })) |
132 | } |
133 | |
134 | return h('header', [ |
135 | h('div.main', [ |
136 | h('a.avatar', { href: `${msg.value.author}` }, [ |
137 | api.about.html.image(msg.value.author) |
138 | ]), |
139 | h('div.main', [ |
140 | h('div.name', [ |
141 | api.profile.html.person(msg.value.author), |
142 | msg.value.author === yourId ? [' ', h('span.you', {}, i18n('(you)'))] : null |
143 | ]), |
144 | h('div.meta', [ |
145 | timestamp(msg), ' ', |
146 | replyInfo |
147 | ]) |
148 | ]) |
149 | ]), |
150 | h('div.meta', [ |
151 | additionalMeta, |
152 | api.message.html.metas(msg) |
153 | ]) |
154 | ]) |
155 | } |
156 | } |
157 | } |
158 | |
159 | function toggleAndTrack (param) { |
160 | return { |
161 | handleEvent: handleToggle, |
162 | param |
163 | } |
164 | } |
165 | |
166 | function handleToggle (ev) { |
167 | this.param.set(!this.param()) |
168 | if (!this.param()) { |
169 | ev.target.scrollIntoViewIfNeeded() |
170 | |
171 | // HACK: due to a browser bug, sometimes the body gets affected!? |
172 | // Why not just hack it!!! |
173 | if (document.body.scrollTop > 0) { |
174 | document.body.scrollTop = 0 |
175 | } |
176 | } |
177 | } |
178 |
Built with git-ssb-web