Files: 5a28f5df1543b0e5813e5fe7dbc74f2ec907ff5a / message / html / markdown.js
3065 bytesRaw
1 | const renderer = require('ssb-markdown') |
2 | const h = require('mutant/h') |
3 | const ref = require('ssb-ref') |
4 | const nest = require('depnest') |
5 | var htmlEscape = require('html-escape') |
6 | var watch = require('mutant/watch') |
7 | const querystring = require('querystring') |
8 | |
9 | exports.needs = nest({ |
10 | 'blob.sync.url': 'first', |
11 | 'blob.obs.has': 'first', |
12 | 'emoji.sync.url': 'first' |
13 | }) |
14 | |
15 | exports.gives = nest('message.html.markdown') |
16 | |
17 | exports.create = function (api) { |
18 | return nest('message.html.markdown', markdown) |
19 | |
20 | function markdown (content) { |
21 | if (typeof content === 'string') { content = {text: content} } |
22 | var mentions = {} |
23 | var typeLookup = {} |
24 | var emojiMentions = {} |
25 | if (Array.isArray(content.mentions)) { |
26 | content.mentions.forEach(function (link) { |
27 | if (link && link.link && link.type) { |
28 | typeLookup[link.link] = link.type |
29 | } |
30 | if (link && link.name && link.link) { |
31 | if (link.emoji) { |
32 | // handle custom emoji |
33 | emojiMentions[link.name] = link.link |
34 | } else { |
35 | // handle old-style patchwork v2 mentions (deprecated) |
36 | mentions['@' + link.name] = link.link |
37 | } |
38 | } |
39 | }) |
40 | } |
41 | |
42 | return h('Markdown', { |
43 | hooks: [ |
44 | LoadingBlobHook(api.blob.obs.has) |
45 | ], |
46 | innerHTML: renderer.block(content.text, { |
47 | emoji: (emoji) => { |
48 | var url = emojiMentions[emoji] |
49 | ? api.blob.sync.url(emojiMentions[emoji]) |
50 | : api.emoji.sync.url(emoji) |
51 | return renderEmoji(emoji, url) |
52 | }, |
53 | toUrl: (id) => { |
54 | var link = ref.parseLink(id) |
55 | if (link && ref.isBlob(link.link)) { |
56 | var url = api.blob.sync.url(link.link) |
57 | var query = {} |
58 | if (link.query && link.query.unbox) query['unbox'] = link.query.unbox |
59 | if (typeLookup[link.link]) query['contentType'] = typeLookup[link.link] |
60 | return url + '?' + querystring.stringify(query) |
61 | } else if (link || id.startsWith('#') || id.startsWith('?')) { |
62 | return id |
63 | } else if (mentions[id]) { |
64 | // handle old-style patchwork v2 mentions (deprecated) |
65 | return mentions[id] |
66 | } |
67 | return false |
68 | }, |
69 | imageLink: (id) => id |
70 | }) |
71 | }) |
72 | } |
73 | |
74 | function renderEmoji (emoji, url) { |
75 | if (!url) return ':' + emoji + ':' |
76 | return ` |
77 | <img |
78 | src="${htmlEscape(url)}" |
79 | alt=":${htmlEscape(emoji)}:" |
80 | title=":${htmlEscape(emoji)}:" |
81 | class="emoji" |
82 | > |
83 | ` |
84 | } |
85 | } |
86 | |
87 | function LoadingBlobHook (hasBlob) { |
88 | return function (element) { |
89 | var releases = [] |
90 | element.querySelectorAll('img').forEach(img => { |
91 | var id = ref.extract(img.src) |
92 | if (id) { |
93 | releases.push(watch(hasBlob(id), has => { |
94 | if (has === false) { |
95 | img.classList.add('-pending') |
96 | } else { |
97 | img.classList.remove('-pending') |
98 | } |
99 | })) |
100 | } |
101 | }) |
102 | return function () { |
103 | while (releases.length) { |
104 | releases.pop()() |
105 | } |
106 | } |
107 | } |
108 | } |
109 |
Built with git-ssb-web