Commit e2fb4d766af3a45d510a0558e410b04dd3366e13
Merge pull request #86 from ticktackim/multi_person_pm
Multi person pmmix irving authored on 2/6/2018, 9:59:01 AM
GitHub committed on 2/6/2018, 9:59:01 AM
Parent: 71dbadafffeb775edb0eb90985feeac67464d988
Parent: bd8064b6ef447f00a6709b42bfe0881145a51ad8
Files changed
app/page/threadNew.js | changed |
app/page/threadNew.mcss | changed |
app/page/threadShow.js | changed |
app/page/threadShow.mcss | changed |
translations/en.js | changed |
app/page/threadNew.js | ||
---|---|---|
@@ -1,17 +1,20 @@ | ||
1 | 1 | const nest = require('depnest') |
2 | -const { h, Struct, Value, computed } = require('mutant') | |
2 | +const { h, Struct, Value, Array: MutantArray, computed, map, resolve } = require('mutant') | |
3 | +const suggestBox = require('suggest-box') | |
3 | 4 | |
4 | 5 | exports.gives = nest('app.page.threadNew') |
5 | 6 | |
6 | 7 | exports.needs = nest({ |
7 | - 'about.html.image': 'first', | |
8 | + 'about.async.suggest': 'first', | |
9 | + 'about.html.avatar': 'first', | |
8 | 10 | 'about.obs.name': 'first', |
9 | 11 | 'app.html.sideNav': 'first', |
10 | 12 | 'app.html.thread': 'first', |
11 | 13 | 'history.sync.push': 'first', |
12 | 14 | 'keys.sync.id': 'first', |
13 | 15 | 'message.html.compose': 'first', |
16 | + 'message.sync.unbox': 'first', | |
14 | 17 | 'translations.sync.strings': 'first', |
15 | 18 | }) |
16 | 19 | |
17 | 20 | exports.create = (api) => { |
@@ -21,41 +24,34 @@ | ||
21 | 24 | function threadNew (location) { |
22 | 25 | const { feed, channel } = location |
23 | 26 | |
24 | 27 | if (feed) return threadNewFeed(location) |
25 | - // if (channel) return threadNewChannel(location) | |
26 | 28 | } |
27 | 29 | |
28 | 30 | function threadNewFeed (location) { |
29 | 31 | const strings = api.translations.sync.strings() |
32 | + const myId = api.keys.sync.id() | |
30 | 33 | |
31 | 34 | const { feed } = location |
32 | - const name = api.about.obs.name(feed) | |
33 | 35 | |
34 | 36 | const meta = Struct({ |
35 | 37 | type: 'post', |
36 | - recps: [ | |
37 | - api.keys.sync.id(), | |
38 | - { link: feed, name } | |
39 | - ], | |
38 | + recps: MutantArray ([ | |
39 | + myId, | |
40 | + { link: feed, name: resolve(api.about.obs.name(feed)) } | |
41 | + ]), | |
40 | 42 | subject: Value() |
41 | 43 | }) |
42 | - const composer = api.message.html.compose( | |
43 | - { meta, shrink: false }, | |
44 | - (err, msg) => api.history.sync.push(err ? err : Object.assign(msg, { feed })) | |
45 | - ) | |
46 | 44 | |
47 | 45 | return h('Page -threadNew', {title: strings.threadNew.pageTitle}, [ |
48 | 46 | api.app.html.sideNav(location), |
49 | 47 | h('div.content', [ |
50 | 48 | h('div.container', [ |
51 | 49 | h('div.field -to', [ |
52 | 50 | h('div.label', strings.threadNew.field.to), |
53 | 51 | h('div.recps', [ |
54 | - h('div.recp', [ | |
55 | - api.about.html.image(feed), | |
56 | - h('div.name', name) | |
57 | - ]) | |
52 | + map(meta.recps, Recipient), | |
53 | + RecipientInput(meta.recps) | |
58 | 54 | ]) |
59 | 55 | ]), |
60 | 56 | h('div.field -subject', [ |
61 | 57 | h('div.label', strings.threadNew.field.subject), |
@@ -63,49 +59,90 @@ | ||
63 | 59 | 'ev-input': e => meta.subject.set(e.target.value), |
64 | 60 | placeholder: strings.optionalField |
65 | 61 | }), |
66 | 62 | ]), |
67 | - composer | |
63 | + Composer(meta) | |
68 | 64 | ]) |
69 | 65 | ]) |
70 | 66 | ]) |
71 | - } | |
72 | 67 | |
73 | - // function threadNewChannel (location) { | |
74 | - // const strings = api.translations.sync.strings() | |
68 | + function Recipient (r) { | |
69 | + if (r === myId) return | |
75 | 70 | |
76 | - // const { channel, flash } = location | |
71 | + return h('div.recp', [ | |
72 | + api.about.html.avatar(r.link, 'tiny'), | |
73 | + h('div.name', r.name) | |
74 | + ]) | |
75 | + } | |
77 | 76 | |
78 | - // const meta = Struct({ | |
79 | - // type: 'post', | |
80 | - // channel, | |
81 | - // subject: Value() | |
82 | - // }) | |
83 | - // const composer = api.message.html.compose( | |
84 | - // { meta, shrink: false }, | |
85 | - // (err, msg) => api.history.sync.push(err ? err : msg) | |
86 | - // ) | |
77 | + function RecipientInput (recps) { | |
78 | + const getRecpSuggestions = api.about.async.suggest() | |
79 | + const input = h('input', { | |
80 | + placeholder: strings.threadNew.action.addMoreRecps | |
81 | + // 'ev-input': e => searchInput.set(e.target.value), | |
82 | + }) | |
87 | 83 | |
88 | - // return h('Page -threadNew', {title: strings.threadNew.pageTitle}, [ | |
89 | - // h('div.content', [ | |
90 | - // flash ? h('div.flash', flash) : '', | |
91 | - // h('div.field -channel', [ | |
92 | - // h('div.label', strings.channel), | |
93 | - // h('div.recps', [ | |
94 | - // h('div.recp', [ | |
95 | - // h('div.name', `#${channel}`) | |
96 | - // ]) | |
97 | - // ]) | |
98 | - // ]), | |
99 | - // h('div.field -subject', [ | |
100 | - // h('div.label', strings.threadNew.field.subject), | |
101 | - // h('input', { | |
102 | - // 'ev-input': e => meta.subject.set(e.target.value), | |
103 | - // placeholder: strings.optionalField | |
104 | - // }), | |
105 | - // ]), | |
106 | - // composer | |
107 | - // ]) | |
108 | - // ]) | |
109 | - // } | |
84 | + var boxActive = false | |
85 | + addSuggest() | |
86 | + | |
87 | + input.addEventListener('suggestselect', (e) => { | |
88 | + const { id, title: name } = e.detail | |
89 | + if (!recps.find(r => r === id || r.link === id)) | |
90 | + recps.push({ link: id, name }) | |
91 | + | |
92 | + boxActive = false | |
93 | + e.target.value = '' | |
94 | + e.target.placeholder = '' | |
95 | + }) | |
96 | + | |
97 | + input.addEventListener('keyup', (e) => { | |
98 | + // don't pop the previous entry if still entering a name! | |
99 | + if (boxActive) { | |
100 | + // if you delete a name you were typing completely, mark box inactive | |
101 | + // so that further deletes pop names | |
102 | + if (e.target.value === '') boxActive = false | |
103 | + return | |
104 | + } | |
105 | + | |
106 | + if (e.code === 'Backspace' || e.key === 'Backspace' || e.keyCode == 8) { | |
107 | + if (recps.getLength() < 3) return // can only delete down to 2 recps (sender + 1 recp) | |
108 | + | |
109 | + recps.pop() | |
110 | + } | |
111 | + }) | |
112 | + | |
113 | + return input | |
114 | + | |
115 | + function addSuggest () { | |
116 | + if (!input.parentElement) return setTimeout(addSuggest, 100) | |
117 | + | |
118 | + suggestBox(input, (inputText, cb) => { | |
119 | + if (recps.getLength() >= 7) return | |
120 | + // TODO - tell the user they're only allowed 6 (or 7?!) people in a message | |
121 | + | |
122 | + boxActive = true | |
123 | + const suggestions = getRecpSuggestions(inputText) | |
124 | + cb(null, suggestions) | |
125 | + }, {cls: 'PatchSuggest'}) | |
126 | + } | |
127 | + } | |
128 | + | |
129 | + function Composer (meta) { | |
130 | + return api.message.html.compose( | |
131 | + { meta, shrink: false }, | |
132 | + (err, msg) => { | |
133 | + if (err) return api.history.sync.push(err) | |
134 | + | |
135 | + const someRecipient = meta.recps.get(1) | |
136 | + if (!someRecipient) return | |
137 | + | |
138 | + api.history.sync.push(Object.assign( | |
139 | + api.message.sync.unbox(msg), // for consistency of message sideNav receives D: | |
140 | + { feed: someRecipient.link } | |
141 | + )) | |
142 | + } | |
143 | + ) | |
144 | + } | |
145 | + } | |
146 | + | |
110 | 147 | } |
111 | 148 |
app/page/threadNew.mcss | ||
---|---|---|
@@ -36,32 +36,40 @@ | ||
36 | 36 | outline: none |
37 | 37 | } |
38 | 38 | |
39 | 39 | div.recps { |
40 | + flex-grow: 1 | |
41 | + height: 2.5rem | |
42 | + | |
43 | + $backgroundPrimaryText | |
44 | + padding-left: .5rem | |
45 | + $borderSubtle | |
46 | + border-radius: 6rem | |
47 | + | |
40 | 48 | display: flex |
41 | 49 | |
42 | 50 | div.recp { |
43 | - padding: .3rem | |
44 | - min-width: 5rem | |
45 | - $borderSubtle | |
46 | - border-radius: 6rem | |
47 | - $backgroundPrimaryText | |
48 | - | |
49 | 51 | margin-right: 1rem |
50 | 52 | |
51 | 53 | display: flex |
52 | 54 | align-items: center |
53 | 55 | |
54 | - img { | |
55 | - width: 2rem | |
56 | - height: 2rem | |
57 | - border-radius: 2rem | |
56 | + img.Avatar { | |
58 | 57 | } |
59 | 58 | |
60 | 59 | div.name { |
61 | 60 | margin: 0 .5rem |
62 | 61 | } |
63 | 62 | } |
63 | + | |
64 | + input { | |
65 | + flex-grow: 1 | |
66 | + | |
67 | + $fontBasic | |
68 | + margin-right: 1rem | |
69 | + border: none | |
70 | + outline: none | |
71 | + } | |
64 | 72 | } |
65 | 73 | |
66 | 74 | } |
67 | 75 | } |
app/page/threadShow.js | ||
---|---|---|
@@ -5,8 +5,9 @@ | ||
5 | 5 | |
6 | 6 | exports.gives = nest('app.page.threadShow') |
7 | 7 | |
8 | 8 | exports.needs = nest({ |
9 | + 'about.html.avatar': 'first', | |
9 | 10 | 'app.html.sideNav': 'first', |
10 | 11 | 'app.html.thread': 'first', |
11 | 12 | 'message.html.compose': 'first', |
12 | 13 | 'unread.sync.markRead': 'first', |
@@ -23,34 +24,38 @@ | ||
23 | 24 | const channel = get(value, 'content.channel') |
24 | 25 | |
25 | 26 | //unread state is set in here... |
26 | 27 | const thread = api.feed.obs.thread(root) |
28 | + const subject = get(location, 'value.content.subject') | |
29 | + const recps = get(location, 'value.content.recps') | |
27 | 30 | |
28 | 31 | const meta = { |
29 | 32 | type: 'post', |
30 | 33 | root, |
31 | 34 | branch: thread.lastId, |
32 | 35 | channel, |
33 | - recps: get(location, 'value.content.recps'), | |
36 | + recps | |
34 | 37 | } |
35 | 38 | const composer = api.message.html.compose({ meta, thread, shrink: false }) |
36 | 39 | |
37 | 40 | return h('Page -threadShow', [ |
38 | 41 | api.app.html.sideNav(location), |
39 | 42 | h('div.content', [ |
40 | - when(thread.subject, h('h1', thread.subject)), | |
43 | + h('header', [ | |
44 | + when(subject, h('h1', subject)), | |
45 | + Recipients(recps), | |
46 | + ]), | |
41 | 47 | api.app.html.thread(thread), |
42 | 48 | composer |
43 | 49 | ]), |
44 | 50 | ]) |
45 | 51 | } |
52 | + | |
53 | + function Recipients (recps) { | |
54 | + if (recps && recps.length > 2) | |
55 | + return h('div.recps', recps.map(r => { | |
56 | + const recp = typeof r === 'string' ? r : r.link | |
57 | + return api.about.html.avatar(recp, 'tiny') | |
58 | + })) | |
59 | + } | |
46 | 60 | } |
47 | 61 | |
48 | - | |
49 | - | |
50 | - | |
51 | - | |
52 | - | |
53 | - | |
54 | - | |
55 | - | |
56 | - |
app/page/threadShow.mcss | ||
---|---|---|
@@ -1,7 +1,22 @@ | ||
1 | 1 | Page -threadShow { |
2 | 2 | div.content { |
3 | + header { | |
4 | + display: flex | |
5 | + align-items: center | |
6 | + flex-wrap: wrap | |
3 | 7 | |
8 | + h1 { | |
9 | + margin-right: 1rem | |
10 | + } | |
11 | + | |
12 | + div.recps { | |
13 | + img.Avatar { | |
14 | + margin-right: .6rem | |
15 | + } | |
16 | + } | |
17 | + } | |
18 | + | |
4 | 19 | div.Thread {} |
5 | 20 | |
6 | 21 | div.Compose { |
7 | 22 | margin: 0 auto |
Built with git-ssb-web