git ssb

10+

Matt McKegg / patchwork



Tree: 88e355bf1c958e3361645fd8f0682f252a313b68

Files: 88e355bf1c958e3361645fd8f0682f252a313b68 / modules / message / html / compose.js

5135 bytesRaw
1var h = require('mutant/h')
2var when = require('mutant/when')
3var send = require('mutant/send')
4var resolve = require('mutant/resolve')
5var Value = require('mutant/value')
6var computed = require('mutant/computed')
7var nest = require('depnest')
8var mentions = require('ssb-mentions')
9var extend = require('xtend')
10var addSuggest = require('suggest-box')
11
12exports.needs = nest({
13 'blob.html.input': 'first',
14 'profile.async.suggest': 'first',
15 'channel.async.suggest': 'first',
16 'message.async.publish': 'first',
17 'emoji.sync.names': 'first',
18 'emoji.sync.url': 'first'
19})
20
21exports.gives = nest('message.html.compose')
22
23exports.create = function (api) {
24 return nest('message.html.compose', function ({shrink = true, meta, prepublish, placeholder = 'Write a message'}, cb) {
25 var files = []
26 var filesById = {}
27 var focused = Value(false)
28 var hasContent = Value(false)
29 var publishing = Value(false)
30 var getProfileSuggestions = api.profile.async.suggest()
31 var getChannelSuggestions = api.channel.async.suggest()
32
33 var blurTimeout = null
34
35 var expanded = computed([shrink, focused, hasContent], (shrink, focused, hasContent) => {
36 if (!shrink || hasContent) {
37 return true
38 } else {
39 return focused
40 }
41 })
42
43 var textArea = h('textarea', {
44 'ev-input': function () {
45 hasContent.set(!!textArea.value)
46 },
47 'ev-blur': () => {
48 clearTimeout(blurTimeout)
49 blurTimeout = setTimeout(() => focused.set(false), 200)
50 },
51 'ev-focus': send(focused.set, true),
52 disabled: publishing,
53 placeholder
54 })
55
56 var warningMessage = Value(null)
57 var warning = h('section.warning',
58 { className: when(warningMessage, '-open', '-closed') },
59 [
60 h('div.warning', warningMessage),
61 h('div.close', { 'ev-click': () => warningMessage.set(null) }, 'x')
62 ]
63 )
64 var fileInput = api.blob.html.input(file => {
65 const megabytes = file.size / 1024 / 1024
66 if (megabytes >= 5) {
67 const rounded = Math.floor(megabytes*100)/100
68 warningMessage.set([
69 h('i.fa.fa-exclamation-triangle'),
70 h('strong', file.name),
71 ` is ${rounded}MB - the current limit is 5MB`
72 ])
73 return
74 }
75
76 files.push(file)
77 filesById[file.link] = file
78
79 var embed = file.type.indexOf('image/') === 0 ? '!' : ''
80 var pos = textArea.selectionStart
81 var before = textArea.value.slice(0, pos)
82 var after = textArea.value.slice(pos)
83
84 var spacer = embed ? '\n' : ' '
85 if (before && !before.endsWith(spacer)) before += spacer
86 if (!after.startsWith(spacer)) after = spacer + after
87
88 textArea.value = `${before}${embed}[${file.name}](${file.link})${after}`
89 console.log('added:', file)
90 })
91
92 fileInput.onclick = function () {
93 hasContent.set(true)
94 }
95
96 var publishBtn = h('button', {
97 'ev-click': publish,
98 disabled: publishing
99 }, when(publishing, 'Publishing...', 'Publish'))
100
101 var actions = h('section.actions', [
102 fileInput,
103 publishBtn
104 ])
105
106 var composer = h('Compose', {
107 classList: [
108 when(expanded, '-expanded', '-contracted')
109 ]
110 }, [
111 textArea,
112 warning,
113 actions
114 ])
115
116 addSuggest(textArea, (inputText, cb) => {
117 if (inputText[0] === '@') {
118 cb(null, getProfileSuggestions(inputText.slice(1)))
119 } else if (inputText[0] === '#') {
120 cb(null, getChannelSuggestions(inputText.slice(1)))
121 } else if (inputText[0] === ':') {
122 // suggest emojis
123 var word = inputText.slice(1)
124 if (word[word.length - 1] === ':') {
125 word = word.slice(0, -1)
126 }
127 // TODO: when no emoji typed, list some default ones
128 cb(null, api.emoji.sync.names().filter(function (name) {
129 return name.slice(0, word.length) === word
130 }).slice(0, 100).map(function (emoji) {
131 return {
132 image: api.emoji.sync.url(emoji),
133 title: emoji,
134 subtitle: emoji,
135 value: ':' + emoji + ':'
136 }
137 }))
138 }
139 }, {cls: 'SuggestBox'})
140
141 return composer
142
143 // scoped
144
145 function publish () {
146 publishing.set(true)
147
148 var content = extend(resolve(meta), {
149 text: textArea.value,
150 mentions: mentions(textArea.value).map(mention => {
151 // merge markdown-detected mention with file info
152 var file = filesById[mention.link]
153 if (file) {
154 if (file.type) mention.type = file.type
155 if (file.size) mention.size = file.size
156 }
157 return mention
158 })
159 })
160
161 try {
162 if (typeof prepublish === 'function') {
163 content = prepublish(content)
164 }
165 } catch (err) {
166 publishing.set(false)
167 if (cb) cb(err)
168 else throw err
169 }
170
171 return api.message.async.publish(content, done)
172
173 function done (err, msg) {
174 publishing.set(false)
175 if (err) throw err
176 else if (msg) textArea.value = ''
177 if (cb) cb(err, msg)
178 }
179 }
180 })
181}
182

Built with git-ssb-web