Files: 162122b46e57bbd1cb6ff6774e6b40502586e89b / app / html / blogCard.js
3967 bytesRaw
1 | var nest = require('depnest') |
2 | var h = require('mutant/h') |
3 | var isString= require('lodash/isString') |
4 | var maxBy= require('lodash/maxBy') |
5 | var markdown = require('ssb-markdown') |
6 | var ref = require('ssb-ref') |
7 | var htmlEscape = require('html-escape') |
8 | |
9 | function renderEmoji (emoji, url) { |
10 | if (!url) return ':' + emoji + ':' |
11 | return ` |
12 | <img |
13 | src="${htmlEscape(url)}" |
14 | alt=":${htmlEscape(emoji)}:" |
15 | title=":${htmlEscape(emoji)}:" |
16 | class="emoji" |
17 | > |
18 | ` |
19 | } |
20 | |
21 | exports.gives = nest('app.html.blogCard', true) |
22 | |
23 | exports.needs = nest({ |
24 | 'keys.sync.id': 'first', |
25 | 'history.sync.push': 'first', |
26 | 'about.obs.color': 'first', |
27 | 'about.obs.name': 'first', |
28 | 'about.html.avatar': 'first', |
29 | 'translations.sync.strings': 'first', |
30 | 'unread.sync.isUnread': 'first', |
31 | 'message.html.channel': 'first', |
32 | 'message.html.timeago': 'first', |
33 | 'blob.sync.url': 'first', |
34 | 'emoji.sync.url': 'first', |
35 | |
36 | 'blog.html.title': 'first', |
37 | 'blog.html.summary': 'first', |
38 | 'blog.html.thumbnail': 'first', |
39 | }) |
40 | |
41 | exports.create = function (api) { |
42 | |
43 | //render markdown, but don't support patchwork@2 style mentions or custom emoji right now. |
44 | function render (source) { |
45 | return markdown.block(source, { |
46 | emoji: (emoji) => { |
47 | return renderEmoji(emoji, api.emoji.sync.url(emoji)) |
48 | }, |
49 | toUrl: (id) => { |
50 | if (ref.isBlob(id)) return api.blob.sync.url(id) |
51 | return id |
52 | }, |
53 | imageLink: (id) => id |
54 | }) |
55 | } |
56 | |
57 | |
58 | //render the icon for a blog. |
59 | //it would be more depjecty to split this |
60 | //into two methods, one in a private plugin |
61 | //one in a channel plugin |
62 | function blogIcon (msg) { |
63 | if(msg.value.private) { |
64 | const myId = api.keys.sync.id() |
65 | |
66 | return msg.value.content.recps |
67 | .map(link => isString(link) ? link : link.link) |
68 | .filter(link => link !== myId) |
69 | .map(link => api.about.html.avatar) |
70 | } |
71 | else if(msg.value.content.channel) |
72 | return '#'+msg.value.content.channel |
73 | } |
74 | |
75 | |
76 | // REFACTOR: move this to a template? |
77 | function buildRecipientNames (blog) { |
78 | const myId = api.keys.sync.id() |
79 | |
80 | return blog.value.content.recps |
81 | .map(link => isString(link) ? link : link.link) |
82 | .filter(link => link !== myId) |
83 | .map(api.about.obs.name) |
84 | } |
85 | |
86 | return nest('app.html.blogCard', (blog, opts = {}) => { |
87 | var strings = api.translations.sync.strings() |
88 | |
89 | if(!blog.value) return |
90 | |
91 | const lastReply = blog.replies && maxBy(blog.replies, r => r.timestamp) |
92 | |
93 | const goToBlog = () => api.history.sync.push(blog) |
94 | const onClick = opts.onClick || goToBlog |
95 | const id = `${blog.key.replace(/[^a-z0-9]/gi, '')}` //-${JSON.stringify(opts)}` |
96 | // id is only here to help morphdom morph accurately |
97 | |
98 | const { content, author } = blog.value |
99 | |
100 | var img = h('Thumbnail') |
101 | |
102 | var image = api.blog.html.thumbnail(blog) |
103 | |
104 | if(image) { |
105 | //Hey this works! fit an image into a specific size (see blog-card.mcss) |
106 | //centered, and scaled to fit the square (works with both landscape and portrait!) |
107 | //This is functional css not opinionated css, so all embedded. |
108 | img.style = 'background-image: url("'+api.blob.sync.url(image)+'"); background-position:center; background-size: cover;' |
109 | |
110 | } |
111 | else { |
112 | var style = { 'background-color': api.about.obs.color(blog.key) } |
113 | img = h('Thumbnail -empty', { style }, [ |
114 | h('i.fa.fa-file-text-o') |
115 | ]) |
116 | |
117 | } |
118 | |
119 | const className = blog.unread ? '-unread': '' |
120 | |
121 | var b = h('BlogCard', { id, className, 'ev-click': onClick }, [ |
122 | h('div.context', [ |
123 | api.about.html.avatar(author, 'tiny'), |
124 | h('div.name', api.about.obs.name(author)), |
125 | api.message.html.timeago(blog) |
126 | ]), |
127 | h('div.content', [ |
128 | img, |
129 | h('div.text', [ |
130 | h('h2', api.blog.html.title(blog)), |
131 | content.channel |
132 | ? api.message.html.channel(blog) |
133 | : '', |
134 | h('div.summary', api.blog.html.summary(blog)) |
135 | ]) |
136 | ]) |
137 | ]) |
138 | |
139 | return b |
140 | }) |
141 | } |
142 | |
143 |
Built with git-ssb-web