git ssb

2+

mixmix / ticktack



Tree: 9ae1c46f226c40c608560b66d0b5ca42ecce151b

Files: 9ae1c46f226c40c608560b66d0b5ca42ecce151b / app / page / blogNew.js

6384 bytesRaw
1const nest = require('depnest')
2const { h, Struct, Value } = require('mutant')
3const addSuggest = require('suggest-box')
4const pull = require('pull-stream')
5const marksum = require('markdown-summary')
6const MediumEditor = require('medium-editor').MediumEditor
7const MediumToMD = require('medium-editor-markdown')
8// const CustomHtml = require('medium-editor-custom-async')
9
10exports.gives = nest('app.page.blogNew')
11
12exports.needs = nest({
13 'app.html.sideNav': 'first',
14 'blob.html.input': 'first',
15 'channel.async.suggest': 'first',
16 'history.sync.push': 'first',
17 'message.html.compose': 'first',
18 'translations.sync.strings': 'first',
19 'sbot.async.addBlob': 'first'
20})
21
22exports.create = (api) => {
23 return nest('app.page.blogNew', blogNew)
24
25 function blogNew (location) {
26 const strings = api.translations.sync.strings()
27 const getChannelSuggestions = api.channel.async.suggest()
28
29 const meta = Struct({
30 type: 'blog',
31 channel: Value(),
32 title: Value(),
33 summary: Value(),
34 text: Value('')
35 })
36
37 const mediumComposer = h('div.editor.Markdown', {
38 'ev-input': e => {
39 }
40 })
41 var filesById = {}
42 const composer = initialiseDummyComposer({ filesById, meta, api })
43 // NOTE we are bootstrapping off the message.html.compose logic
44 // - the mediumComposer feeds content into the composer, which the provides publishing
45 // - this works, but should be refactorer after more thought about generalised composer
46 // componentes have been done
47
48 const channelInput = h('input', {
49 'ev-input': e => meta.channel.set(e.target.value),
50 value: meta.channel,
51 placeholder: strings.channel
52 })
53
54 var page = h('Page -blogNew', [
55 api.app.html.sideNav(location),
56 h('div.content', [
57 h('div.container', [
58 h('div.field -channel', [
59 h('div.label', strings.channel),
60 channelInput
61 ]),
62 h('div.field -title', [
63 h('div.label', strings.blogNew.field.title),
64 h('input', {
65 'ev-input': e => meta.title.set(e.target.value),
66 placeholder: strings.blogNew.field.title
67 })
68 ]),
69 h('div.field -summary', [
70 h('div.label', strings.blogNew.field.summary),
71 h('input', {
72 'ev-input': e => meta.summary.set(e.target.value),
73 placeholder: strings.blogNew.field.summary
74 })
75 ]),
76 mediumComposer,
77 AddFileButton({ api, filesById, meta, textArea: mediumComposer }),
78 composer
79 ])
80 ])
81 ])
82
83 initialiseMedium({ page, el: mediumComposer, meta })
84 initialiseChannelSuggests({ input: channelInput, suggester: getChannelSuggestions, meta })
85
86 return page
87 }
88}
89
90function AddFileButton ({ api, filesById, meta, textArea }) {
91 // var textRaw = meta.text
92
93 const fileInput = api.blob.html.input(file => {
94 filesById[file.link] = file
95
96 const isImage = file.type.match(/^image/)
97
98 var content
99
100 if (isImage) {
101 content = h('img', {
102 src: `http://localhost:8989/blobs/get/${encodeURIComponent(file.link)}`,
103 alt: file.name
104 })
105 } else {
106 content = h('a', { href: file.link }, file.name)
107 }
108 // TODO - insert where the mouse is yo
109 textArea.appendChild(h('p', content))
110
111 console.log('added:', file)
112 })
113
114 return fileInput
115}
116
117function initialiseDummyComposer ({ meta, api, filesById }) {
118 return api.message.html.compose(
119 {
120 meta,
121 // placeholder: strings.blogNew.actions.writeBlog,
122 shrink: false,
123 filesById,
124 prepublish: function (content, cb) {
125 var m = /\!\[[^]+\]\(([^\)]+)\)/.exec(marksum.image(content.text))
126 content.thumbnail = m && m[1]
127 // content.summary = marksum.summary(content.text) // TODO Need a summary which doesn't trim the start
128
129 var stream = pull.values([content.text])
130 api.sbot.async.addBlob(stream, function (err, hash) {
131 if (err) return cb(err)
132 if (!hash) throw new Error('missing hash')
133 content.blog = hash
134 delete content.text
135 cb(null, content)
136 })
137 }
138 },
139 (err, msg) => api.history.sync.push(err || { page: 'blogIndex' })
140 )
141}
142
143function initialiseChannelSuggests ({ input, suggester, meta }) {
144 addSuggest(input, (inputText, cb) => {
145 inputText = inputText.replace(/^#/, '')
146 var suggestions = suggester(inputText)
147 .map(s => {
148 s.value = s.value.replace(/^#/, '') // strip the defualt # prefix here
149 return s
150 })
151 .map(s => {
152 if (s.subtitle === 'subscribed') { s.subtitle = h('i.fa.fa-heart') } // TODO - translation-friendly subscribed
153 return s
154 })
155
156 // HACK add the input text if it's not an option already
157 if (!suggestions.some(s => s.title === inputText)) {
158 suggestions.push({
159 title: inputText,
160 subtitle: h('i.fa.fa-plus-circle'),
161 value: inputText
162 })
163 }
164
165 cb(null, suggestions)
166 }, {cls: 'PatchSuggest.-channelHorizontal'}) // WARNING hacking suggest-box cls
167
168 input.addEventListener('suggestselect', (e) => {
169 meta.channel.set(e.detail.value)
170 })
171}
172
173function initialiseMedium ({ page, el, meta }) {
174 var editor = new MediumEditor(el, {
175 elementsContainer: page,
176 toolbar: {
177 allowMultiParagraphSelection: true,
178 buttons: ['bold', 'italic', 'underline', 'anchor', 'h2', 'h3', 'quote'],
179 diffLeft: 0,
180 diffTop: -10,
181 firstButtonClass: 'medium-editor-button-first',
182 lastButtonClass: 'medium-editor-button-last',
183 relativeContainer: null,
184 standardizeSelectionStart: false,
185 static: false,
186 /* options which only apply when static is true */
187 align: 'center',
188 sticky: false,
189 updateOnEmptySelection: false
190 },
191 extensions: {
192 markdown: new MediumToMD(
193 {
194 toMarkdownOptions: {
195 converters: [{
196 filter: 'img',
197 replacement: (content, node) => {
198 var blob = decodeURIComponent(node.src.replace('http://localhost:8989/blobs/get/', ''))
199 return `![${node.alt}](${blob})`
200 }
201 }]
202 },
203 events: ['input', 'change', 'DOMNodeInserted']
204 },
205 md => meta.text.set(md)
206 )
207 }
208 })
209
210 return editor
211}
212

Built with git-ssb-web