Commit fb6a6fd101e60f483142411bcb0f3bc164a3abfb
prompt to set up profile on first use, add description field (only self so far)
Matt McKegg committed on 3/16/2017, 2:07:19 AMParent: 0200c1c158b288014cfe99b3917705830f077f70
Files changed
main-window.js | ||
---|---|---|
@@ -2,8 +2,9 @@ | ||
2 | 2 | var entry = require('depject/entry') |
3 | 3 | var electron = require('electron') |
4 | 4 | var h = require('mutant/h') |
5 | 5 | var when = require('mutant/when') |
6 | +var onceTrue = require('mutant/once-true') | |
6 | 7 | var computed = require('mutant/computed') |
7 | 8 | var catchLinks = require('./lib/catch-links') |
8 | 9 | var insertCss = require('insert-css') |
9 | 10 | var nest = require('depnest') |
@@ -21,18 +22,29 @@ | ||
21 | 22 | ) |
22 | 23 | |
23 | 24 | var api = entry(sockets, nest({ |
24 | 25 | 'keys.sync.id': 'first', |
26 | + 'sbot.obs.connection': 'first', | |
25 | 27 | 'blob.sync.url': 'first', |
26 | 28 | 'page.html.render': 'first', |
27 | 29 | 'app.html.search': 'first', |
28 | 30 | 'app.views': 'first', |
29 | - 'app.html.progressNotifier': 'first' | |
31 | + 'app.html.progressNotifier': 'first', | |
32 | + 'profile.sheet.edit': 'first' | |
30 | 33 | })) |
31 | 34 | |
32 | 35 | var id = api.keys.sync.id() |
33 | 36 | var latestUpdate = LatestUpdate() |
34 | 37 | |
38 | + // prompt to setup profile on first use | |
39 | + onceTrue(api.sbot.obs.connection, (sbot) => { | |
40 | + sbot.latestSequence(sbot.id, (_, key) => { | |
41 | + if (key == null) { | |
42 | + api.profile.sheet.edit() | |
43 | + } | |
44 | + }) | |
45 | + }) | |
46 | + | |
35 | 47 | var views = api.app.views(api.page.html.render, [ |
36 | 48 | '/public', '/private', id, '/mentions' |
37 | 49 | ]) |
38 | 50 |
modules/page/html/render/profile.js | ||
---|---|---|
@@ -5,22 +5,25 @@ | ||
5 | 5 | |
6 | 6 | exports.needs = nest({ |
7 | 7 | 'about.obs': { |
8 | 8 | name: 'first', |
9 | + description: 'first', | |
9 | 10 | names: 'first', |
10 | 11 | images: 'first', |
11 | 12 | color: 'first' |
12 | 13 | }, |
13 | 14 | 'blob.sync.url': 'first', |
14 | 15 | 'blob.html.input': 'first', |
15 | 16 | 'message.async.publish': 'first', |
17 | + 'message.html.markdown': 'first', | |
16 | 18 | 'about.html.image': 'first', |
17 | 19 | 'feed.html.rollup': 'first', |
18 | 20 | 'sbot.pull.userFeed': 'first', |
19 | 21 | 'sbot.async.publish': 'first', |
20 | 22 | 'keys.sync.id': 'first', |
21 | 23 | 'sheet.display': 'first', |
22 | 24 | 'profile.obs.rank': 'first', |
25 | + 'profile.sheet.edit': 'first', | |
23 | 26 | 'contact.obs': { |
24 | 27 | followers: 'first', |
25 | 28 | following: 'first' |
26 | 29 | } |
@@ -31,8 +34,9 @@ | ||
31 | 34 | return nest('page.html.render', function profile (id) { |
32 | 35 | if (!ref.isFeed(id)) return |
33 | 36 | |
34 | 37 | var name = api.about.obs.name(id) |
38 | + var description = api.about.obs.description(id) | |
35 | 39 | var yourId = api.keys.sync.id() |
36 | 40 | var yourFollows = api.contact.obs.following(yourId) |
37 | 41 | var rawFollowers = api.contact.obs.followers(id) |
38 | 42 | var rawFollowing = api.contact.obs.following(id) |
@@ -133,9 +137,9 @@ | ||
133 | 137 | h('div.title', [ |
134 | 138 | h('h1', ['@', name]), |
135 | 139 | h('div.meta', [ |
136 | 140 | when(id === yourId, [ |
137 | - h('a.ToggleButton.-disabled', 'This is you!') | |
141 | + h('button', {'ev-click': api.profile.sheet.edit}, 'Edit Your Profile') | |
138 | 142 | ], [ |
139 | 143 | when(youFollow, |
140 | 144 | h('a.ToggleButton.-unsubscribe', { |
141 | 145 | 'href': '#', |
@@ -149,8 +153,15 @@ | ||
149 | 153 | ) |
150 | 154 | ]) |
151 | 155 | ]) |
152 | 156 | ]), |
157 | + h('section -description', [ | |
158 | + computed(description, (text) => { | |
159 | + if (typeof text === 'string') { | |
160 | + return api.message.html.markdown(text) | |
161 | + } | |
162 | + }) | |
163 | + ]), | |
153 | 164 | h('section', [ namePicker, imagePicker ]) |
154 | 165 | ]) |
155 | 166 | ]) |
156 | 167 |
modules/profile/sheet/edit.js | ||
---|---|---|
@@ -1,0 +1,142 @@ | ||
1 | +var nest = require('depnest') | |
2 | +var extend = require('xtend') | |
3 | +var {Value, h, computed, when} = require('mutant') | |
4 | +var fallbackImageUrl = 'data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' | |
5 | + | |
6 | +exports.gives = nest('profile.sheet.edit') | |
7 | + | |
8 | +exports.needs = nest({ | |
9 | + 'sheet.display': 'first', | |
10 | + 'keys.sync.id': 'first', | |
11 | + 'sbot.async.publish': 'first', | |
12 | + 'about.obs': { | |
13 | + name: 'first', | |
14 | + description: 'first', | |
15 | + image: 'first', | |
16 | + color: 'first' | |
17 | + }, | |
18 | + 'blob.html.input': 'first', | |
19 | + 'blob.sync.url': 'first' | |
20 | +}) | |
21 | + | |
22 | +exports.create = function (api) { | |
23 | + return nest('profile.sheet.edit', function () { | |
24 | + var id = api.keys.sync.id() | |
25 | + api.sheet.display(close => { | |
26 | + var currentName = api.about.obs.name(id) | |
27 | + var currentImage = api.about.obs.image(id) | |
28 | + var currentDescription = api.about.obs.description(id) | |
29 | + | |
30 | + var publishing = Value(false) | |
31 | + var chosenImage = Value(currentImage()) | |
32 | + var chosenName = Value(currentName()) | |
33 | + var chosenDescription = Value(currentDescription()) | |
34 | + | |
35 | + return { | |
36 | + content: h('div', { | |
37 | + style: { | |
38 | + padding: '20px', | |
39 | + 'text-align': 'center' | |
40 | + } | |
41 | + }, [ | |
42 | + h('h2', { | |
43 | + style: { | |
44 | + 'font-weight': 'normal' | |
45 | + } | |
46 | + }, ['Your Profile']), | |
47 | + h('ProfileEditor', [ | |
48 | + h('div.side', [ | |
49 | + h('ImageInput', [ | |
50 | + h('img', { | |
51 | + style: { 'background-color': api.about.obs.color(id) }, | |
52 | + src: computed(chosenImage, (id) => id ? api.blob.sync.url(id) : fallbackImageUrl) | |
53 | + }), | |
54 | + h('span', ['🖼 Choose Profile Image...']), | |
55 | + api.blob.html.input(file => { | |
56 | + chosenImage.set(file.link) | |
57 | + }, { | |
58 | + accept: 'image/*', | |
59 | + resize: { width: 500, height: 500 } | |
60 | + }) | |
61 | + ]) | |
62 | + ]), | |
63 | + h('div.main', [ | |
64 | + h('input.name', { | |
65 | + placeholder: 'Choose a name', | |
66 | + hooks: [ValueHook(chosenName), FocusHook()] | |
67 | + }), | |
68 | + h('textarea.description', { | |
69 | + placeholder: 'Describe yourself', | |
70 | + hooks: [ValueHook(chosenDescription)] | |
71 | + }) | |
72 | + ]) | |
73 | + ]) | |
74 | + ]), | |
75 | + footer: [ | |
76 | + h('button -save', { | |
77 | + 'ev-click': save, | |
78 | + 'disabled': publishing | |
79 | + }, when(publishing, 'Publishing...', 'Publish')), | |
80 | + h('button -cancel', { | |
81 | + 'ev-click': close | |
82 | + }, 'Cancel') | |
83 | + ] | |
84 | + } | |
85 | + | |
86 | + function save () { | |
87 | + // no confirm | |
88 | + var update = {} | |
89 | + | |
90 | + if (chosenImage() !== currentImage()) update.image = chosenImage() | |
91 | + if (chosenName() !== currentName()) update.name = chosenName() | |
92 | + if (chosenDescription() !== currentDescription()) update.description = chosenDescription() | |
93 | + | |
94 | + if (Object.keys(update).length) { | |
95 | + publishing.set(true) | |
96 | + api.sbot.async.publish(extend({ | |
97 | + type: 'about', | |
98 | + about: id | |
99 | + }, update), (err) => { | |
100 | + if (err) { | |
101 | + publishing.set(false) | |
102 | + showDialog({ | |
103 | + type: 'error', | |
104 | + title: 'Error', | |
105 | + buttons: ['OK'], | |
106 | + message: 'An error occured while attempting to publish about message.', | |
107 | + detail: err.message | |
108 | + }) | |
109 | + } else { | |
110 | + close() | |
111 | + } | |
112 | + }) | |
113 | + } else { | |
114 | + close() | |
115 | + } | |
116 | + } | |
117 | + }) | |
118 | + }) | |
119 | +} | |
120 | + | |
121 | +function FocusHook () { | |
122 | + return function (element) { | |
123 | + setTimeout(() => { | |
124 | + element.focus() | |
125 | + element.select() | |
126 | + }, 5) | |
127 | + } | |
128 | +} | |
129 | + | |
130 | +function ValueHook (obs) { | |
131 | + return function (element) { | |
132 | + element.value = obs() | |
133 | + element.oninput = function () { | |
134 | + obs.set(element.value.trim()) | |
135 | + } | |
136 | + } | |
137 | +} | |
138 | + | |
139 | +function showDialog (opts) { | |
140 | + var electron = require('electron') | |
141 | + electron.remote.dialog.showMessageBox(electron.remote.getCurrentWindow(), opts) | |
142 | +} |
plugs/message/html/render/about.js | ||
---|---|---|
@@ -5,9 +5,10 @@ | ||
5 | 5 | |
6 | 6 | exports.needs = nest({ |
7 | 7 | 'message.html': { |
8 | 8 | decorate: 'reduce', |
9 | - layout: 'first' | |
9 | + layout: 'first', | |
10 | + markdown: 'first' | |
10 | 11 | }, |
11 | 12 | 'keys.sync.id': 'first', |
12 | 13 | 'about.html.link': 'first', |
13 | 14 | 'about.obs.name': 'first', |
@@ -51,11 +52,26 @@ | ||
51 | 52 | h('img', {src: api.blob.sync.url(c.image)}) |
52 | 53 | ])) |
53 | 54 | } |
54 | 55 | |
55 | - var element = api.message.html.layout(msg, extend({ | |
56 | - content, layout: 'mini' | |
57 | - }, opts)) | |
56 | + var elements = [] | |
58 | 57 | |
59 | - return api.message.html.decorate(element, { msg }) | |
58 | + if (content.length) { | |
59 | + var element = api.message.html.layout(msg, extend({ | |
60 | + content, layout: 'mini' | |
61 | + }, opts)) | |
62 | + elements.push(api.message.html.decorate(element, { msg })) | |
63 | + } | |
64 | + | |
65 | + if (c.description) { | |
66 | + elements.push(api.message.html.decorate(api.message.html.layout(msg, extend({ | |
67 | + content: [ | |
68 | + self ? 'self assigned a description' : ['assigned a description to ', api.about.html.link(c.about)], | |
69 | + api.message.html.markdown(c.description) | |
70 | + ], | |
71 | + layout: 'mini' | |
72 | + }, opts)), { msg })) | |
73 | + } | |
74 | + | |
75 | + return elements | |
60 | 76 | }) |
61 | 77 | } |
styles/profile-header.mcss | ||
---|---|---|
@@ -22,6 +22,11 @@ | ||
22 | 22 | h1 { |
23 | 23 | flex: 1 |
24 | 24 | } |
25 | 25 | } |
26 | + section { | |
27 | + -description { | |
28 | + font-size: 120% | |
29 | + } | |
30 | + } | |
26 | 31 | } |
27 | 32 | } |
styles/image-input.mcss | ||
---|---|---|
@@ -1,0 +1,43 @@ | ||
1 | +ImageInput { | |
2 | + position: relative | |
3 | + width: 200px; | |
4 | + padding: 5px; | |
5 | + background: white; | |
6 | + box-shadow: 0 0 10px #AAA; | |
7 | + transition: box-shadow 0.2s | |
8 | + | |
9 | + img { | |
10 | + width: 100% | |
11 | + background-image: linear-gradient(172deg, rgb(247, 247, 247), rgba(0,0,0,0)) | |
12 | + } | |
13 | + input { | |
14 | + cursor: pointer | |
15 | + opacity: 0 | |
16 | + position: absolute | |
17 | + width: 100% | |
18 | + height: 100% | |
19 | + top: 0 | |
20 | + left: 0 | |
21 | + } | |
22 | + | |
23 | + span { | |
24 | + position: absolute | |
25 | + left: 0 | |
26 | + right: 0 | |
27 | + bottom: 0 | |
28 | + margin: 20px | |
29 | + background: #444 | |
30 | + padding: 4px 8px | |
31 | + border-radius: 5px | |
32 | + transition: opacity 0.2s | |
33 | + opacity: 0.5 | |
34 | + color: white | |
35 | + } | |
36 | + | |
37 | + :hover { | |
38 | + box-shadow: 0 0 10px #393939; | |
39 | + span { | |
40 | + opacity: 1 | |
41 | + } | |
42 | + } | |
43 | +} |
styles/profile-editor.mcss | ||
---|---|---|
@@ -1,0 +1,23 @@ | ||
1 | +ProfileEditor { | |
2 | + display: flex | |
3 | + div.side { | |
4 | + margin-right: 10px | |
5 | + } | |
6 | + div.main { | |
7 | + flex: 1 | |
8 | + display: flex | |
9 | + flex-direction: column | |
10 | + input { | |
11 | + border: 1px solid #CCC | |
12 | + font-size: 150% | |
13 | + padding: 10px | |
14 | + } | |
15 | + textarea { | |
16 | + margin-top: 10px | |
17 | + border: 1px solid #CCC | |
18 | + padding: 10px | |
19 | + font-size: 150% | |
20 | + flex: 1 | |
21 | + } | |
22 | + } | |
23 | +} |
Built with git-ssb-web