Commit 390dfd4c75d9544f0b36ee3341d2ac1852b2ce56
threadNew -posta new message thread to a single person
mix irving committed on 8/17/2017, 2:11:22 AMParent: f61a6365dcfa4962dd501e41f3afb98a390c9947
Files changed
app/html/thread.js | changed |
app/html/thread.mcss | changed |
app/page/page.mcss | changed |
app/page/threadNew.js | changed |
app/page/threadShow.js | changed |
app/page/threadNew.mcss | added |
message/html/compose.js | changed |
message/html/compose.mcss | changed |
styles/button.mcss | changed |
styles/global.mcss | changed |
styles/mixins.js | changed |
translations/sync.js | changed |
app/html/thread.js | ||
---|---|---|
@@ -50,8 +50,11 @@ | ||
50 | 50 | |
51 | 51 | return h('div.msg', api.message.html.markdown(raw)) |
52 | 52 | } |
53 | 53 | |
54 | + threadView.subject = computed(thread.messages, msgs => { | |
55 | + return get(msgs, '[0].value.content.subject') | |
56 | + }) | |
54 | 57 | return threadView |
55 | 58 | } |
56 | 59 | } |
57 | 60 |
app/html/thread.mcss | ||
---|---|---|
@@ -13,9 +13,9 @@ | ||
13 | 13 | |
14 | 14 | div.msgs { |
15 | 15 | div.msg-row { |
16 | 16 | div.msg { |
17 | - $primaryColor | |
17 | + $colorPrimary | |
18 | 18 | |
19 | 19 | (a) { color: #4d3568 } |
20 | 20 | $roundLeft |
21 | 21 | } |
app/page/page.mcss | ||
---|---|---|
@@ -1,23 +1,17 @@ | ||
1 | 1 | Page { |
2 | - h1 { | |
3 | - margin-left: 1rem | |
4 | - } | |
2 | + $primaryBackground | |
5 | 3 | |
6 | - div.Nav {} | |
7 | - | |
8 | 4 | div.container { |
9 | - $primaryBackground | |
5 | + $maxWidth | |
10 | 6 | padding: 1rem |
7 | + margin: 0 auto | |
11 | 8 | |
12 | 9 | div.Thread { |
13 | - $maxWidth | |
14 | - margin: 0 auto 1rem | |
10 | + margin-bottom: 1rem | |
15 | 11 | } |
16 | 12 | |
17 | 13 | div.Compose { |
18 | - $maxWidth | |
19 | - margin: 0 auto | |
20 | 14 | } |
21 | 15 | } |
22 | 16 | } |
23 | 17 |
app/page/threadNew.js | ||
---|---|---|
@@ -1,28 +1,61 @@ | ||
1 | 1 | const nest = require('depnest') |
2 | -const { h } = require('mutant') | |
2 | +const { h, Struct, Value, computed } = require('mutant') | |
3 | 3 | |
4 | 4 | exports.gives = nest('app.page.threadNew') |
5 | 5 | |
6 | 6 | exports.needs = nest({ |
7 | 7 | 'translations.sync.strings': 'first', |
8 | 8 | 'about.html.image': 'first', |
9 | 9 | 'about.obs.name': 'first', |
10 | 10 | 'app.html.thread': 'first', |
11 | + 'history.sync.push': 'first', | |
12 | + 'keys.sync.id': 'first', | |
13 | + 'message.html.compose': 'first' | |
11 | 14 | }) |
12 | 15 | |
13 | 16 | exports.create = (api) => { |
14 | - var strings = api.translations.sync.strings() | |
15 | 17 | |
16 | 18 | return nest('app.page.threadNew', threadNew) |
17 | 19 | |
18 | 20 | function threadNew (location) { |
21 | + const strings = api.translations.sync.strings() | |
19 | 22 | const { feed } = location |
23 | + const name = api.about.obs.name(feed) | |
20 | 24 | |
21 | - return h('Page -threadNew', {title: strings.threadNew}, [ | |
22 | - h('h1', ['New thread with ', api.about.obs.name(feed)]), | |
23 | - api.about.html.image(feed), | |
24 | - h('div', 'compose box') // a special one which takes us to threadShow | |
25 | + const meta = Struct({ | |
26 | + type: 'post', | |
27 | + recps: [ | |
28 | + api.keys.sync.id(), | |
29 | + { link: feed, name } | |
30 | + ], | |
31 | + subject: Value() | |
32 | + }) | |
33 | + const composer = api.message.html.compose( | |
34 | + { meta, shrink: false }, | |
35 | + (err, msg) => api.history.sync.push(err ? err : msg) | |
36 | + ) | |
37 | + | |
38 | + return h('Page -threadNew', {title: strings.threadNew.pageTitle}, [ | |
39 | + h('div.container', [ | |
40 | + h('div.field -to', [ | |
41 | + h('div.label', strings.threadNew.field.to), | |
42 | + h('div.recps', [ | |
43 | + h('div.recp', [ | |
44 | + api.about.html.image(feed), | |
45 | + h('div.name', name) | |
46 | + ]) | |
47 | + ]) | |
48 | + ]), | |
49 | + h('div.field -subject', [ | |
50 | + h('div.label', strings.threadNew.field.subject), | |
51 | + h('input', { | |
52 | + 'ev-input': e => meta.subject.set(e.target.value), | |
53 | + placeholder: strings.optionalField | |
54 | + }), | |
55 | + ]), | |
56 | + composer | |
57 | + ]) | |
25 | 58 | ]) |
26 | 59 | } |
27 | 60 | } |
28 | 61 |
app/page/threadShow.js | ||
---|---|---|
@@ -1,6 +1,6 @@ | ||
1 | 1 | const nest = require('depnest') |
2 | -const { h } = require('mutant') | |
2 | +const { h, computed } = require('mutant') | |
3 | 3 | const last = require('lodash/last') |
4 | 4 | const get = require('lodash/get') |
5 | 5 | |
6 | 6 | exports.gives = nest('app.page.threadShow') |
@@ -30,11 +30,13 @@ | ||
30 | 30 | channel: channel, |
31 | 31 | recps: get(location, 'value.content.recps') |
32 | 32 | } |
33 | 33 | const composer = api.message.html.compose({ meta, shrink: false }) |
34 | + const subject = computed(thread.subject, subject => subject || strings.threadShow) | |
34 | 35 | |
35 | - return h('Page -threadShow', {title: strings.threadShow}, [ | |
36 | + return h('Page -threadShow', [ | |
36 | 37 | h('div.container', [ |
38 | + h('h1', subject), | |
37 | 39 | thread, |
38 | 40 | composer |
39 | 41 | ]), |
40 | 42 | ]) |
app/page/threadNew.mcss | ||
---|---|---|
@@ -1,0 +1,52 @@ | ||
1 | +Page -threadNew { | |
2 | + | |
3 | + div.container { | |
4 | + div.field { | |
5 | + margin-bottom: .5rem | |
6 | + | |
7 | + display: flex | |
8 | + align-items: center | |
9 | + | |
10 | + div.label { | |
11 | + flex-basis: 4rem | |
12 | + padding-left: 1rem | |
13 | + margin-right: 1rem | |
14 | + } | |
15 | + | |
16 | + input { | |
17 | + flex-grow: 1 | |
18 | + $fontBasic | |
19 | + padding: .6rem | |
20 | + } | |
21 | + | |
22 | + div.recps { | |
23 | + display: flex | |
24 | + | |
25 | + div.recp { | |
26 | + padding: .3rem | |
27 | + $borderSubtle | |
28 | + border-radius: 6rem | |
29 | + background-color: #fff | |
30 | + | |
31 | + margin-right: 1rem | |
32 | + | |
33 | + display: flex | |
34 | + align-items: center | |
35 | + | |
36 | + img { | |
37 | + width: 2rem | |
38 | + height: 2rem | |
39 | + border-radius: 2rem | |
40 | + | |
41 | + margin-right: .5rem | |
42 | + } | |
43 | + | |
44 | + div.name { | |
45 | + margin-right: .5rem | |
46 | + } | |
47 | + } | |
48 | + } | |
49 | + | |
50 | + } | |
51 | + } | |
52 | +} |
message/html/compose.js | ||
---|---|---|
@@ -13,14 +13,17 @@ | ||
13 | 13 | 'emoji.sync.names': 'first', |
14 | 14 | 'emoji.sync.url': 'first', |
15 | 15 | 'message.async.publish': 'first', |
16 | 16 | // 'message.html.confirm': 'first' |
17 | + 'translations.sync.strings': 'first' | |
17 | 18 | }) |
18 | 19 | |
19 | 20 | exports.create = function (api) { |
20 | 21 | return nest('message.html.compose', compose) |
21 | 22 | |
22 | 23 | function compose ({ shrink = true, meta, prepublish, placeholder = 'Write a message' }, cb) { |
24 | + const strings = api.translations.sync.strings() | |
25 | + | |
23 | 26 | var files = [] |
24 | 27 | var filesById = {} |
25 | 28 | var channelInputFocused = Value(false) |
26 | 29 | var textAreaFocused = Value(false) |
@@ -79,9 +82,9 @@ | ||
79 | 82 | }) |
80 | 83 | |
81 | 84 | fileInput.onclick = () => hasContent.set(true) |
82 | 85 | |
83 | - var publishBtn = h('button', { 'ev-click': publish }, 'Publish') | |
86 | + var publishBtn = h('Button -primary', { 'ev-click': publish }, strings.sendMessage) | |
84 | 87 | |
85 | 88 | var actions = h('section.actions', [ |
86 | 89 | fileInput, |
87 | 90 | publishBtn |
@@ -165,21 +168,26 @@ | ||
165 | 168 | content = prepublish(content) |
166 | 169 | } |
167 | 170 | } catch (err) { |
168 | 171 | publishBtn.disabled = false |
169 | - if (cb) cb(err) | |
170 | - else throw err | |
172 | + handleErr(err) | |
171 | 173 | } |
174 | + debugger | |
172 | 175 | |
173 | 176 | return api.message.async.publish(content, done) |
174 | 177 | // return api.message.html.confirm(content, done) |
175 | 178 | |
176 | 179 | function done (err, msg) { |
177 | 180 | publishBtn.disabled = false |
178 | - if (err) throw err | |
181 | + if (err) handleErr(err) | |
179 | 182 | else if (msg) textArea.value = '' |
180 | 183 | if (cb) cb(err, msg) |
181 | 184 | } |
185 | + | |
186 | + function handleErr (err) { | |
187 | + if (cb) cb(err) | |
188 | + else throw err | |
189 | + } | |
182 | 190 | } |
183 | 191 | } |
184 | 192 | } |
185 | 193 |
message/html/compose.mcss | ||
---|---|---|
@@ -2,18 +2,18 @@ | ||
2 | 2 | display: flex |
3 | 3 | flex-direction: column |
4 | 4 | |
5 | 5 | textarea { |
6 | - $basicText | |
6 | + $fontBasic | |
7 | 7 | |
8 | 8 | padding: .6rem |
9 | - border: 1px solid gainsboro | |
9 | + $borderSubtle | |
10 | 10 | border-top-left-radius: 0 |
11 | 11 | border-top-right-radius: 0 |
12 | 12 | } |
13 | 13 | |
14 | 14 | input.channel { |
15 | - border: 1px solid gainsboro | |
15 | + $borderSubtle | |
16 | 16 | border-bottom: none |
17 | 17 | border-bottom-left-radius: 0 |
18 | 18 | border-bottom-right-radius: 0 |
19 | 19 | padding: .5rem |
@@ -48,9 +48,9 @@ | ||
48 | 48 | visibility: hidden |
49 | 49 | } |
50 | 50 | |
51 | 51 | ::before { |
52 | - $composeButton | |
52 | + $attachButton | |
53 | 53 | padding-top: .3rem |
54 | 54 | |
55 | 55 | content: '๐' |
56 | 56 | font-size: 1rem |
@@ -65,14 +65,9 @@ | ||
65 | 65 | box-shadow: none |
66 | 66 | } |
67 | 67 | } |
68 | 68 | |
69 | - button { | |
70 | - $composeButton | |
71 | - | |
72 | - text-transform: uppercase | |
73 | - font-weight: bold | |
74 | - font-size: .7rem | |
69 | + div.Button { | |
75 | 70 | } |
76 | 71 | } |
77 | 72 | |
78 | 73 | -expanded { |
@@ -106,14 +101,12 @@ | ||
106 | 101 | } |
107 | 102 | |
108 | 103 | } |
109 | 104 | |
110 | -$composeButton { | |
105 | +$attachButton { | |
111 | 106 | background: #fff |
112 | 107 | color: #666 |
113 | - border: 1px solid #bbb | |
114 | - border-radius: .5rem | |
108 | + border: 1px solid #b9b9b9 | |
115 | 109 | padding: .5rem |
116 | 110 | margin: 0 |
117 | 111 | cursor: pointer |
118 | 112 | } |
119 | - |
styles/button.mcss | ||
---|---|---|
@@ -14,9 +14,9 @@ | ||
14 | 14 | color: #222 |
15 | 15 | } |
16 | 16 | } |
17 | 17 | -primary { |
18 | - $primaryColor | |
18 | + $colorPrimary | |
19 | 19 | |
20 | 20 | :hover { |
21 | 21 | opacity: .9 |
22 | 22 | } |
styles/global.mcss | ||
---|---|---|
@@ -1,6 +1,6 @@ | ||
1 | 1 | body { |
2 | - $basicText | |
2 | + $fontBasic | |
3 | 3 | background-color: #fff |
4 | 4 | |
5 | 5 | margin: 0 |
6 | 6 | |
@@ -17,7 +17,4 @@ | ||
17 | 17 | img { |
18 | 18 | max-width: 100% |
19 | 19 | } |
20 | 20 | |
21 | -button { | |
22 | - width: 100% | |
23 | -} |
styles/mixins.js | ||
---|---|---|
@@ -9,18 +9,18 @@ | ||
9 | 9 | }) |
10 | 10 | } |
11 | 11 | |
12 | 12 | const mainMixins = ` |
13 | -$basicText { | |
13 | +$fontBasic { | |
14 | 14 | font-family: arial |
15 | 15 | font-size: 1rem |
16 | 16 | } |
17 | 17 | |
18 | 18 | $maxWidth { |
19 | 19 | max-width: 1200px |
20 | 20 | } |
21 | 21 | |
22 | -$primaryColor { | |
22 | +$colorPrimary { | |
23 | 23 | color: white |
24 | 24 | background-color: #3dc8c3 |
25 | 25 | } |
26 | 26 | |
@@ -63,8 +63,12 @@ | ||
63 | 63 | } |
64 | 64 | } |
65 | 65 | } |
66 | 66 | |
67 | +$borderSubtle { | |
68 | + border: 1px solid #b9b9b9 | |
69 | +} | |
70 | + | |
67 | 71 | $roundLeft { |
68 | 72 | border-top-left-radius: 1.2rem |
69 | 73 | border-bottom-left-radius: 1.2rem |
70 | 74 | } |
translations/sync.js | ||
---|---|---|
@@ -7,8 +7,10 @@ | ||
7 | 7 | } |
8 | 8 | |
9 | 9 | const en = { |
10 | 10 | loading: 'Loading...', |
11 | + optionalField: 'optional', | |
12 | + sendMessage: 'Send', | |
11 | 13 | showMore: 'Show More', |
12 | 14 | channels: 'Channels', |
13 | 15 | directMessages: 'Direct Messages', |
14 | 16 | replySymbol: '> ', |
@@ -20,9 +22,16 @@ | ||
20 | 22 | groupIndex: "Group Index", |
21 | 23 | //stub: should not be shown on released software! |
22 | 24 | stub: "this page is a stub", |
23 | 25 | settings: "Settings", |
24 | - threadNew: 'New Thread', | |
26 | + threadNew: { | |
27 | + pageTitle: 'New Thread', | |
28 | + field: { | |
29 | + to: 'To', | |
30 | + subject: 'Subject' | |
31 | + } | |
32 | + | |
33 | + }, | |
25 | 34 | threadShow: "Direct Messages", |
26 | 35 | userShow: { |
27 | 36 | action: { |
28 | 37 | follow: 'Follow', |
Built with git-ssb-web