Files: fea2881ae175c88bac926fcff65d32598742e9a1 / app / page / threadNew.js
4258 bytesRaw
1 | const nest = require('depnest') |
2 | const { h, Struct, Value, Array: MutantArray, computed, map, resolve } = require('mutant') |
3 | const suggestBox = require('suggest-box') |
4 | const isEmpty = require('lodash/isEmpty') |
5 | |
6 | exports.gives = nest('app.page.threadNew') |
7 | |
8 | exports.needs = nest({ |
9 | 'about.async.suggest': 'first', |
10 | 'about.html.avatar': 'first', |
11 | 'about.obs.name': 'first', |
12 | 'app.html.sideNav': 'first', |
13 | 'app.html.thread': 'first', |
14 | 'history.sync.push': 'first', |
15 | 'keys.sync.id': 'first', |
16 | 'message.html.compose': 'first', |
17 | 'message.sync.unbox': 'first', |
18 | 'translations.sync.strings': 'first' |
19 | }) |
20 | |
21 | exports.create = (api) => { |
22 | return nest('app.page.threadNew', threadNew) |
23 | |
24 | function threadNew (location) { |
25 | if (isEmpty(location.participants)) return |
26 | |
27 | return threadNewFeed(location) |
28 | } |
29 | |
30 | function threadNewFeed (location) { |
31 | const strings = api.translations.sync.strings() |
32 | const myId = api.keys.sync.id() |
33 | |
34 | const { participants } = location |
35 | |
36 | const meta = Struct({ |
37 | type: 'post', |
38 | recps: MutantArray([ |
39 | myId, |
40 | ...participants.map(p => { |
41 | return { |
42 | link: p, |
43 | name: resolve(api.about.obs.name(p)) |
44 | } |
45 | }) |
46 | ]), |
47 | subject: Value() |
48 | }) |
49 | |
50 | return h('Page -threadNew', {title: strings.threadNew.pageTitle}, [ |
51 | api.app.html.sideNav(location), |
52 | h('div.content', [ |
53 | h('div.container', [ |
54 | h('div.field -to', [ |
55 | h('div.label', strings.threadNew.field.to), |
56 | h('div.recps', [ |
57 | map(meta.recps, Recipient), |
58 | RecipientInput(meta.recps) |
59 | ]) |
60 | ]), |
61 | h('div.field -subject', [ |
62 | h('div.label', strings.threadNew.field.subject), |
63 | h('input', { |
64 | 'ev-input': e => meta.subject.set(e.target.value), |
65 | placeholder: strings.optionalField |
66 | }) |
67 | ]), |
68 | Composer(meta) |
69 | ]) |
70 | ]) |
71 | ]) |
72 | |
73 | function Recipient (r) { |
74 | if (r === myId) return |
75 | |
76 | return h('div.recp', [ |
77 | api.about.html.avatar(r.link, 'tiny'), |
78 | h('div.name', r.name) |
79 | ]) |
80 | } |
81 | |
82 | function RecipientInput (recps) { |
83 | const getRecpSuggestions = api.about.async.suggest() |
84 | const input = h('input', { |
85 | placeholder: strings.threadNew.action.addMoreRecps |
86 | // 'ev-input': e => searchInput.set(e.target.value), |
87 | }) |
88 | |
89 | var boxActive = false |
90 | addSuggest() |
91 | |
92 | input.addEventListener('suggestselect', (e) => { |
93 | const { id, title: name } = e.detail |
94 | if (!recps.find(r => r === id || r.link === id)) { recps.push({ link: id, name }) } |
95 | |
96 | boxActive = false |
97 | e.target.value = '' |
98 | e.target.placeholder = '' |
99 | }) |
100 | |
101 | input.addEventListener('keyup', (e) => { |
102 | // don't pop the previous entry if still entering a name! |
103 | if (boxActive) { |
104 | // if you delete a name you were typing completely, mark box inactive |
105 | // so that further deletes pop names |
106 | if (e.target.value === '') boxActive = false |
107 | return |
108 | } |
109 | |
110 | if (e.code === 'Backspace' || e.key === 'Backspace' || e.keyCode == 8) { |
111 | if (recps.getLength() < 3) return // can only delete down to 2 recps (sender + 1 recp) |
112 | |
113 | recps.pop() |
114 | } |
115 | }) |
116 | |
117 | return input |
118 | |
119 | function addSuggest () { |
120 | if (!input.parentElement) return setTimeout(addSuggest, 100) |
121 | |
122 | suggestBox(input, (inputText, cb) => { |
123 | if (recps.getLength() >= 7) return |
124 | // TODO - tell the user they're only allowed 6 (or 7?!) people in a message |
125 | |
126 | boxActive = true |
127 | const suggestions = getRecpSuggestions(inputText) |
128 | cb(null, suggestions) |
129 | }, {cls: 'PatchSuggest'}) |
130 | } |
131 | } |
132 | |
133 | function Composer (meta) { |
134 | return api.message.html.compose( |
135 | { meta, shrink: false }, |
136 | (err, msg) => { |
137 | if (err) return api.history.sync.push(err) |
138 | |
139 | const someRecipient = meta.recps.get(1) |
140 | if (!someRecipient) return |
141 | |
142 | api.history.sync.push(Object.assign( |
143 | api.message.sync.unbox(msg), // for consistency of message sideNav receives D: |
144 | { feed: someRecipient.link } |
145 | )) |
146 | } |
147 | ) |
148 | } |
149 | } |
150 | } |
151 |
Built with git-ssb-web