Commit d1fb3daa34d11265fab5f4118776a40e96d39062
blogShow basic display. refactor of Avatar.
mix irving committed on 10/18/2017, 3:32:41 AMParent: d4471ed9e0be3a61e29df8a0de9c3b36cf52e096
Files changed
about/html/avatar.js | changed |
about/html/avatar.mcss | changed |
app/html/context.js | changed |
app/html/context.mcss | changed |
app/html/thread.js | changed |
app/html/thread.mcss | changed |
app/html/blog-card.js | deleted |
app/html/blogCard.js | added |
app/html/blog-card.mcss | deleted |
app/html/blogCard.mcss | added |
app/html/comments.js | added |
app/index.js | changed |
app/page/blogIndex.js | changed |
app/page/blogShow.js | changed |
app/page/groupFind.mcss | changed |
app/page/threadNew.mcss | changed |
app/page/userFind.mcss | changed |
app/page/userShow.js | changed |
app/page/userShow.mcss | changed |
app/page/blogShow.mcss | added |
message/html/compose.js | changed |
message/html/compose.mcss | changed |
message/html/channel.js | added |
message/html/timeago.js | added |
message/html/timeago.mcss | added |
message/index.js | changed |
router/sync/routes.js | changed |
styles/button.mcss | changed |
styles/global.mcss | changed |
styles/mixins.js | changed |
about/html/avatar.js | ||
---|---|---|
@@ -1,22 +1,23 @@ | ||
1 | 1 | const nest = require('depnest') |
2 | 2 | const { h } = require('mutant') |
3 | 3 | |
4 | +exports.gives = nest('about.html.avatar') | |
5 | + | |
4 | 6 | exports.needs = nest({ |
5 | - 'about.html.image': 'first', | |
6 | - 'app.html.link': 'first' | |
7 | + 'about.obs.imageUrl': 'first', | |
8 | + 'about.obs.color': 'first', | |
9 | + 'history.sync.push': 'first' | |
7 | 10 | }) |
8 | 11 | |
9 | -exports.gives = nest('about.html.avatar') | |
10 | - | |
11 | 12 | exports.create = function (api) { |
12 | - return nest('about.html.avatar', feed => { | |
13 | - const Link = api.app.html.link | |
14 | - | |
15 | - return Link( | |
16 | - { page: 'userShow', feed }, | |
17 | - api.about.html.image(feed) | |
18 | - ) | |
19 | - | |
13 | + return nest('about.html.avatar', function (id, size = 'small') { | |
14 | + return h('img', { | |
15 | + classList: `Avatar -${size}`, | |
16 | + style: { 'background-color': api.about.obs.color(id) }, | |
17 | + src: api.about.obs.imageUrl(id), | |
18 | + title: id, | |
19 | + 'ev-click': () => api.history.sync.push({ page: 'userShow', feed: id }) | |
20 | + }) | |
20 | 21 | }) |
21 | 22 | } |
22 | 23 |
about/html/avatar.mcss | ||
---|---|---|
@@ -1,5 +1,20 @@ | ||
1 | 1 | Avatar { |
2 | - $avatarSmall | |
3 | - margin-right: .5rem | |
2 | + $circleSmall | |
3 | + | |
4 | + -tiny { | |
5 | + $circleTiny | |
6 | + } | |
7 | + | |
8 | + -small { | |
9 | + $circleSmall | |
10 | + } | |
11 | + | |
12 | + -medium { | |
13 | + $circleMedium | |
14 | + } | |
15 | + | |
16 | + -large { | |
17 | + $circleLarge | |
18 | + } | |
4 | 19 | } |
5 | 20 |
app/html/context.js | ||
---|---|---|
@@ -51,33 +51,39 @@ | ||
51 | 51 | LevelTwoContext() |
52 | 52 | ]) |
53 | 53 | |
54 | 54 | function LevelOneContext () { |
55 | - const PAGES_UNDER_DISCOVER = ['blogIndex', 'blogShow', 'home'] | |
55 | + function isDiscoverContext (loc) { | |
56 | + const PAGES_UNDER_DISCOVER = ['blogIndex', 'blogShow', 'home'] | |
56 | 57 | |
58 | + return PAGES_UNDER_DISCOVER.includes(location.page) | |
59 | + || get(location, 'value.private') === undefined | |
60 | + } | |
61 | + | |
57 | 62 | return h('div.level.-one', [ |
58 | 63 | // Nearby |
59 | 64 | computed(nearby, n => !isEmpty(n) ? h('header', strings.peopleNearby) : null), |
60 | 65 | map(nearby, feedId => Option({ |
61 | 66 | notifications: Math.random() > 0.7 ? Math.floor(Math.random()*9+1) : 0, // TODO |
62 | - imageEl: api.about.html.avatar(feedId), | |
67 | + imageEl: api.about.html.avatar(feedId, 'small'), | |
63 | 68 | label: api.about.obs.name(feedId), |
64 | 69 | selected: location.feed === feedId, |
65 | 70 | location: computed(recentPeersContacted, recent => { |
66 | 71 | const lastMsg = recent[feedId] |
67 | 72 | return lastMsg |
68 | 73 | ? Object.assign(lastMsg, { feed: feedId }) |
69 | 74 | : { page: 'threadNew', feed: feedId } |
70 | 75 | }), |
71 | - })), | |
76 | + }), { comparer: (a, b) => a === b }), | |
77 | + | |
72 | 78 | computed(nearby, n => !isEmpty(n) ? h('hr') : null), |
73 | 79 | |
74 | 80 | // Discover |
75 | 81 | Option({ |
76 | 82 | notifications: Math.floor(Math.random()*5+1), |
77 | 83 | imageEl: h('i.fa.fa-binoculars'), |
78 | 84 | label: strings.blogIndex.title, |
79 | - selected: PAGES_UNDER_DISCOVER.includes(location.page), | |
85 | + selected: isDiscoverContext(location), | |
80 | 86 | location: { page: 'blogIndex' }, |
81 | 87 | }), |
82 | 88 | |
83 | 89 | // Recent Messages |
@@ -92,9 +98,9 @@ | ||
92 | 98 | label: api.about.obs.name(feedId), |
93 | 99 | selected: location.feed === feedId, |
94 | 100 | location: Object.assign({}, lastMsg, { feed: feedId }) // TODO make obs? |
95 | 101 | }) |
96 | - }) | |
102 | + }, { comparer: (a, b) => a === b }) | |
97 | 103 | ]) |
98 | 104 | } |
99 | 105 | |
100 | 106 | function LevelTwoContext () { |
@@ -126,9 +132,9 @@ | ||
126 | 132 | label: api.message.html.subject(thread), |
127 | 133 | selected: thread.key === root, |
128 | 134 | location: Object.assign(thread, { feed: targetUser }), |
129 | 135 | }) |
130 | - }) | |
136 | + }, { comparer: (a, b) => a === b }) | |
131 | 137 | ]) |
132 | 138 | } |
133 | 139 | |
134 | 140 | function Option ({ notifications = 0, imageEl, label, location, selected }) { |
app/html/context.mcss | ||
---|---|---|
@@ -1,9 +1,9 @@ | ||
1 | 1 | Context { |
2 | 2 | flex-shrink: 0 |
3 | 3 | flex-grow: 0 |
4 | 4 | overflow: hidden |
5 | - background-color: #fff | |
5 | + $backgroundPrimaryText | |
6 | 6 | |
7 | 7 | display: flex |
8 | 8 | |
9 | 9 | div.level { |
@@ -74,9 +74,9 @@ | ||
74 | 74 | a img { |
75 | 75 | |
76 | 76 | } |
77 | 77 | i { |
78 | - $avatarSmall | |
78 | + $circleSmall | |
79 | 79 | $colorPrimary |
80 | 80 | font-size: 1.3rem |
81 | 81 | display: flex |
82 | 82 | justify-content: center |
app/html/thread.js | ||
---|---|---|
@@ -1,8 +1,9 @@ | ||
1 | 1 | const nest = require('depnest') |
2 | 2 | const { h, Array: MutantArray, map, computed, when } = require('mutant') |
3 | 3 | const get = require('lodash/get') |
4 | 4 | |
5 | +// TODO - rename threadPrivate | |
5 | 6 | exports.gives = nest('app.html.thread') |
6 | 7 | |
7 | 8 | exports.needs = nest({ |
8 | 9 | 'about.html.avatar': 'first', |
@@ -26,18 +27,18 @@ | ||
26 | 27 | const author = computed([chunk], chunk => get(chunk, '[0].value.author')) |
27 | 28 | |
28 | 29 | return author() === myId |
29 | 30 | ? h('div.my-chunk', [ |
30 | - h('div.avatar'), | |
31 | + h('Avatar -small'), | |
31 | 32 | h('div.msgs', map(chunk, msg => { |
32 | 33 | return h('div.msg-row', [ |
33 | 34 | h('div.spacer'), |
34 | 35 | message(msg) |
35 | 36 | ]) |
36 | 37 | })) |
37 | 38 | ]) |
38 | 39 | : h('div.other-chunk', [ |
39 | - h('div.avatar', when(author, api.about.html.avatar(author()))), | |
40 | + when(author, api.about.html.avatar(author()), 'small'), | |
40 | 41 | h('div.msgs', map(chunk, msg => { |
41 | 42 | return h('div.msg-row', [ |
42 | 43 | message(msg), |
43 | 44 | h('div.spacer') |
@@ -96,11 +97,4 @@ | ||
96 | 97 | // TODO (mix) use lodash/get |
97 | 98 | return msgA.value.author === msgB.value.author |
98 | 99 | } |
99 | 100 | |
100 | - | |
101 | - | |
102 | - | |
103 | - | |
104 | - | |
105 | - | |
106 | - |
app/html/thread.mcss | ||
---|---|---|
@@ -9,9 +9,9 @@ | ||
9 | 9 | $chunk |
10 | 10 | |
11 | 11 | justify-content: space-between |
12 | 12 | |
13 | - div.avatar { | |
13 | + img.Avatar { | |
14 | 14 | visibility: hidden |
15 | 15 | } |
16 | 16 | |
17 | 17 | div.msgs { |
@@ -42,16 +42,9 @@ | ||
42 | 42 | $chunk { |
43 | 43 | display: flex |
44 | 44 | margin-bottom: .5rem |
45 | 45 | |
46 | - div.avatar { | |
47 | - background-color: #333 | |
48 | - $avatarSmall | |
49 | - | |
50 | - img { | |
51 | - $avatarSmall | |
52 | - } | |
53 | - | |
46 | + img.Avatar { | |
54 | 47 | margin-right: 1rem |
55 | 48 | } |
56 | 49 | |
57 | 50 | div.msgs { |
@@ -69,9 +62,9 @@ | ||
69 | 62 | } |
70 | 63 | |
71 | 64 | div.msg { |
72 | 65 | line-height: 1.2 |
73 | - background-color: #fff | |
66 | + $backgroundPrimaryText | |
74 | 67 | padding: 0 .7rem |
75 | 68 | border-radius: 4px |
76 | 69 | } |
77 | 70 | div.spacer { |
app/html/blog-card.js | ||
---|---|---|
@@ -1,130 +1,0 @@ | ||
1 | -var nest = require('depnest') | |
2 | -var h = require('mutant/h') | |
3 | -var isString= require('lodash/isString') | |
4 | -var maxBy= require('lodash/maxBy') | |
5 | -var humanTime = require('human-time') | |
6 | -var marksum = require('markdown-summary') | |
7 | -var markdown = require('ssb-markdown') | |
8 | -var ref = require('ssb-ref') | |
9 | -var htmlEscape = require('html-escape') | |
10 | - | |
11 | -function renderEmoji (emoji, url) { | |
12 | - if (!url) return ':' + emoji + ':' | |
13 | - return ` | |
14 | - <img | |
15 | - src="${htmlEscape(url)}" | |
16 | - alt=":${htmlEscape(emoji)}:" | |
17 | - title=":${htmlEscape(emoji)}:" | |
18 | - class="emoji" | |
19 | - > | |
20 | - ` | |
21 | -} | |
22 | - | |
23 | -exports.gives = nest('app.html.blogCard', true) | |
24 | - | |
25 | -exports.needs = nest({ | |
26 | - 'keys.sync.id': 'first', | |
27 | - 'history.sync.push': 'first', | |
28 | - 'about.obs.name': 'first', | |
29 | - 'about.html.avatar': 'first', | |
30 | - 'translations.sync.strings': 'first', | |
31 | - 'unread.sync.isUnread': 'first', | |
32 | - 'message.html.markdown': 'first', | |
33 | - 'blob.sync.url': 'first', | |
34 | - 'emoji.sync.url': 'first' | |
35 | -}) | |
36 | - | |
37 | -exports.create = function (api) { | |
38 | - | |
39 | - //render markdown, but don't support patchwork@2 style mentions or custom emoji right now. | |
40 | - function render (source) { | |
41 | - return markdown.block(source, { | |
42 | - emoji: (emoji) => { | |
43 | - return renderEmoji(emoji, api.emoji.sync.url(emoji)) | |
44 | - }, | |
45 | - toUrl: (id) => { | |
46 | - if (ref.isBlob(id)) return api.blob.sync.url(id) | |
47 | - return id | |
48 | - }, | |
49 | - imageLink: (id) => id | |
50 | - }) | |
51 | - } | |
52 | - | |
53 | - | |
54 | - //render the icon for a thread. | |
55 | - //it would be more depjecty to split this | |
56 | - //into two methods, one in a private plugin | |
57 | - //one in a channel plugin | |
58 | - function threadIcon (msg) { | |
59 | - if(msg.value.private) { | |
60 | - const myId = api.keys.sync.id() | |
61 | - | |
62 | - return msg.value.content.recps | |
63 | - .map(link => isString(link) ? link : link.link) | |
64 | - .filter(link => link !== myId) | |
65 | - .map(api.about.html.avatar) | |
66 | - } | |
67 | - else if(msg.value.content.channel) | |
68 | - return '#'+msg.value.content.channel | |
69 | - } | |
70 | - | |
71 | - | |
72 | - // REFACTOR: move this to a template? | |
73 | - function buildRecipientNames (thread) { | |
74 | - const myId = api.keys.sync.id() | |
75 | - | |
76 | - return thread.value.content.recps | |
77 | - .map(link => isString(link) ? link : link.link) | |
78 | - .filter(link => link !== myId) | |
79 | - .map(api.about.obs.name) | |
80 | - } | |
81 | - | |
82 | - return nest('app.html.blogCard', (thread, opts = {}) => { | |
83 | - var strings = api.translations.sync.strings() | |
84 | - const { subject } = api.message.html | |
85 | - | |
86 | - if(!thread.value) return | |
87 | - if('string' !== typeof thread.value.content.text) return | |
88 | - | |
89 | - const lastReply = thread.replies && maxBy(thread.replies, r => r.timestamp) | |
90 | - | |
91 | - const onClick = opts.onClick || function () { api.history.sync.push(thread) } | |
92 | - const id = `${thread.key.replace(/[^a-z0-9]/gi, '')}` //-${JSON.stringify(opts)}` | |
93 | - // id is only here to help morphdom morph accurately | |
94 | - | |
95 | - const { content, author, timestamp } = thread.value | |
96 | - | |
97 | - var img = h('Thumbnail') | |
98 | - var m = /\!\[[^]+\]\(([^\)]+)\)/.exec(marksum.image(content.text)) | |
99 | - if(m) { | |
100 | - //Hey this works! fit an image into a specific size (see thread-card.mcss) | |
101 | - //centered, and scaled to fit the square (works with both landscape and portrait!) | |
102 | - //This is functional css not opinionated css, so all embedded. | |
103 | - img.style = 'background-image: url("'+api.blob.sync.url(m[1])+'"); background-position:center; background-size: cover;' | |
104 | - } | |
105 | - | |
106 | - const title = render(marksum.title(content.text)) | |
107 | - const summary = render(marksum.summary(content.text)) | |
108 | - | |
109 | - const className = thread.unread ? '-unread': '' | |
110 | - | |
111 | - return h('BlogCard', { id, className }, [ | |
112 | - h('div.context', [ | |
113 | - api.about.html.avatar(author), | |
114 | - h('div.name', api.about.obs.name(author)), | |
115 | - h('div.timeago', humanTime(new Date(timestamp))), | |
116 | - ]), | |
117 | - h('div.content', {'ev-click': onClick}, [ | |
118 | - img, | |
119 | - h('div.text', [ | |
120 | - h('h2', {innerHTML: title}), | |
121 | - content.channel | |
122 | - ? h('Button -channel', '#'+content.channel) | |
123 | - : '', | |
124 | - h('div.summary', {innerHTML: summary}) | |
125 | - ]) | |
126 | - ]) | |
127 | - ]) | |
128 | - }) | |
129 | -} | |
130 | - |
app/html/blogCard.js | ||
---|---|---|
@@ -1,0 +1,130 @@ | ||
1 | +var nest = require('depnest') | |
2 | +var h = require('mutant/h') | |
3 | +var isString= require('lodash/isString') | |
4 | +var maxBy= require('lodash/maxBy') | |
5 | +var marksum = require('markdown-summary') | |
6 | +var markdown = require('ssb-markdown') | |
7 | +var ref = require('ssb-ref') | |
8 | +var htmlEscape = require('html-escape') | |
9 | + | |
10 | +function renderEmoji (emoji, url) { | |
11 | + if (!url) return ':' + emoji + ':' | |
12 | + return ` | |
13 | + <img | |
14 | + src="${htmlEscape(url)}" | |
15 | + alt=":${htmlEscape(emoji)}:" | |
16 | + title=":${htmlEscape(emoji)}:" | |
17 | + class="emoji" | |
18 | + > | |
19 | + ` | |
20 | +} | |
21 | + | |
22 | +exports.gives = nest('app.html.blogCard', true) | |
23 | + | |
24 | +exports.needs = nest({ | |
25 | + 'keys.sync.id': 'first', | |
26 | + 'history.sync.push': 'first', | |
27 | + 'about.obs.name': 'first', | |
28 | + 'about.html.avatar': 'first', | |
29 | + 'translations.sync.strings': 'first', | |
30 | + 'unread.sync.isUnread': 'first', | |
31 | + // 'message.html.markdown': 'first', | |
32 | + 'message.html.timeago': 'first', | |
33 | + 'blob.sync.url': 'first', | |
34 | + 'emoji.sync.url': 'first' | |
35 | +}) | |
36 | + | |
37 | +exports.create = function (api) { | |
38 | + | |
39 | + //render markdown, but don't support patchwork@2 style mentions or custom emoji right now. | |
40 | + function render (source) { | |
41 | + return markdown.block(source, { | |
42 | + emoji: (emoji) => { | |
43 | + return renderEmoji(emoji, api.emoji.sync.url(emoji)) | |
44 | + }, | |
45 | + toUrl: (id) => { | |
46 | + if (ref.isBlob(id)) return api.blob.sync.url(id) | |
47 | + return id | |
48 | + }, | |
49 | + imageLink: (id) => id | |
50 | + }) | |
51 | + } | |
52 | + | |
53 | + | |
54 | + //render the icon for a blog. | |
55 | + //it would be more depjecty to split this | |
56 | + //into two methods, one in a private plugin | |
57 | + //one in a channel plugin | |
58 | + function blogIcon (msg) { | |
59 | + if(msg.value.private) { | |
60 | + const myId = api.keys.sync.id() | |
61 | + | |
62 | + return msg.value.content.recps | |
63 | + .map(link => isString(link) ? link : link.link) | |
64 | + .filter(link => link !== myId) | |
65 | + .map(link => api.about.html.avatar) | |
66 | + } | |
67 | + else if(msg.value.content.channel) | |
68 | + return '#'+msg.value.content.channel | |
69 | + } | |
70 | + | |
71 | + | |
72 | + // REFACTOR: move this to a template? | |
73 | + function buildRecipientNames (blog) { | |
74 | + const myId = api.keys.sync.id() | |
75 | + | |
76 | + return blog.value.content.recps | |
77 | + .map(link => isString(link) ? link : link.link) | |
78 | + .filter(link => link !== myId) | |
79 | + .map(api.about.obs.name) | |
80 | + } | |
81 | + | |
82 | + return nest('app.html.blogCard', (blog, opts = {}) => { | |
83 | + var strings = api.translations.sync.strings() | |
84 | + | |
85 | + if(!blog.value) return | |
86 | + if('string' !== typeof blog.value.content.text) return | |
87 | + | |
88 | + const lastReply = blog.replies && maxBy(blog.replies, r => r.timestamp) | |
89 | + | |
90 | + const goToBlog = () => api.history.sync.push(blog) | |
91 | + const onClick = opts.onClick || goToBlog | |
92 | + const id = `${blog.key.replace(/[^a-z0-9]/gi, '')}` //-${JSON.stringify(opts)}` | |
93 | + // id is only here to help morphdom morph accurately | |
94 | + | |
95 | + const { content, author } = blog.value | |
96 | + | |
97 | + var img = h('Thumbnail') | |
98 | + var m = /\!\[[^]+\]\(([^\)]+)\)/.exec(marksum.image(content.text)) | |
99 | + if(m) { | |
100 | + //Hey this works! fit an image into a specific size (see blog-card.mcss) | |
101 | + //centered, and scaled to fit the square (works with both landscape and portrait!) | |
102 | + //This is functional css not opinionated css, so all embedded. | |
103 | + img.style = 'background-image: url("'+api.blob.sync.url(m[1])+'"); background-position:center; background-size: cover;' | |
104 | + } | |
105 | + | |
106 | + const title = render(marksum.title(content.text)) | |
107 | + const summary = render(marksum.summary(content.text)) | |
108 | + | |
109 | + const className = blog.unread ? '-unread': '' | |
110 | + | |
111 | + return h('BlogCard', { id, className }, [ | |
112 | + h('div.context', [ | |
113 | + api.about.html.avatar(author, 'tiny'), | |
114 | + h('div.name', api.about.obs.name(author)), | |
115 | + api.message.html.timeago(blog) | |
116 | + ]), | |
117 | + h('div.content', {'ev-click': onClick}, [ | |
118 | + img, | |
119 | + h('div.text', [ | |
120 | + h('h2', {innerHTML: title}), | |
121 | + content.channel | |
122 | + ? h('Button -channel', '#'+content.channel) | |
123 | + : '', | |
124 | + h('div.summary', {innerHTML: summary}) | |
125 | + ]) | |
126 | + ]) | |
127 | + ]) | |
128 | + }) | |
129 | +} | |
130 | + |
app/html/blog-card.mcss | ||
---|---|---|
@@ -1,84 +1,0 @@ | ||
1 | -BlogCard { | |
2 | - padding: 1rem | |
3 | - background-color: #fff | |
4 | - | |
5 | - border: 1px solid #fff | |
6 | - transition: all .5s ease | |
7 | - | |
8 | - :hover { | |
9 | - border: 1px solid gainsboro | |
10 | - box-shadow: gainsboro 2px 2px 10px | |
11 | - } | |
12 | - | |
13 | - display: flex | |
14 | - flex-direction: column | |
15 | - | |
16 | - div.context { | |
17 | - font-size: .8rem | |
18 | - margin-bottom: 1rem | |
19 | - | |
20 | - display: flex | |
21 | - align-items: center | |
22 | - | |
23 | - div.Link { | |
24 | - height: 2rem | |
25 | - img.Avatar { | |
26 | - width: 2rem | |
27 | - height: 2rem | |
28 | - } | |
29 | - } | |
30 | - | |
31 | - div.name { | |
32 | - margin-right: 1rem | |
33 | - } | |
34 | - div.timeago { | |
35 | - $colorSubtle | |
36 | - } | |
37 | - } | |
38 | - | |
39 | - div.content { | |
40 | - display: flex | |
41 | - flex-direction: row | |
42 | - flex-grow: 1 | |
43 | - | |
44 | - cursor: pointer | |
45 | - | |
46 | - | |
47 | - div.Thumbnail { | |
48 | - margin-right: 1rem | |
49 | - } | |
50 | - | |
51 | - div.text { | |
52 | - display: flex | |
53 | - flex-wrap: wrap | |
54 | - | |
55 | - h2 { | |
56 | - $markdownLarge | |
57 | - margin: 0 .5rem 0 0 | |
58 | - } | |
59 | - div.Button.-channel {} | |
60 | - div.summary { | |
61 | - flex-basis: 100% | |
62 | - } | |
63 | - } | |
64 | - } | |
65 | - | |
66 | - -unread { | |
67 | - div.content { | |
68 | - background-color: #fff | |
69 | - | |
70 | - div.subject { | |
71 | - $markdownBold | |
72 | - } | |
73 | - } | |
74 | - } | |
75 | -} | |
76 | - | |
77 | -Thumbnail { | |
78 | - border-radius: .5rem | |
79 | - min-width: 8rem | |
80 | - min-height: 6rem | |
81 | - width: 8rem | |
82 | - height: 6rem | |
83 | -} | |
84 | - |
app/html/blogCard.mcss | ||
---|---|---|
@@ -1,0 +1,70 @@ | ||
1 | +BlogCard { | |
2 | + padding: 1rem | |
3 | + $backgroundPrimaryText | |
4 | + | |
5 | + border: 1px solid #fff | |
6 | + transition: all .5s ease | |
7 | + | |
8 | + :hover { | |
9 | + border: 1px solid gainsboro | |
10 | + box-shadow: gainsboro 2px 2px 10px | |
11 | + } | |
12 | + | |
13 | + display: flex | |
14 | + flex-direction: column | |
15 | + | |
16 | + div.context { | |
17 | + font-size: .8rem | |
18 | + margin-bottom: 1rem | |
19 | + | |
20 | + display: flex | |
21 | + align-items: center | |
22 | + | |
23 | + img.Avatar { | |
24 | + margin-right: .5rem | |
25 | + } | |
26 | + | |
27 | + div.name { | |
28 | + margin-right: 1rem | |
29 | + } | |
30 | + | |
31 | + div.Timeago {} | |
32 | + } | |
33 | + | |
34 | + div.content { | |
35 | + display: flex | |
36 | + flex-direction: row | |
37 | + flex-grow: 1 | |
38 | + | |
39 | + cursor: pointer | |
40 | + | |
41 | + | |
42 | + div.Thumbnail { | |
43 | + margin-right: 1rem | |
44 | + } | |
45 | + | |
46 | + div.text { | |
47 | + display: flex | |
48 | + flex-wrap: wrap | |
49 | + | |
50 | + h2 { | |
51 | + $markdownLarge | |
52 | + margin: 0 .5rem 0 0 | |
53 | + } | |
54 | + div.Button.-channel {} | |
55 | + div.summary { | |
56 | + flex-basis: 100% | |
57 | + } | |
58 | + } | |
59 | + } | |
60 | + | |
61 | +} | |
62 | + | |
63 | +Thumbnail { | |
64 | + border-radius: .5rem | |
65 | + min-width: 8rem | |
66 | + min-height: 6rem | |
67 | + width: 8rem | |
68 | + height: 6rem | |
69 | +} | |
70 | + |
app/html/comments.js | ||
---|---|---|
@@ -1,0 +1,59 @@ | ||
1 | +const nest = require('depnest') | |
2 | +const { h, Array: MutantArray, map, computed, when, resolve } = require('mutant') | |
3 | +const get = require('lodash/get') | |
4 | + | |
5 | +exports.gives = nest('app.html.comments') | |
6 | + | |
7 | +exports.needs = nest({ | |
8 | + 'about.html.avatar': 'first', | |
9 | + 'about.obs.name': 'first', | |
10 | + 'feed.obs.thread': 'first', | |
11 | + 'keys.sync.id': 'first', | |
12 | + 'message.html.markdown': 'first', | |
13 | + 'unread.sync.markRead': 'first', | |
14 | + 'unread.sync.isUnread': 'first' | |
15 | +}) | |
16 | + | |
17 | +exports.create = (api) => { | |
18 | + return nest('app.html.comments', comments) | |
19 | + | |
20 | + function comments (root) { | |
21 | + const myId = api.keys.sync.id() | |
22 | + const { messages } = api.feed.obs.thread(root) | |
23 | + | |
24 | + return h('Comments', | |
25 | + map(messages, Comment) | |
26 | + ) | |
27 | + | |
28 | + function Comment (msgObs) { | |
29 | + const msg = resolve(msgObs) | |
30 | + const raw = get(msg, 'value.content.text') | |
31 | + var className = api.unread.sync.isUnread(msg) ? ' -unread' : ' -read' | |
32 | + api.unread.sync.markRead(msg) | |
33 | + | |
34 | + if (!get(msg, 'value.content.root')) return | |
35 | + | |
36 | + const { author } = msg.value | |
37 | + return h('Comment', { className }, [ | |
38 | + h('div.left', api.about.html.avatar(author)), | |
39 | + h('div.right', [ | |
40 | + h('section.context', [ | |
41 | + h('div.name', api.about.obs.name(author)), | |
42 | + h('div.timeago', '3 hours ago'), //TODO | |
43 | + ]), | |
44 | + h('section.content', api.message.html.markdown(raw)), | |
45 | + h('section.actions', [ | |
46 | + h('div.reply', [ | |
47 | + h('i.fa.fa-commenting-o'), | |
48 | + ]), | |
49 | + h('div.like', [ | |
50 | + Math.random() > 0.5 ? h('i.fa.fa-heart') : h('i.fa.fa-heart-o'), // TODO -obs I like | |
51 | + 3 // TODO -obs like count | |
52 | + ]) | |
53 | + ]) | |
54 | + ]) | |
55 | + ]) | |
56 | + } | |
57 | + } | |
58 | +} | |
59 | + |
app/index.js | ||
---|---|---|
@@ -3,13 +3,14 @@ | ||
3 | 3 | catchLinkClick: require('./async/catch-link-click'), |
4 | 4 | }, |
5 | 5 | html: { |
6 | 6 | app: require('./html/app'), |
7 | + comments: require('./html/comments'), | |
7 | 8 | context: require('./html/context'), |
8 | 9 | header: require('./html/header'), |
9 | 10 | thread: require('./html/thread'), |
10 | 11 | link: require('./html/link'), |
11 | - blogCard: require('./html/blog-card'), | |
12 | + blogCard: require('./html/blogCard'), | |
12 | 13 | }, |
13 | 14 | page: { |
14 | 15 | blogIndex: require('./page/blogIndex'), |
15 | 16 | blogNew: require('./page/blogNew'), |
app/page/blogIndex.js | ||
---|---|---|
@@ -98,9 +98,9 @@ | ||
98 | 98 | groupedThreads.map(thread => { |
99 | 99 | const { recps, channel } = thread.value.content |
100 | 100 | var onClick |
101 | 101 | if (channel && !recps) |
102 | - onClick = (ev) => api.history.sync.push({ key: thread.key, page: 'blogShow' }) | |
102 | + onClick = (ev) => api.history.sync.push(Object.assign({}, thread, { page: 'blogShow' })) | |
103 | 103 | |
104 | 104 | return api.app.html.blogCard(thread, { onClick }) |
105 | 105 | }) |
106 | 106 | ) |
app/page/blogShow.js | ||
---|---|---|
@@ -1,102 +1,69 @@ | ||
1 | 1 | const nest = require('depnest') |
2 | -const { h, Array: MutantArray, computed, when, map } = require('mutant') | |
3 | -const pull = require('pull-stream') | |
2 | +const { h, computed, when } = require('mutant') | |
3 | +const { title: getTitle } = require('markdown-summary') | |
4 | +const last = require('lodash/last') | |
4 | 5 | const get = require('lodash/get') |
5 | 6 | |
6 | -exports.gives = nest('app.page.userShow') | |
7 | +exports.gives = nest('app.page.blogShow') | |
7 | 8 | |
8 | 9 | exports.needs = nest({ |
9 | - 'about.html.image': 'first', | |
10 | + 'about.html.avatar': 'first', | |
10 | 11 | 'about.obs.name': 'first', |
11 | - 'app.html.link': 'first', | |
12 | - 'app.html.blogCard': 'first', | |
13 | - 'contact.async.follow': 'first', | |
14 | - 'contact.async.unfollow': 'first', | |
15 | - 'contact.obs.followers': 'first', | |
16 | - 'feed.pull.private': 'first', | |
17 | - 'feed.pull.rollup': 'first', | |
18 | - 'keys.sync.id': 'first', | |
19 | - 'state.obs.threads': 'first', | |
20 | - 'translations.sync.strings': 'first', | |
12 | + 'app.html.comments': 'first', | |
13 | + 'app.html.context': 'first', | |
14 | + 'message.html.channel': 'first', | |
15 | + 'message.html.compose': 'first', | |
16 | + 'message.html.markdown': 'first', | |
17 | + 'message.html.timeago': 'first', | |
21 | 18 | }) |
22 | 19 | |
23 | 20 | exports.create = (api) => { |
24 | - return nest('app.page.userShow', userShow) | |
21 | + return nest('app.page.blogShow', blogShow) | |
25 | 22 | |
26 | - function userShow (location) { | |
23 | + function blogShow (blogMsg) { | |
24 | + // blogMsg = a thread (message, may be decorated with replies) | |
27 | 25 | |
28 | - const { feed } = location | |
29 | - const myId = api.keys.sync.id() | |
30 | - const name = api.about.obs.name(feed) | |
26 | + const { author, content } = blogMsg.value | |
31 | 27 | |
32 | - const strings = api.translations.sync.strings() | |
28 | + const blog = content.text | |
29 | + const title = content.title || getTitle(blog) | |
33 | 30 | |
34 | - const { followers } = api.contact.obs | |
31 | + const comments = api.app.html.comments(blogMsg.key) | |
35 | 32 | |
36 | - const youFollowThem = computed(followers(feed), followers => followers.includes(myId)) | |
37 | - // const theyFollowYou = computed(followers(myId), followers => followers.includes(feed)) | |
38 | - // const youAreFriends = computed([youFollowThem, theyFollowYou], (a, b) => a && b) | |
33 | + const meta = { | |
34 | + type: 'post', | |
35 | + root: blogMsg.key, | |
36 | + // branch: get(last(blogMsg.replies), 'key'), // TODO - change to match new comments logic | |
37 | + // >> lastId? CHECK THIS LOGIC | |
38 | + channel: content.channel | |
39 | + } | |
39 | 40 | |
40 | - // const ourRelationship = computed( | |
41 | - // [youAreFriends, youFollowThem, theyFollowYou], | |
42 | - // (youAreFriends, youFollowThem, theyFollowYou) => { | |
43 | - // if (youAreFriends) return strings.userShow.state.friends | |
44 | - // if (theyFollowYou) return strings.userShow.state.theyFollow | |
45 | - // if (youFollowThem) return strings.userShow.state.youFollow | |
46 | - // } | |
47 | - // ) | |
48 | - const { unfollow, follow } = api.contact.async | |
49 | - const followButton = when(followers(myId).sync, | |
50 | - when(youFollowThem, | |
51 | - h('Button -primary', { 'ev-click': () => unfollow(feed) }, strings.userShow.action.unfollow), | |
52 | - h('Button -primary', { 'ev-click': () => follow(feed) }, strings.userShow.action.follow) | |
53 | - ), | |
54 | - h('Button', { disabled: 'disabled' }, strings.loading ) | |
55 | - ) | |
41 | + const { timeago, channel, markdown, compose } = api.message.html | |
42 | + const composer = compose({ meta, shrink: true }) | |
56 | 43 | |
57 | - const Link = api.app.html.link | |
58 | - const userEditButton = Link({ page: 'userEdit', feed }, h('i.fa.fa-pencil')) | |
59 | - const directMessageButton = Link({ page: 'threadNew', feed }, h('Button', strings.userShow.action.directMessage)) | |
60 | - | |
61 | - const threads = MutantArray() | |
62 | - pull( | |
63 | - // next(api.feed.pull.private, {reverse: true, limit: 100, live: false}, ['value', 'timestamp']), | |
64 | - // api.feed.pull.private({reverse: true, limit: 100, live: false}), | |
65 | - api.feed.pull.private({reverse: true, live: false}), | |
66 | - pull.filter(msg => { | |
67 | - const recps = get(msg, 'value.content.recps') | |
68 | - if (!recps) return | |
69 | - | |
70 | - return recps | |
71 | - .map(r => typeof r === 'object' ? r.link : r) | |
72 | - .includes(feed) | |
73 | - }), | |
74 | - api.feed.pull.rollup(), | |
75 | - pull.drain(threads.push) | |
76 | - // Scroller(content, scrollerContent, render, false, false) | |
77 | - ) | |
78 | - | |
79 | - return h('Page -userShow', {title: name}, [ | |
44 | + return h('Page -blogShow', [ | |
45 | + api.app.html.context({ page: 'discover' }), // HACK to highlight discover | |
80 | 46 | h('div.content', [ |
81 | - h('section.about', [ | |
82 | - api.about.html.image(feed), | |
83 | - h('h1', [ | |
84 | - name, | |
85 | - feed === myId // Only expose own profile editing right now | |
86 | - ? userEditButton | |
87 | - : '' | |
47 | + h('header', [ | |
48 | + h('div.blog', [ | |
49 | + h('h1', title), | |
50 | + timeago(blogMsg), | |
51 | + channel(blogMsg) | |
88 | 52 | ]), |
89 | - feed !== myId | |
90 | - ? h('div.actions', [ | |
91 | - h('div.friendship', followButton), | |
92 | - h('div.directMessage', directMessageButton) | |
93 | - ]) | |
94 | - : '', | |
53 | + h('div.author', [ | |
54 | + h('div.leftCol', api.about.html.avatar(author, 'medium')), | |
55 | + h('div.rightCol', [ | |
56 | + h('div.name', api.about.obs.name(author)), | |
57 | + h('Button', 'Follow') | |
58 | + ]), | |
59 | + ]) | |
95 | 60 | ]), |
96 | - h('section.blogs', map(threads, api.app.html.blogCard)) | |
97 | - ]) | |
61 | + h('div.break', h('hr')), | |
62 | + h('section.blog', markdown(blog)), | |
63 | + composer, | |
64 | + comments, | |
65 | + ]), | |
98 | 66 | ]) |
99 | 67 | } |
100 | 68 | } |
101 | 69 | |
102 | - |
app/page/groupFind.mcss | ||
---|---|---|
@@ -1,9 +1,9 @@ | ||
1 | 1 | Page -groupFind { |
2 | 2 | div.content { |
3 | 3 | $maxWidthSmaller |
4 | 4 | div.search { |
5 | - background-color: #fff | |
5 | + $backgroundPrimaryText | |
6 | 6 | |
7 | 7 | margin-bottom: 1rem |
8 | 8 | |
9 | 9 | display: flex |
@@ -29,17 +29,17 @@ | ||
29 | 29 | $maxWidthSmaller |
30 | 30 | |
31 | 31 | div.Link { |
32 | 32 | div.result { |
33 | - background-color: #fff | |
33 | + $backgroundPrimaryText | |
34 | 34 | |
35 | 35 | padding: .5rem |
36 | 36 | |
37 | 37 | display: flex |
38 | 38 | align-items: center |
39 | 39 | |
40 | 40 | img { |
41 | - $avatarSmall | |
41 | + $circleSmall | |
42 | 42 | margin-right: 1rem |
43 | 43 | } |
44 | 44 | |
45 | 45 | div.alias { |
app/page/threadNew.mcss | ||
---|---|---|
@@ -33,9 +33,9 @@ | ||
33 | 33 | padding: .3rem |
34 | 34 | min-width: 5rem |
35 | 35 | $borderSubtle |
36 | 36 | border-radius: 6rem |
37 | - background-color: #fff | |
37 | + $backgroundPrimaryText | |
38 | 38 | |
39 | 39 | margin-right: 1rem |
40 | 40 | |
41 | 41 | display: flex |
app/page/userFind.mcss | ||
---|---|---|
@@ -1,9 +1,9 @@ | ||
1 | 1 | Page -userFind { |
2 | 2 | div.content { |
3 | 3 | $maxWidthSmaller |
4 | 4 | div.search { |
5 | - background-color: #fff | |
5 | + $backgroundPrimaryText | |
6 | 6 | |
7 | 7 | margin-bottom: 1rem |
8 | 8 | |
9 | 9 | display: flex |
@@ -29,17 +29,17 @@ | ||
29 | 29 | $maxWidthSmaller |
30 | 30 | |
31 | 31 | div.Link { |
32 | 32 | div.result { |
33 | - background-color: #fff | |
33 | + $backgroundPrimaryText | |
34 | 34 | |
35 | 35 | padding: .5rem |
36 | 36 | |
37 | 37 | display: flex |
38 | 38 | align-items: center |
39 | 39 | |
40 | 40 | img { |
41 | - $avatarSmall | |
41 | + $circleSmall | |
42 | 42 | margin-right: 1rem |
43 | 43 | } |
44 | 44 | |
45 | 45 | div.alias { |
app/page/userShow.js | ||
---|---|---|
@@ -5,9 +5,9 @@ | ||
5 | 5 | |
6 | 6 | exports.gives = nest('app.page.userShow') |
7 | 7 | |
8 | 8 | exports.needs = nest({ |
9 | - 'about.html.image': 'first', | |
9 | + 'about.html.avatar': 'first', | |
10 | 10 | 'about.obs.name': 'first', |
11 | 11 | 'app.html.link': 'first', |
12 | 12 | 'app.html.blogCard': 'first', |
13 | 13 | 'contact.async.follow': 'first', |
@@ -78,9 +78,9 @@ | ||
78 | 78 | |
79 | 79 | return h('Page -userShow', {title: name}, [ |
80 | 80 | h('div.content', [ |
81 | 81 | h('section.about', [ |
82 | - api.about.html.image(feed), | |
82 | + api.about.html.avatar(feed, 'large'), | |
83 | 83 | h('h1', [ |
84 | 84 | name, |
85 | 85 | feed === myId // Only expose own profile editing right now |
86 | 86 | ? userEditButton |
app/page/userShow.mcss | ||
---|---|---|
@@ -8,11 +8,8 @@ | ||
8 | 8 | flex-direction: column |
9 | 9 | align-items: center |
10 | 10 | |
11 | 11 | img.Avatar { |
12 | - width: 5rem | |
13 | - height: 5rem | |
14 | - border-radius: 3rem | |
15 | 12 | } |
16 | 13 | |
17 | 14 | h1 { |
18 | 15 | font-weight: 300 |
app/page/blogShow.mcss | ||
---|---|---|
@@ -1,0 +1,82 @@ | ||
1 | +Page -blogShow { | |
2 | + // div.context {} | |
3 | + | |
4 | + div.content { | |
5 | + header { | |
6 | + $backgroundPrimaryText | |
7 | + padding: 1rem | |
8 | + | |
9 | + display: flex | |
10 | + | |
11 | + div.blog { | |
12 | + display: flex | |
13 | + flex-wrap: wrap | |
14 | + flex-grow: 1 | |
15 | + | |
16 | + h1 { | |
17 | + flex-basis: 100% | |
18 | + | |
19 | + font-weight: 300 | |
20 | + margin: 0 0 1rem 0 | |
21 | + } | |
22 | + | |
23 | + div.Timeago { | |
24 | + flex-basis: 100% | |
25 | + margin-bottom: .6rem | |
26 | + } | |
27 | + | |
28 | + div.Button.-channel {} | |
29 | + } | |
30 | + | |
31 | + div.author { | |
32 | + display: flex | |
33 | + | |
34 | + div.leftCol { | |
35 | + margin-right: 1rem | |
36 | + img.Avatar {} | |
37 | + } | |
38 | + | |
39 | + div.rightCol { | |
40 | + div.name { | |
41 | + font-size: .9rem | |
42 | + margin-bottom: .5rem | |
43 | + } | |
44 | + div.Button.-follow {} // extract | |
45 | + } | |
46 | + } | |
47 | + } | |
48 | + | |
49 | + div.break { | |
50 | + padding: 0 1rem | |
51 | + $backgroundPrimaryText | |
52 | + | |
53 | + hr { | |
54 | + margin: 0 | |
55 | + border: none | |
56 | + border-bottom: 1px solid gainsboro | |
57 | + } | |
58 | + } | |
59 | + | |
60 | + section.blog { | |
61 | + $backgroundPrimaryText | |
62 | + padding: 1rem | |
63 | + | |
64 | + margin-bottom: 1.5rem | |
65 | + } | |
66 | + | |
67 | + div.Compose { | |
68 | + margin-bottom: 1.5rem | |
69 | + | |
70 | + section.actions { | |
71 | + input { display: none } | |
72 | + | |
73 | + div.Button { | |
74 | + -preview { display: none } | |
75 | + } | |
76 | + } | |
77 | + } | |
78 | + | |
79 | + div.Comments {} | |
80 | + } | |
81 | +} | |
82 | + |
message/html/compose.js | ||
---|---|---|
@@ -83,9 +83,9 @@ | ||
83 | 83 | else |
84 | 84 | fileInput = h('input', { style: {visibility: 'hidden'}}) |
85 | 85 | |
86 | 86 | var showPreview = Value(false) |
87 | - var previewBtn = h('Button', | |
87 | + var previewBtn = h('Button -preview', | |
88 | 88 | { |
89 | 89 | className: when(showPreview, '-primary'), |
90 | 90 | 'ev-click': () => showPreview.set(!showPreview()) |
91 | 91 | }, |
message/html/compose.mcss | ||
---|---|---|
@@ -6,36 +6,23 @@ | ||
6 | 6 | |
7 | 7 | textarea { |
8 | 8 | $fontBasic |
9 | 9 | |
10 | - padding: .6rem | |
11 | - $borderSubtle | |
12 | - border-top-left-radius: 0 | |
13 | - border-top-right-radius: 0 | |
14 | - } | |
10 | + padding: 1rem | |
11 | + border-radius: 1rem | |
12 | + border: none | |
13 | + margin-bottom: .5rem | |
15 | 14 | |
16 | - input.channel { | |
17 | - $borderSubtle | |
18 | - border-bottom: none | |
19 | - border-bottom-left-radius: 0 | |
20 | - border-bottom-right-radius: 0 | |
21 | - padding: .5rem | |
22 | - | |
23 | 15 | :focus { |
24 | 16 | outline: none |
25 | - box-shadow: none | |
26 | 17 | } |
27 | - :disabled { | |
28 | - background-color: #f1f1f1 | |
29 | - cursor: not-allowed | |
30 | - } | |
31 | 18 | } |
32 | 19 | |
33 | 20 | section.actions { |
34 | 21 | display: flex |
35 | 22 | flex-direction: row |
36 | 23 | align-items: baseline |
37 | - justify-content: space-between | |
24 | + justify-content: flex-end | |
38 | 25 | |
39 | 26 | margin-top: .4rem |
40 | 27 | |
41 | 28 | input { flex-grow: 1 } |
message/html/channel.js | ||
---|---|---|
@@ -1,0 +1,24 @@ | ||
1 | +const nest = require('depnest') | |
2 | +const { h } = require('mutant') | |
3 | + | |
4 | +exports.gives = nest('message.html.channel') | |
5 | + | |
6 | +exports.needs = nest({ | |
7 | + 'history.sync.push': 'first' | |
8 | +}) | |
9 | + | |
10 | +exports.create = function (api) { | |
11 | + return nest('message.html.channel', channel) | |
12 | + | |
13 | + function channel (msg) { | |
14 | + const { channel } = msg.value.content | |
15 | + | |
16 | + if (!channel) return | |
17 | + | |
18 | + return h('Button -channel', { | |
19 | + // 'ev-click': () => history.sync.push({ page: 'channelIndex', channel }) // TODO | |
20 | + }, channel) | |
21 | + } | |
22 | +} | |
23 | + | |
24 | + |
message/html/timeago.js | ||
---|---|---|
@@ -1,0 +1,18 @@ | ||
1 | +const nest = require('depnest') | |
2 | +const { h } = require('mutant') | |
3 | +const humanTime = require('human-time') | |
4 | + | |
5 | +exports.gives = nest('message.html.timeago') | |
6 | + | |
7 | +exports.create = function (api) { | |
8 | + return nest('message.html.timeago', timeago) | |
9 | + | |
10 | + function timeago (msg) { | |
11 | + const { timestamp } = msg.value | |
12 | + | |
13 | + // TODO implement the light auto-updating of this app-wide | |
14 | + // perhaps by adding an initializer which sweeps for data-timestamp elements and updates them | |
15 | + return h('Timeago', humanTime(new Date(timestamp))) | |
16 | + } | |
17 | +} | |
18 | + |
message/index.js | ||
---|---|---|
@@ -2,9 +2,11 @@ | ||
2 | 2 | async: { |
3 | 3 | publish: require('./async/publish'), |
4 | 4 | }, |
5 | 5 | html: { |
6 | + channel: require('./html/channel'), | |
6 | 7 | compose: require('./html/compose'), |
7 | - subject: require('./html/subject') | |
8 | + subject: require('./html/subject'), | |
9 | + timeago: require('./html/timeago') | |
8 | 10 | } |
9 | 11 | } |
10 | 12 |
router/sync/routes.js | ||
---|---|---|
@@ -8,8 +8,9 @@ | ||
8 | 8 | exports.needs = nest({ |
9 | 9 | 'app.page.error': 'first', |
10 | 10 | 'app.page.blogIndex': 'first', |
11 | 11 | 'app.page.blogNew': 'first', |
12 | + 'app.page.blogShow': 'first', | |
12 | 13 | 'app.page.settings': 'first', |
13 | 14 | // 'app.page.channel': 'first', |
14 | 15 | // 'app.page.groupFind': 'first', |
15 | 16 | // 'app.page.groupIndex': 'first', |
@@ -30,9 +31,22 @@ | ||
30 | 31 | // route format: [ routeValidator, routeFunction ] |
31 | 32 | |
32 | 33 | const routes = [ |
33 | 34 | |
34 | - // Thread pages | |
35 | + // Blog pages | |
36 | + [ location => location.page === 'home', pages.blogIndex ], | |
37 | + [ location => location.page === 'discovery', pages.blogIndex ], | |
38 | + [ location => location.page === 'blogIndex', pages.blogIndex ], | |
39 | + [ location => location.page === 'blogNew', pages.blogNew ], | |
40 | + [ location => location.page === 'blogShow', pages.blogShow ], | |
41 | + [ location => isMsg(location.key) && get(location, 'value.content.type') === 'blog', pages.blogShow ], | |
42 | + [ location => { | |
43 | + return isMsg(location.key) | |
44 | + && get(location, 'value.content.type') === 'post' | |
45 | + && !get(location, 'value.private') // treats public posts as 'blogs' | |
46 | + }, pages.blogShow ], | |
47 | + | |
48 | + // Private Thread pages | |
35 | 49 | // [ location => location.page === 'threadNew' && location.channel, pages.threadNew ], |
36 | 50 | [ location => location.page === 'threadNew' && isFeed(location.feed), pages.threadNew ], |
37 | 51 | [ location => isMsg(location.key), pages.threadShow ], |
38 | 52 | |
@@ -47,14 +61,8 @@ | ||
47 | 61 | // [ location => location.page === 'groupNew', pages.groupNew ], |
48 | 62 | // // [ location => location.type === 'groupShow' && isMsg(location.key), pages.groupShow ], |
49 | 63 | // [ location => location.channel , pages.channel ], |
50 | 64 | |
51 | - // Blog pages | |
52 | - [ location => location.page === 'home', pages.blogIndex ], | |
53 | - [ location => location.page === 'discovery', pages.blogIndex ], | |
54 | - [ location => location.page === 'blogIndex', pages.blogIndex ], | |
55 | - [ location => location.page === 'blogNew', pages.blogNew ], | |
56 | - | |
57 | 65 | [ location => location.page === 'settings', pages.settings ], |
58 | 66 | |
59 | 67 | // [ location => isBlob(location.blob), pages.image ], |
60 | 68 | [ location => isBlob(location.blob), (location) => { |
styles/button.mcss | ||
---|---|---|
@@ -1,10 +1,10 @@ | ||
1 | 1 | Button { |
2 | 2 | font-family: arial |
3 | - background-color: #fff | |
3 | + $backgroundPrimaryText | |
4 | 4 | |
5 | 5 | min-width: 6rem |
6 | - height: 1.2rem | |
6 | + height: 1.2em | |
7 | 7 | padding: .2rem 1rem |
8 | 8 | |
9 | 9 | border: 1px #b9b9b9 solid |
10 | 10 | border-radius: 10rem |
@@ -23,15 +23,23 @@ | ||
23 | 23 | } |
24 | 24 | |
25 | 25 | -primary { |
26 | 26 | $colorPrimary |
27 | + $font | |
27 | 28 | $borderPrimary |
28 | 29 | |
29 | 30 | :hover { |
30 | 31 | opacity: .9 |
31 | 32 | } |
32 | 33 | } |
33 | 34 | |
35 | + -channel { | |
36 | + $backgroundPrimary | |
37 | + $colorFontPrimary | |
38 | + font-size: .9rem | |
39 | + min-width: initial | |
40 | + } | |
41 | + | |
34 | 42 | -showMore { |
35 | 43 | width: 100% |
36 | 44 | |
37 | 45 | padding: .2rem 0 |
styles/global.mcss | ||
---|---|---|
@@ -1,7 +1,7 @@ | ||
1 | 1 | body { |
2 | 2 | $fontBasic |
3 | - background-color: #fff | |
3 | + $backgroundPrimaryText | |
4 | 4 | |
5 | 5 | margin: 0 |
6 | 6 | |
7 | 7 | // different to Page |
styles/mixins.js | ||
---|---|---|
@@ -44,10 +44,10 @@ | ||
44 | 44 | $colorFontBasic { |
45 | 45 | color: #222 |
46 | 46 | } |
47 | 47 | |
48 | -$colorPrimaryFG { | |
49 | - color: #fff | |
48 | +$colorFontPrimary { | |
49 | + color: #5c6bc0 | |
50 | 50 | } |
51 | 51 | |
52 | 52 | $colorSubtle { |
53 | 53 | color: #999 |
@@ -56,28 +56,52 @@ | ||
56 | 56 | $backgroundPrimary { |
57 | 57 | background-color: #f5f6f7 |
58 | 58 | } |
59 | 59 | |
60 | +$backgroundPrimaryText { | |
61 | + background-color: #fff | |
62 | +} | |
63 | + | |
60 | 64 | $backgroundSelected { |
61 | 65 | background-color: #f0f1f2 |
62 | 66 | } |
63 | 67 | |
64 | 68 | $borderPrimary { |
65 | 69 | border: 1px #2f63ad solid |
66 | 70 | } |
67 | 71 | |
68 | -$avatarSmall { | |
69 | - width: 3rem | |
70 | - height: 3rem | |
71 | - border-radius: 1.5rem | |
72 | +$circleTiny { | |
73 | + min-width: 2rem | |
74 | + min-height: 2rem | |
75 | + width: 2rem | |
76 | + height: 2rem | |
77 | + border-radius: 1rem | |
72 | 78 | } |
73 | 79 | |
74 | -$avatarLarge { | |
75 | - width: 6rem | |
76 | - height: 6rem | |
77 | - border-radius: 3rem | |
80 | +$circleSmall { | |
81 | + min-width: 2.8rem | |
82 | + min-height: 2.8rem | |
83 | + width: 2.8rem | |
84 | + height: 2.8rem | |
85 | + border-radius: 1.4rem | |
78 | 86 | } |
79 | 87 | |
88 | +$circleMedium { | |
89 | + min-width: 3.5rem | |
90 | + min-height: 3.5rem | |
91 | + width: 3.5rem | |
92 | + height: 3.5rem | |
93 | + border-radius: 1.75rem | |
94 | +} | |
95 | + | |
96 | +$circlelarge { | |
97 | + min-width: 5rem | |
98 | + min-height: 5rem | |
99 | + width: 5rem | |
100 | + height: 5rem | |
101 | + border-radius: 2.5rem | |
102 | +} | |
103 | + | |
80 | 104 | $markdownSmall { |
81 | 105 | div.Markdown { |
82 | 106 | h1, h2, h3, h4, h5, h6, p { |
83 | 107 | font-size: .9rem |
Built with git-ssb-web