git ssb

10+

Matt McKegg / patchwork



Tree: b6ad2e7692f72c980a5ef0564f60b049333a9e1e

Files: b6ad2e7692f72c980a5ef0564f60b049333a9e1e / modules / page / html / render / message.js

4665 bytesRaw
1var { h, when, map, Proxy, Struct, Value, computed } = require('mutant')
2var nest = require('depnest')
3var ref = require('ssb-ref')
4var AnchorHook = require('../../../../lib/anchor-hook')
5
6exports.needs = nest({
7 'keys.sync.id': 'first',
8 'feed.obs.thread': 'first',
9 'message.sync.unbox': 'first',
10 'message.sync.root': 'first',
11 'message.html': {
12 render: 'first',
13 compose: 'first'
14 },
15 'sbot.async.get': 'first',
16 'intl.sync.i18n': 'first',
17 'message.html.missing': 'first'
18})
19
20exports.gives = nest('page.html.render')
21
22exports.create = function (api) {
23 const i18n = api.intl.sync.i18n
24 return nest('page.html.render', function (id) {
25 if (!ref.isMsg(id)) return
26 var loader = h('div', {className: 'Loading -large'})
27
28 var result = Proxy(loader)
29 var anchor = Value()
30 var participants = Proxy([])
31
32 var meta = Struct({
33 type: 'post',
34 root: Proxy(id),
35 branch: Proxy(id),
36 reply: Proxy(undefined),
37 channel: Value(undefined),
38 recps: Value(undefined)
39 })
40
41 var compose = api.message.html.compose({
42 meta,
43 isPrivate: when(meta.recps, true),
44 shrink: false,
45 participants,
46 hooks: [
47 AnchorHook('reply', anchor, (el) => el.focus())
48 ],
49 placeholder: when(meta.recps, i18n('Write a private reply'), i18n('Write a public reply'))
50 })
51
52 api.sbot.async.get(id, (err, value) => {
53 if (err) {
54 return result.set(h('PageHeading', [
55 h('h1', i18n('Cannot load thread'))
56 ]))
57 }
58
59 if (typeof value.content === 'string') {
60 value = api.message.sync.unbox(value)
61 }
62
63 if (!value) {
64 return result.set(h('PageHeading', [
65 h('h1', i18n('Cannot display message.'))
66 ]))
67 }
68
69 // what happens in private stays in private!
70 meta.recps.set(value.content.recps)
71
72 var root = api.message.sync.root({key: id, value}) || id
73 var isReply = id !== root
74 var thread = api.feed.obs.thread(id, {branch: isReply})
75
76 meta.channel.set(value.content.channel)
77 meta.root.set(root || thread.rootId)
78
79 // track message author for resolving missing messages and reply mentions
80 meta.reply.set(computed(thread.messages, messages => {
81 var result = {}
82 var first = messages[0]
83 var last = messages[messages.length - 1]
84
85 if (first && first.value) {
86 result[messages[0].key] = messages[0].value.author
87 }
88
89 if (last && last !== first && last.value) {
90 result[last.key] = last.value.author
91 }
92
93 return result
94 }))
95
96 // if root thread, reply to last post
97 meta.branch.set(isReply ? thread.branchId : thread.lastId)
98
99 participants.set(computed(thread.messages, messages => {
100 return messages.map(msg => msg && msg.value && msg.value.author)
101 }))
102
103 var container = h('Thread', [
104 h('div.messages', [
105 when(thread.branchId, h('a.full', {href: thread.rootId, anchor: id}, [i18n('View full thread')])),
106 map(thread.messages, (msg) => {
107 return computed([msg, thread.previousKey(msg)], (msg, previousId) => {
108 return h('div', {
109 hooks: [AnchorHook(msg.key, anchor, showContext)]
110 }, [
111 msg.key !== id ? api.message.html.missing(last(msg.value.content.branch), msg) : null,
112 api.message.html.render(msg, {
113 pageId: id,
114 previousId,
115 includeForks: msg.key !== id,
116 includeReferences: true
117 })
118 ])
119 })
120 })
121 ]),
122 compose
123 ])
124 result.set(when(thread.sync, container, loader))
125 })
126
127 var view = h('div', {className: 'SplitView'}, [
128 h('div.main', [
129 result
130 ])
131 ])
132
133 view.setAnchor = function (value) {
134 anchor.set(value)
135 }
136
137 return view
138 })
139}
140
141function showContext (element) {
142 var scrollParent = getScrollParent(element)
143 if (scrollParent) {
144 // ensure context is visible
145 scrollParent.scrollTop = Math.max(0, scrollParent.scrollTop - 100)
146 }
147}
148
149function getScrollParent (element) {
150 while (element.parentNode) {
151 if (element.parentNode.scrollTop > 10 && isScroller(element.parentNode)) {
152 return element.parentNode
153 } else {
154 element = element.parentNode
155 }
156 }
157}
158
159function isScroller (element) {
160 var value = window.getComputedStyle(element)['overflow-y']
161 return (value === 'auto' || value === 'scroll')
162}
163
164function last (array) {
165 if (Array.isArray(array)) {
166 return array[array.length - 1]
167 } else {
168 return array
169 }
170}
171

Built with git-ssb-web