git ssb

2+

mixmix / ticktack



Commit e2fb4d766af3a45d510a0558e410b04dd3366e13

Merge pull request #86 from ticktackim/multi_person_pm

Multi person pm
mix 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.jschanged
app/page/threadNew.mcsschanged
app/page/threadShow.jschanged
app/page/threadShow.mcsschanged
translations/en.jschanged
app/page/threadNew.jsView
@@ -1,17 +1,20 @@
11 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')
34
45 exports.gives = nest('app.page.threadNew')
56
67 exports.needs = nest({
7- 'about.html.image': 'first',
8+ 'about.async.suggest': 'first',
9+ 'about.html.avatar': 'first',
810 'about.obs.name': 'first',
911 'app.html.sideNav': 'first',
1012 'app.html.thread': 'first',
1113 'history.sync.push': 'first',
1214 'keys.sync.id': 'first',
1315 'message.html.compose': 'first',
16+ 'message.sync.unbox': 'first',
1417 'translations.sync.strings': 'first',
1518 })
1619
1720 exports.create = (api) => {
@@ -21,41 +24,34 @@
2124 function threadNew (location) {
2225 const { feed, channel } = location
2326
2427 if (feed) return threadNewFeed(location)
25- // if (channel) return threadNewChannel(location)
2628 }
2729
2830 function threadNewFeed (location) {
2931 const strings = api.translations.sync.strings()
32+ const myId = api.keys.sync.id()
3033
3134 const { feed } = location
32- const name = api.about.obs.name(feed)
3335
3436 const meta = Struct({
3537 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+ ]),
4042 subject: Value()
4143 })
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- )
4644
4745 return h('Page -threadNew', {title: strings.threadNew.pageTitle}, [
4846 api.app.html.sideNav(location),
4947 h('div.content', [
5048 h('div.container', [
5149 h('div.field -to', [
5250 h('div.label', strings.threadNew.field.to),
5351 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)
5854 ])
5955 ]),
6056 h('div.field -subject', [
6157 h('div.label', strings.threadNew.field.subject),
@@ -63,49 +59,90 @@
6359 'ev-input': e => meta.subject.set(e.target.value),
6460 placeholder: strings.optionalField
6561 }),
6662 ]),
67- composer
63+ Composer(meta)
6864 ])
6965 ])
7066 ])
71- }
7267
73- // function threadNewChannel (location) {
74- // const strings = api.translations.sync.strings()
68+ function Recipient (r) {
69+ if (r === myId) return
7570
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+ }
7776
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+ })
8783
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+
110147 }
111148
app/page/threadNew.mcssView
@@ -36,32 +36,40 @@
3636 outline: none
3737 }
3838
3939 div.recps {
40+ flex-grow: 1
41+ height: 2.5rem
42+
43+ $backgroundPrimaryText
44+ padding-left: .5rem
45+ $borderSubtle
46+ border-radius: 6rem
47+
4048 display: flex
4149
4250 div.recp {
43- padding: .3rem
44- min-width: 5rem
45- $borderSubtle
46- border-radius: 6rem
47- $backgroundPrimaryText
48-
4951 margin-right: 1rem
5052
5153 display: flex
5254 align-items: center
5355
54- img {
55- width: 2rem
56- height: 2rem
57- border-radius: 2rem
56+ img.Avatar {
5857 }
5958
6059 div.name {
6160 margin: 0 .5rem
6261 }
6362 }
63+
64+ input {
65+ flex-grow: 1
66+
67+ $fontBasic
68+ margin-right: 1rem
69+ border: none
70+ outline: none
71+ }
6472 }
6573
6674 }
6775 }
app/page/threadShow.jsView
@@ -5,8 +5,9 @@
55
66 exports.gives = nest('app.page.threadShow')
77
88 exports.needs = nest({
9+ 'about.html.avatar': 'first',
910 'app.html.sideNav': 'first',
1011 'app.html.thread': 'first',
1112 'message.html.compose': 'first',
1213 'unread.sync.markRead': 'first',
@@ -23,34 +24,38 @@
2324 const channel = get(value, 'content.channel')
2425
2526 //unread state is set in here...
2627 const thread = api.feed.obs.thread(root)
28+ const subject = get(location, 'value.content.subject')
29+ const recps = get(location, 'value.content.recps')
2730
2831 const meta = {
2932 type: 'post',
3033 root,
3134 branch: thread.lastId,
3235 channel,
33- recps: get(location, 'value.content.recps'),
36+ recps
3437 }
3538 const composer = api.message.html.compose({ meta, thread, shrink: false })
3639
3740 return h('Page -threadShow', [
3841 api.app.html.sideNav(location),
3942 h('div.content', [
40- when(thread.subject, h('h1', thread.subject)),
43+ h('header', [
44+ when(subject, h('h1', subject)),
45+ Recipients(recps),
46+ ]),
4147 api.app.html.thread(thread),
4248 composer
4349 ]),
4450 ])
4551 }
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+ }
4660 }
4761
48-
49-
50-
51-
52-
53-
54-
55-
56-
app/page/threadShow.mcssView
@@ -1,7 +1,22 @@
11 Page -threadShow {
22 div.content {
3+ header {
4+ display: flex
5+ align-items: center
6+ flex-wrap: wrap
37
8+ h1 {
9+ margin-right: 1rem
10+ }
11+
12+ div.recps {
13+ img.Avatar {
14+ margin-right: .6rem
15+ }
16+ }
17+ }
18+
419 div.Thread {}
520
621 div.Compose {
722 margin: 0 auto
translations/en.jsView
@@ -86,9 +86,10 @@
8686 to: 'To',
8787 subject: 'Subject',
8888 },
8989 action: {
90- new: 'New Message'
90+ new: 'New Message',
91+ addMoreRecps: 'add more recipients (optional)'
9192 }
9293 },
9394 threadShow: 'Direct Messages',
9495 userEdit: {

Built with git-ssb-web