Files: 8bc84ea53b6b557f2173afd3670d0154c1ca27aa / app / page / blogNew.js
5631 bytesRaw
1 | const nest = require('depnest') |
2 | const { h, Struct, Value } = require('mutant') |
3 | const addSuggest = require('suggest-box') |
4 | const pull = require('pull-stream') |
5 | const marksum = require('markdown-summary') |
6 | const MediumEditor = require('medium-editor').MediumEditor |
7 | const MediumToMD = require('medium-editor-markdown') |
8 | // const CustomHtml = require('medium-editor-custom-async') |
9 | |
10 | exports.gives = nest('app.page.blogNew') |
11 | |
12 | exports.needs = nest({ |
13 | 'app.html.sideNav': 'first', |
14 | 'channel.async.suggest': 'first', |
15 | 'history.sync.push': 'first', |
16 | 'message.html.compose': 'first', |
17 | 'translations.sync.strings': 'first', |
18 | 'sbot.async.addBlob': 'first' |
19 | }) |
20 | |
21 | exports.create = (api) => { |
22 | var contentHtmlObs |
23 | |
24 | return nest('app.page.blogNew', blogNew) |
25 | |
26 | function blogNew (location) { |
27 | const strings = api.translations.sync.strings() |
28 | const getChannelSuggestions = api.channel.async.suggest() |
29 | |
30 | const meta = Struct({ |
31 | type: 'blog', |
32 | channel: Value(), |
33 | title: Value(), |
34 | summary: Value(), |
35 | text: Value('') |
36 | }) |
37 | |
38 | const mediumComposer = h('div.editor', { |
39 | 'ev-input': e => { |
40 | } |
41 | }) |
42 | const composer = api.message.html.compose( |
43 | { |
44 | meta, |
45 | placeholder: strings.blogNew.actions.writeBlog, |
46 | shrink: false, |
47 | prepublish: function (content, cb) { |
48 | var m = /\!\[[^]+\]\(([^\)]+)\)/.exec(marksum.image(content.text)) |
49 | content.thumbnail = m && m[1] |
50 | // content.summary = marksum.summary(content.text) // Need a summary whihch doesn't trim the start |
51 | |
52 | var stream = pull.values([content.text]) |
53 | delete content.text |
54 | api.sbot.async.addBlob(stream, function (err, hash) { |
55 | if (err) return cb(err) |
56 | if (!hash) throw new Error('missing hash') |
57 | content.blog = hash |
58 | cb(null, content) |
59 | }) |
60 | } |
61 | }, |
62 | (err, msg) => api.history.sync.push(err || { page: 'blogIndex' }) |
63 | ) |
64 | |
65 | const channelInput = h('input', { |
66 | 'ev-input': e => meta.channel.set(e.target.value), |
67 | value: meta.channel, |
68 | placeholder: strings.channel |
69 | }) |
70 | |
71 | var page = h('Page -blogNew', [ |
72 | api.app.html.sideNav(location), |
73 | h('div.content', [ |
74 | h('div.container', [ |
75 | h('div.field -channel', [ |
76 | h('div.label', strings.channel), |
77 | channelInput |
78 | ]), |
79 | h('div.field -title', [ |
80 | h('div.label', strings.blogNew.field.title), |
81 | h('input', { |
82 | 'ev-input': e => meta.title.set(e.target.value), |
83 | placeholder: strings.blogNew.field.title |
84 | }) |
85 | ]), |
86 | h('div.field -summary', [ |
87 | h('div.label', strings.blogNew.field.summary), |
88 | h('input', { |
89 | 'ev-input': e => meta.summary.set(e.target.value), |
90 | placeholder: strings.blogNew.field.summary |
91 | }) |
92 | ]), |
93 | mediumComposer, |
94 | h('Button', { |
95 | 'ev-click': () => { |
96 | var img = h('img', { src: 'http://localhost:8989/blobs/get/%264TKyoyZmjjtpPvwiSR%2BGQIgrJs8o6XmzfUDZ5p1PP30%3D.sha256' }) |
97 | // var e = MediumEditor.getEditorFromElement(mediumComposer) |
98 | // e.serialize()['element-0'].value |
99 | mediumComposer.appendChild(img) |
100 | } |
101 | }, 'Add Image'), |
102 | composer |
103 | ]) |
104 | ]) |
105 | ]) |
106 | |
107 | function initialiseMedium () { |
108 | new MediumEditor(mediumComposer, { |
109 | elementsContainer: page, |
110 | toolbar: { |
111 | allowMultiParagraphSelection: true, |
112 | buttons: ['bold', 'italic', 'underline', 'anchor', 'h2', 'h3', 'quote'], |
113 | diffLeft: 0, |
114 | diffTop: -10, |
115 | firstButtonClass: 'medium-editor-button-first', |
116 | lastButtonClass: 'medium-editor-button-last', |
117 | relativeContainer: null, |
118 | standardizeSelectionStart: false, |
119 | static: false, |
120 | /* options which only apply when static is true */ |
121 | align: 'center', |
122 | sticky: false, |
123 | updateOnEmptySelection: false |
124 | }, |
125 | extensions: { |
126 | markdown: new MediumToMD ( |
127 | { |
128 | toMarkdownOptions: { |
129 | converters: [{ |
130 | filter: 'img', |
131 | replacement: (content, node) => { |
132 | var blob = decodeURIComponent(node.src.replace('http://localhost:8989/blobs/get/', '')) |
133 | return `![](${blob})` |
134 | } |
135 | }] |
136 | }, |
137 | events: ['input', 'change', 'DOMNodeInserted'] |
138 | }, |
139 | md => meta.text.set(md) |
140 | ) |
141 | } |
142 | }) |
143 | } |
144 | initialiseMedium() |
145 | |
146 | addSuggest(channelInput, (inputText, cb) => { |
147 | inputText = inputText.replace(/^#/, '') |
148 | var suggestions = getChannelSuggestions(inputText) |
149 | .map(s => { |
150 | s.value = s.value.replace(/^#/, '') // strip the defualt # prefix here |
151 | return s |
152 | }) |
153 | .map(s => { |
154 | if (s.subtitle === 'subscribed') { s.subtitle = h('i.fa.fa-heart') } // TODO - translation-friendly subscribed |
155 | return s |
156 | }) |
157 | |
158 | // HACK add the input text if it's not an option already |
159 | if (!suggestions.some(s => s.title === inputText)) { |
160 | suggestions.push({ |
161 | title: inputText, |
162 | subtitle: h('i.fa.fa-plus-circle'), |
163 | value: inputText |
164 | }) |
165 | } |
166 | |
167 | cb(null, suggestions) |
168 | }, {cls: 'PatchSuggest.-channelHorizontal'}) // WARNING hacking suggest-box cls |
169 | |
170 | channelInput.addEventListener('suggestselect', (e) => { |
171 | meta.channel.set(e.detail.value) |
172 | }) |
173 | |
174 | return page |
175 | } |
176 | } |
177 |
Built with git-ssb-web