git ssb

2+

mixmix / ticktack



Commit 9de2656efd585098ef7eb72d618684bd03e34c37

blogSearch minimal + openExternal

mix irving committed on 12/1/2017, 5:21:20 AM
Parent: d01f8cc07bc8311ed0717219f766c6a4dd7cec7a

Files changed

app/async/catch-link-click.jschanged
app/html/blogCard.jschanged
app/html/blogCard.mcsschanged
app/html/context.mcsschanged
app/html/thread.mcsschanged
app/html/blogHeader.jsdeleted
app/html/blogNav.jsadded
app/html/blogHeader.mcssdeleted
app/html/blogNav.mcssadded
app/index.jschanged
app/page/blogIndex.jschanged
app/page/blogIndex.mcsschanged
app/page/blogSearch.jschanged
app/page/blogSearch.mcsschanged
app/page/blogShow.jschanged
app/page/blogShow.mcsschanged
app/page/settings.jschanged
app/sync/initialize/clickHandler.jschanged
message/html/channel.jschanged
styles/button.mcsschanged
translations/en.jschanged
app/async/catch-link-click.jsView
@@ -37,9 +37,9 @@
app/html/blogCard.jsView
@@ -23,13 +23,15 @@
2323
2424 exports.needs = nest({
2525 'keys.sync.id': 'first',
2626 'history.sync.push': 'first',
27+ 'about.obs.color': 'first',
2728 'about.obs.name': 'first',
2829 'about.html.avatar': 'first',
2930 'translations.sync.strings': 'first',
3031 'unread.sync.isUnread': 'first',
3132 // 'message.html.markdown': 'first',
33+ 'message.html.channel': 'first',
3234 'message.html.timeago': 'first',
3335 'blob.sync.url': 'first',
3436 'emoji.sync.url': 'first'
3537 })
@@ -100,8 +102,13 @@
100102 //Hey this works! fit an image into a specific size (see blog-card.mcss)
101103 //centered, and scaled to fit the square (works with both landscape and portrait!)
102104 //This is functional css not opinionated css, so all embedded.
103105 img.style = 'background-image: url("'+api.blob.sync.url(m[1])+'"); background-position:center; background-size: cover;'
106+ } else {
107+ var style = { 'background-color': api.about.obs.color(blog.key) }
108+ img = h('Thumbnail -empty', { style }, [
109+ h('i.fa.fa-file-text-o')
110+ ])
104111 }
105112
106113 const title = render(marksum.title(content.text))
107114 const summary = render(marksum.summary(content.text))
@@ -118,9 +125,9 @@
118125 img,
119126 h('div.text', [
120127 h('h2', {innerHTML: title}),
121128 content.channel
122- ? h('Button -channel', '#'+content.channel)
129+ ? api.message.html.channel(blog)
123130 : '',
124131 h('div.summary', {innerHTML: summary})
125132 ])
126133 ])
app/html/blogCard.mcssView
@@ -9,9 +9,9 @@
99 flex-direction: column
1010
1111 div.context {
1212 font-size: .8rem
13- margin-bottom: 1rem
13+ margin-bottom: 1.5rem
1414
1515 display: flex
1616 align-items: center
1717
@@ -63,10 +63,20 @@
6363 }
6464
6565 Thumbnail {
6666 border-radius: .5rem
67- min-width: 8rem
68- min-height: 6rem
69- width: 8rem
70- height: 6rem
67+ min-width: 9rem
68+ width: 9rem
69+ min-height: 7rem
70+ height: 7rem
71+
72+ -empty {
73+ color: #fff
74+ font-size: 1.8rem
75+ opacity: .8
76+
77+ display: flex
78+ justify-content: center
79+ align-items: center
80+ }
7181 }
7282
app/html/context.mcssView
@@ -12,32 +12,28 @@
1212 overflow-x: hidden
1313
1414 border-right: 1px gainsboro solid
1515
16- div.wrapper {
17- section {
18- header {
19- $colorFontSubtle
20- padding: .5rem 1rem
21- }
16+ section {
17+ header {
18+ $colorFontSubtle
19+ padding: .5rem 1rem
20+ }
2221
23- div.Option {}
22+ div.Option {}
2423
25- hr {
26- border: 1px solid gainsboro
27- border-bottom: none
28- margin: 0
29- }
24+ hr {
25+ border: 1px solid gainsboro
26+ border-bottom: none
27+ margin: 0
3028 }
3129 }
3230
3331 -one {}
3432 -two {
35- div.wrapper {
36- section {
37- div.Option {
38- padding: 0 1rem
39- }
33+ section {
34+ div.Option {
35+ padding: 0 1rem
4036 }
4137 }
4238 }
4339 }
app/html/thread.mcssView
@@ -32,8 +32,9 @@
3232 div.msg-row {
3333 div.msg {
3434 border: 1px #fff solid
3535 $roundRight
36+
3637 -unread {
3738 font-weight: bold
3839 }
3940
app/html/blogHeader.jsView
@@ -1,47 +1,0 @@
1-const nest = require('depnest')
2-const { h, computed, when } = require('mutant')
3-const get = require('lodash/get')
4-
5-exports.gives = nest('app.html.blogHeader')
6-
7-exports.needs = nest({
8- 'history.sync.push': 'first',
9- 'translations.sync.strings': 'first',
10-})
11-
12-exports.create = (api) => {
13- return nest('app.html.blogHeader', (location) => {
14- const strings = api.translations.sync.strings()
15- const goTo = (loc) => () => api.history.sync.push(loc)
16-
17- if (location.page === 'blogIndex' || location.page === 'blogSearch') {
18- return h('BlogHeader', [
19- h('div.left', [
20- h('div', {
21- className: location.page === 'blogIndex' ? '-active' : '',
22- 'ev-click': goTo({ page: 'blogIndex' })
23- }, strings.blogHeader.blogsAll),
24- h('div', {
25- className: location.page === 'blogSearch' ? '-active' : '',
26- 'ev-click': goTo({ page: 'blogSearch' })
27- }, strings.blogHeader.blogSearch),
28- ]),
29- h('div.right', [
30- h('Button -primary', { 'ev-click': () => api.history.sync.push({ page: 'blogNew' }) }, strings.blogNew.actions.writeBlog),
31- ])
32- ])
33- }
34-
35- return h('BlogHeader', [
36- h('div.left', [
37- h('div.-discovery', { 'ev-click': goTo({ page: 'blogIndex' }) }, [
38- h('i.fa.fa-chevron-left'),
39- strings.blogIndex.title
40- ]),
41- ]),
42- h('div.right', [
43- ])
44- ])
45- })
46-}
47-
app/html/blogNav.jsView
@@ -1,0 +1,47 @@
1+const nest = require('depnest')
2+const { h, computed, when } = require('mutant')
3+const get = require('lodash/get')
4+
5+exports.gives = nest('app.html.blogNav')
6+
7+exports.needs = nest({
8+ 'history.sync.push': 'first',
9+ 'translations.sync.strings': 'first',
10+})
11+
12+exports.create = (api) => {
13+ return nest('app.html.blogNav', (location) => {
14+ const strings = api.translations.sync.strings()
15+ const goTo = (loc) => () => api.history.sync.push(loc)
16+
17+ if (location.page === 'blogIndex' || location.page === 'blogSearch') {
18+ return h('BlogNav', [
19+ h('div.left', [
20+ h('div', {
21+ className: location.page === 'blogIndex' ? '-active' : '',
22+ 'ev-click': goTo({ page: 'blogIndex' })
23+ }, strings.blogNav.blogsAll),
24+ h('div', {
25+ className: location.page === 'blogSearch' ? '-active' : '',
26+ 'ev-click': goTo({ page: 'blogSearch' })
27+ }, strings.blogNav.blogSearch),
28+ ]),
29+ h('div.right', [
30+ h('Button -primary', { 'ev-click': () => api.history.sync.push({ page: 'blogNew' }) }, strings.blogNew.actions.writeBlog),
31+ ])
32+ ])
33+ }
34+
35+ return h('BlogNav', [
36+ h('div.left', [
37+ h('div.-discovery', { 'ev-click': goTo({ page: 'blogIndex' }) }, [
38+ h('i.fa.fa-chevron-left'),
39+ strings.blogIndex.title
40+ ]),
41+ ]),
42+ h('div.right', [
43+ ])
44+ ])
45+ })
46+}
47+
app/html/blogHeader.mcssView
@@ -1,36 +1,0 @@
1-BlogHeader {
2- background-color: #fff
3- height: 2.5rem
4- padding: 1rem
5- padding-bottom: .5rem
6-
7- display: flex
8- justify-content: space-between
9-
10- div.left {
11- display: flex
12-
13- div {
14- font-size: 1rem
15- $colorFontSubtle
16- cursor: pointer
17-
18- padding-bottom: .6rem
19- margin: .5rem 1rem .5rem .5rem
20-
21- -active {
22- $colorFontBasic
23- border-bottom: 2px solid #222
24- }
25-
26- -discovery {
27- $colorFontBasic
28- i { margin-right: .5rem }
29- }
30- }
31- }
32-
33- div.right {
34- }
35-}
36-
app/html/blogNav.mcssView
@@ -1,0 +1,36 @@
1+BlogNav {
2+ background-color: #fff
3+ height: 2.5rem
4+ padding: 1rem
5+ padding-bottom: .5rem
6+
7+ display: flex
8+ justify-content: space-between
9+
10+ div.left {
11+ display: flex
12+
13+ div {
14+ font-size: 1rem
15+ $colorFontSubtle
16+ cursor: pointer
17+
18+ padding-bottom: .6rem
19+ margin: .5rem 1rem .5rem .5rem
20+
21+ -active {
22+ $colorFontBasic
23+ border-bottom: 2px solid #222
24+ }
25+
26+ -discovery {
27+ $colorFontBasic
28+ i { margin-right: .5rem }
29+ }
30+ }
31+ }
32+
33+ div.right {
34+ }
35+}
36+
app/index.jsView
@@ -9,9 +9,9 @@
99 header: require('./html/header'),
1010 thread: require('./html/thread'),
1111 link: require('./html/link'),
1212 blogCard: require('./html/blogCard'),
13- blogHeader: require('./html/blogHeader'),
13+ blogNav: require('./html/blogNav'),
1414 scroller: require('./html/scroller'),
1515 },
1616 page: {
1717 blogIndex: require('./page/blogIndex'),
app/page/blogIndex.jsView
@@ -6,9 +6,9 @@
66
77 exports.needs = nest({
88 'app.html.context': 'first',
99 'app.html.blogCard': 'first',
10- 'app.html.blogHeader': 'first',
10+ 'app.html.blogNav': 'first',
1111 'app.html.scroller': 'first',
1212 'feed.pull.public': 'first',
1313 'history.sync.push': 'first',
1414 'keys.sync.id': 'first',
@@ -23,9 +23,9 @@
2323 var strings = api.translations.sync.strings()
2424
2525 var blogs = api.app.html.scroller({
2626 classList: ['content'],
27- prepend: api.app.html.blogHeader(location),
27+ prepend: api.app.html.blogNav(location),
2828 stream: api.feed.pull.public,
2929 filter: () => pull(
3030 pull.filter(msg => {
3131 const type = msg.value.content.type
app/page/blogIndex.mcssView
@@ -6,18 +6,21 @@
66 position: sticky
77 left: 0
88 right: 0
99 top: 0
10+ z-index: 99
11+
12+ background-color: #fff
1013 }
1114
1215 section.content {
1316 background-color: #fff
1417 $maxWidth
15- margin: .8rem
16- padding: 0 2rem
18+ margin: .8rem auto
19+ padding: .5rem 2rem
1720
1821 div.BlogCard {
19- border-bottom: 1px solid gainsboro
22+ border-bottom: 1px solid rgba(0,0,0, .1)
2023 }
2124 }
2225
2326 section.bottom {
app/page/blogSearch.jsView
@@ -1,18 +1,20 @@
11 const nest = require('depnest')
2-const { h } = require('mutant')
2+const { h, Value, computed, map, when, resolve } = require('mutant')
33 const pull = require('pull-stream')
44
55 exports.gives = nest('app.page.blogSearch')
66
77 exports.needs = nest({
88 'app.html.context': 'first',
99 'app.html.blogCard': 'first',
10- 'app.html.blogHeader': 'first',
10+ 'app.html.blogNav': 'first',
1111 'app.html.scroller': 'first',
12+ 'channel.obs.recent': 'first',
1213 'feed.pull.public': 'first',
1314 'history.sync.push': 'first',
1415 'keys.sync.id': 'first',
16+ 'message.html.channel': 'first',
1517 'translations.sync.strings': 'first',
1618 'unread.sync.isUnread': 'first'
1719 })
1820
@@ -20,21 +22,58 @@
2022 return nest('app.page.blogSearch', blogSearch)
2123
2224 function blogSearch (location) {
2325 // location here can expected to be: { page: 'blogSearch'}
26+ // OR { page: 'blogSearch', channel: 'scuttlebutt', searchVal: 'scutt'}
2427
2528 var strings = api.translations.sync.strings()
2629
30+ var searchVal = Value(resolve(location.searchVal) || '')
31+ var searchResults = computed([api.channel.obs.recent(), searchVal], (channels, val) => {
32+ if (val.length < 2) return []
33+
34+ return channels.filter(c => c.toLowerCase().indexOf(val.toLowerCase()) > -1)
35+ })
36+ var searchField = h('div.search', [
37+ h('div.input', [
38+ h('i.fa.fa-search'),
39+ h('input', {
40+ 'ev-input': e => searchVal.set(e.target.value),
41+ value: searchVal
42+ })
43+ ]),
44+ when(searchResults,
45+ h('div.results', map(searchResults, channel => {
46+ const classList = channel === location.channel
47+ ? ['-channelActive']
48+ : ''
49+ const newLocation = {
50+ page: 'blogSearch',
51+ channel,
52+ searchVal
53+ }
54+ return api.message.html.channel(channel, { classList, location: newLocation })
55+ }))
56+ )
57+ ])
58+
2759 var blogs = api.app.html.scroller({
2860 classList: ['content'],
29- prepend: api.app.html.blogHeader(location),
61+ prepend: [
62+ api.app.html.blogNav(location),
63+ searchField
64+ ],
3065 stream: api.feed.pull.public,
3166 filter: () => pull(
3267 pull.filter(msg => {
3368 const type = msg.value.content.type
3469 return type === 'post' || type === 'blog'
3570 }),
36- pull.filter(msg => !msg.value.content.root) // show only root messages
71+ pull.filter(msg => !msg.value.content.root), // show only root messages
72+ pull.filter(msg => {
73+ if (!location.channel) return true
74+ return msg.value.content.channel === location.channel
75+ })
3776 ),
3877 // FUTURE : if we need better perf, we can add a persistent cache. At the moment this page is fast enough though.
3978 // See implementation of app.html.context for example
4079 // store: recentMsgCache,
app/page/blogSearch.mcssView
@@ -6,16 +6,54 @@
66 position: sticky
77 left: 0
88 right: 0
99 top: 0
10- flex-basis: 100%
10+ z-index: 99
11+
12+ background-color: #fff
13+
14+ div.search {
15+ border-top: 1px solid gainsboro
16+ padding: 1rem
17+
18+ div.input {
19+ max-width: 16rem
20+ border: 1px solid gainsboro
21+ margin-left: .5rem
22+
23+ display: flex
24+ align-items: center
25+
26+ i.fa {
27+ margin-left: .5rem
28+ }
29+
30+ input {
31+ padding: .5rem
32+ border: none
33+ outline: none
34+ }
35+ }
36+
37+ div.results {
38+ margin-top: 1rem
39+ margin-left: .5rem
40+
41+ display: flex
42+ flex-wrap: wrap
43+
44+ div.Button {
45+ margin: .2rem .1rem
46+ }
47+ }
48+ }
1149 }
1250
1351 section.content {
1452 background-color: #fff
1553 $maxWidth
16- margin: .8rem
17- padding: 0 2rem
54+ margin: .8rem auto
55+ padding: .5rem 2rem
1856
1957 div.BlogCard {
2058 border-bottom: 1px solid gainsboro
2159 }
app/page/blogShow.jsView
@@ -8,9 +8,9 @@
88
99 exports.needs = nest({
1010 'about.html.avatar': 'first',
1111 'about.obs.name': 'first',
12- 'app.html.blogHeader': 'first',
12+ 'app.html.blogNav': 'first',
1313 'app.html.comments': 'first',
1414 'app.html.context': 'first',
1515 'contact.html.follow': 'first',
1616 'message.html.channel': 'first',
@@ -39,9 +39,9 @@
3939 return h('Page -blogShow', [
4040 api.app.html.context({ page: 'discover' }), // HACK to highlight discover
4141 h('div.content', [
4242 h('section.top', [
43- api.app.html.blogHeader(location)
43+ api.app.html.blogNav(location)
4444 ]),
4545 h('section.content', [
4646 h('header', [
4747 h('div.blog', [
app/page/blogShow.mcssView
@@ -7,9 +7,9 @@
77 position: sticky
88 left: 0
99 right: 0
1010 top: 0
11- flex-basis: 100%
11+ background-color: #fff
1212
1313 div.blogHeader {
1414 }
1515 }
app/page/settings.jsView
@@ -31,10 +31,12 @@
3131 // RESET the app when the settings are changed
3232 api.settings.obs.get('language')(() => {
3333 console.log('language changed, resetting view')
3434
35- api.history.obs.store().set([]) // wipe nav cache
36- api.history.sync.push({page: 'blogIndex'}) // queue up basic pages
35+ // clear history back to start page
36+ api.history.obs.store().set([
37+ { page: 'blogIndex' }
38+ ])
3739 api.history.sync.push({page: 'settings'})
3840 })
3941
4042 const feed = api.keys.sync.id()
app/sync/initialize/clickHandler.jsView
@@ -1,5 +1,6 @@
11 const nest = require('depnest')
2+const openExternal = require('open-external')
23
34 exports.gives = nest('app.sync.initialize')
45
56 exports.needs = nest({
message/html/channel.jsView
@@ -1,6 +1,6 @@
11 const nest = require('depnest')
2-const { h } = require('mutant')
2+const { h, resolve } = require('mutant')
33
44 exports.gives = nest('message.html.channel')
55
66 exports.needs = nest({
@@ -9,16 +9,30 @@
99
1010 exports.create = function (api) {
1111 return nest('message.html.channel', channel)
1212
13- function channel (msg) {
14- const { channel } = msg.value.content
13+ function channel (msgOrChannel, opts = {} ) {
14+ const channel = typeof msgOrChannel === 'string'
15+ ? msgOrChannel
16+ : msgOrChannel.value.content.channel
1517
1618 if (!channel) return
1719
20+ const {
21+ classList = [],
22+ location = { page: 'blogSearch', channel }
23+ } = opts
24+
25+ const goToChannel = (e) => {
26+ e.stopPropagation()
27+
28+ api.history.sync.push(location)
29+ }
30+
1831 return h('Button -channel', {
19- // 'ev-click': () => history.sync.push({ page: 'channelIndex', channel }) // TODO
20- }, channel)
32+ 'ev-click': goToChannel,
33+ classList
34+ }, '#' + channel)
2135 }
2236 }
2337
2438
styles/button.mcssView
@@ -38,8 +38,15 @@
3838 font-size: .9rem
3939 min-width: initial
4040 }
4141
42+ -channelActive {
43+ $colorPrimary
44+ $borderPrimary
45+ font-size: .9rem
46+ min-width: initial
47+ }
48+
4249 -showMore {
4350 width: 100%
4451
4552 padding: .2rem 0
translations/en.jsView
@@ -1,9 +1,9 @@
11 module.exports = {
22 blogIndex: {
33 title: 'Discover',
44 },
5- blogHeader: {
5+ blogNav: {
66 blogsAll: 'Dynamics',
77 blogSearch: 'Search'
88 },
99 blogNew: {

Built with git-ssb-web