git ssb

2+

mixmix / ticktack



Commit 87dd7388171016a3b49fb155c4b07fd06b4c5436

get comment notifications mostly working

mix irving committed on 5/7/2018, 4:55:57 AM
Parent: fea2881ae175c88bac926fcff65d32598742e9a1

Files changed

app/html/comments.jschanged
app/html/scroller.jschanged
app/html/scroller.mcsschanged
app/page/notifications.jschanged
app/page/notifications.mcssadded
message/html/comment.jschanged
message/html/comment.mcsschanged
ssb-server-blog-stats.jschanged
app/html/comments.jsView
@@ -22,15 +22,12 @@
2222 return msgs
2323 .filter(msg => forkOf(msg) === undefined) // exclude nested replies / forks
2424 .filter(msg => msg.value.content.root) // exclude root message / blog
2525 .map(comment => {
26- const nestedReplies = msgs.filter(msg => forkOf(msg) === comment.key)
27-
2826 return Struct({
29- comment: comment,
30- replies: nestedReplies
27+ comment,
28+ replies: msgs.filter(msg => forkOf(msg) === comment.key)
3129 })
32- // return Object.assign({}, comment, { replies: nestedReplies })
3330 })
3431 })
3532
3633 const root = computed(messages, ary => ary[0].key)
@@ -58,9 +55,9 @@
5855 {
5956 comparer: (a, b) => {
6057 if (a === undefined || b === undefined) return false
6158
62- return a.comment() === b.comment() && a.replies().length === b.replies().length
59+ return a.comment().key === b.comment().key && a.replies().length === b.replies().length
6360 }
6461 }
6562 ),
6663 compose({ meta, feedIdsInThread, shrink: false, canAttach: true, placeholder: strings.writeComment })
app/html/scroller.jsView
@@ -13,20 +13,20 @@
1313 return nest('app.html.scroller', createScroller)
1414
1515 function createScroller (opts = {}) {
1616 const {
17- stream,
17+ stream, // TODO - rename this to createStream (rename across app)
1818 filter = () => pull.filter((msg) => true),
1919 indexProperty = ['value', 'timestamp']
2020 } = opts
2121
2222 const streamToTop = pull(
23- next(stream, {old: false, limit: 100, property: indexProperty }),
23+ next(stream, { live: true, reverse: false, old: false, limit: 100, property: indexProperty }),
2424 filter() // is a pull-stream through
2525 )
2626
2727 const streamToBottom = pull(
28- next(stream, {reverse: true, limit: 100, live: false, property: indexProperty }),
28+ next(stream, { live: false, reverse: true, limit: 100, property: indexProperty }),
2929 filter()
3030 )
3131
3232 return Scroller(Object.assign({}, opts, { streamToTop, streamToBottom }))
app/html/scroller.mcssView
@@ -1,8 +1,8 @@
11 Scroller {
22 overflow: auto
33 width: 100%
4- height: 100%
4+ /* height: 100% */
55 min-height: 0px
66
77 section.top {
88 /* position: sticky */
app/page/notifications.jsView
@@ -1,146 +1,48 @@
11 const nest = require('depnest')
2-const { h, onceTrue, throttle, Value, Array: MutantArray, map } = require('mutant')
3-const pull = require('pull-stream')
2+const { h, onceTrue } = require('mutant')
3+const defer = require('pull-defer')
44
55 exports.gives = nest('app.page.notifications')
66
77 exports.needs = nest({
8- // 'app.html.blogCard': 'first',
9- // 'app.html.topNav': 'first',
10- // 'app.html.scroller': 'first',
8+ 'app.html.scroller': 'first',
119 'app.html.sideNav': 'first',
1210 'message.html.comment': 'first',
13- // 'blog.sync.isBlog': 'first',
14- // 'feed.pull.public': 'first',
15- // 'feed.pull.type': 'first',
16- // 'history.sync.push': 'first',
17- // 'keys.sync.id': 'first',
18- // 'message.sync.isBlocked': 'first',
1911 'sbot.obs.connection': 'first',
20- 'translations.sync.strings': 'first',
21- // 'unread.sync.isUnread': 'first'
12+ 'translations.sync.strings': 'first'
2213 })
2314
2415 exports.create = (api) => {
25- // var blogsCache = MutantArray()
26-
2716 return nest('app.page.notifications', function (location) {
28- // location here can expected to be: { page: 'notifications'}
17+ // location here can expected to be: { page: 'notifications', section: * }
2918
30- var strings = api.translations.sync.strings()
31-
32- var commentsStore = MutantArray([])
33-
34- onceTrue(api.sbot.obs.connection, server => {
35- pull(
36- server.blogStats.readAllComments(),
37- pull.drain(m => {
38- commentsStore.push(m)
39- })
40- )
19+ var scroller = api.app.html.scroller({
20+ classList: ['content'],
21+ stream: createBlogCommentStream,
22+ render: Comment
4123 })
4224
43- // server.blogStats.getBlogs({ keys: true, values: false }, (err, data) => {
44- // if (err) throw err
25+ function createBlogCommentStream (opts) {
26+ const source = defer.source()
27+ var resolved = false
4528
46- // const blogIds = data.map(d => d[1])
29+ onceTrue(api.sbot.obs.connection, server => {
30+ if (resolved) return
4731
48- // var source = server.blogStats.read({
49- // // live: true,
50- // gte: [ 'C', undefined, undefined ],
51- // lte: [ 'C~', null, null ],
52- // reverse: true,
53- // values: true,
54- // keys: true,
55- // seqs: false
56- // })
57- // console.log(blogIds)
32+ source.resolve(server.blogStats.readAllComments(opts))
33+ resolved = true
34+ })
5835
59- // pull(
60- // source,
61- // pull.filter(result => {
62- // return blogIds.includes(result.key[1])
63- // }),
64- // pull.map(result => result.value),
65- // pull.drain(m => {
66- // commentsStore.push(m)
67- // })
68- // )
69- // })
70- // })
36+ return source
37+ }
7138
72- // var blogs = api.app.html.scroller({
73- // classList: ['content'],
74- // prepend: api.app.html.topNav(location),
75- // // stream: api.feed.pull.public,
76- // stream: api.feed.pull.type('blog'),
77- // filter: () => pull(
78- // // pull.filter(api.blog.sync.isBlog),
79- // pull.filter(msg => !msg.value.content.root), // show only root messages
80- // pull.filter(msg => !api.message.sync.isBlocked(msg))
81- // ),
82- // // FUTURE : if we need better perf, we can add a persistent cache. At the moment this page is fast enough though.
83- // // See implementation of app.html.sideNav for example
84- // store: blogsCache,
85- // updateTop: update,
86- // updateBottom: update,
87- // render
88- // })
89-
9039 return h('Page -notifications', [
9140 api.app.html.sideNav(location),
92- h('div.content', map(
93- throttle(commentsStore, 300),
94- msg => Comment(msg),
95- {
96- // comparer: (a, b) => a === b
97- }
98- ))
41+ scroller
9942 ])
10043
10144 function Comment (msg) {
102- return api.message.html.comment(msg)
45+ return api.message.html.comment({ comment: msg, showRootLink: true })
10346 }
10447 })
105-
106-/* function update (soFar, newBlog) { */
107-// soFar.transaction(() => {
108-// const { timestamp } = newBlog.value
109-
110-// var object = newBlog // Value(newBlog)
111-
112-// const index = indexOf(soFar, (blog) => newBlog.key === resolve(blog).key)
113-// // if blog already in cache, not needed again
114-// if (index >= 0) return
115-
116-// // Orders by: time received
117-// const justOlderPosition = indexOf(soFar, (msg) => newBlog.timestamp > resolve(msg).timestamp)
118-
119-// // Orders by: time published BUT the messagesByType stream streams _by time received_
120-// // TODO - we need an index of all blogs otherwise the scroller doesn't work...
121-// // const justOlderPosition = indexOf(soFar, (msg) => timestamp > resolve(msg).value.timestamp)
122-
123-// if (justOlderPosition > -1) {
124-// soFar.insert(object, justOlderPosition)
125-// } else {
126-// soFar.push(object)
127-// }
128-// })
129-// }
130-
131-// function render (blog) {
132-// const { recps, channel } = blog.value.content
133-// var onClick
134-// if (channel && !recps) { onClick = (ev) => api.history.sync.push(Object.assign({}, blog, { page: 'blogShow' })) }
135-// return api.app.html.blogCard(blog, { onClick })
136-// }
137-// }
138-
139-// function indexOf (array, fn) {
140-// for (var i = 0; i < array.getLength(); i++) {
141-// if (fn(array.get(i))) {
142-// return i
143-// }
144-// }
145-// return -1
14648 }
app/page/notifications.mcssView
@@ -1,0 +1,11 @@
1+Page -notifications {
2+ div.Scroller.content {
3+
4+ section.content {
5+ div.Comment {
6+ flex-grow: 1
7+
8+ }
9+ }
10+ }
11+}
message/html/comment.jsView
@@ -1,49 +1,62 @@
11 const nest = require('depnest')
2-const { h, Value, map, when, resolve } = require('mutant')
2+const { h, Value, map, when, resolve, computed, onceTrue } = require('mutant')
33 const get = require('lodash/get')
4+const { heads } = require('ssb-sort')
45
56 exports.gives = nest('message.html.comment')
67
78 exports.needs = nest({
89 'about.html.avatar': 'first',
910 'about.obs.name': 'first',
10- // 'backlinks.obs.for': 'first',
11+ 'backlinks.obs.for': 'first',
12+ 'blog.html.title': 'first',
1113 'message.html.compose': 'first',
1214 'message.html.markdown': 'first',
1315 'message.html.timeago': 'first',
1416 'message.html.likes': 'first',
1517 'unread.sync.markRead': 'first',
1618 'unread.sync.isUnread': 'first',
19+ 'sbot.obs.connection': 'first',
1720 'translations.sync.strings': 'first'
1821 })
1922
2023 exports.create = (api) => {
2124 return nest('message.html.comment', Comment)
2225
23- function Comment ({ comment: msgObs, replies, branch }) {
26+ function Comment ({ comment, replies, branch, showRootLink = false }) {
2427 const strings = api.translations.sync.strings()
25- const msg = resolve(msgObs)
26-
27- const raw = get(msg, 'value.content.text')
28- var className = api.unread.sync.isUnread(msg) ? ' -unread' : ' -read'
29- api.unread.sync.markRead(msg)
30-
28+ const msg = resolve(comment)
3129 var root = get(msg, 'value.content.root')
3230 if (!root) return
3331
3432 const { author, content } = msg.value
3533
36- // // TODO - move this upstream into patchcore:feed.obs.thread ??
37- // // OR change strategy to use forks
38- // const backlinks = api.backlinks.obs.for(msg.key)
39- // const nestedReplies = computed(backlinks, backlinks => {
40- // return backlinks.filter(backlinker => {
41- // const { type, root } = backlinker.value.content
42- // return type === 'post' && root === msg.key
43- // })
44- // })
34+ if (!replies) {
35+ replies = computed(api.backlinks.obs.for(msg.key), backlinks => {
36+ return backlinks.filter(backlinker => {
37+ const { type, root } = backlinker.value.content
38+ return type === 'post' && root === msg.key
39+ })
40+ })
41+ }
42+ if (!branch) {
43+ branch = computed(api.backlinks.obs.for(root), backlinks => {
44+ return heads(backlinks)
45+ })
46+ }
4547
48+ var className = api.unread.sync.isUnread(msg) ? ' -unread' : ' -read'
49+ api.unread.sync.markRead(msg)
50+
51+ var title = Value()
52+ if (showRootLink) {
53+ processMessage(root, msg => {
54+ var t = api.blog.html.title(msg)
55+ title.set(t.innerText ? t.innerText : t)
56+ })
57+ }
58+
4659 var nestedReplyCompose = Value(false)
4760 const toggleCompose = () => nestedReplyCompose.set(!nestedReplyCompose())
4861 const nestedReplyComposer = api.message.html.compose({
4962 meta: {
@@ -63,17 +76,24 @@
6376 h('div.left', api.about.html.avatar(author, 'tiny')),
6477 h('div.right', [
6578 h('section.context', [
6679 h('div.name', api.about.obs.name(author)),
67- api.message.html.timeago(msg)
80+ api.message.html.timeago(msg),
81+ when(showRootLink, h('a.rootLink', {href: root}, ['<< ', title, ' >>']))
82+ // TODO don't link to root, link to position of message within blog!
6883 ]),
69- h('section.content', api.message.html.markdown(raw)),
84+ h('section.content', api.message.html.markdown(get(msg, 'value.content.text'))),
7085 when(replies,
7186 h('section.replies',
7287 map(
7388 replies,
7489 NestedComment,
75- { comparer: (a, b) => a === b }
90+ {
91+ comparer: (a, b) => {
92+ if (a === undefined || b === undefined) return false
93+ return a.key === b.key
94+ }
95+ }
7696 )
7797 )
7898 ),
7999 h('section.actions', [
@@ -86,10 +106,10 @@
86106 ])
87107 ])
88108 }
89109
90- function NestedComment (msgObs) {
91- const msg = resolve(msgObs)
110+ function NestedComment (comment) {
111+ const msg = resolve(comment)
92112 const raw = get(msg, 'value.content.text')
93113 if (!raw) return
94114
95115 const { author } = msg.value
@@ -104,10 +124,12 @@
104124 h('section.content', api.message.html.markdown(raw))
105125 ])
106126 ])
107127 }
108-}
109128
110-function forkOf (msg) {
111- return get(msg, 'value.content.fork')
129+ function processMessage (key, fn) {
130+ onceTrue(api.sbot.obs.connection, server => server.get(key, (err, value) => {
131+ if (err) return console.error(err)
132+ fn({ key, value })
133+ }))
134+ }
112135 }
113-
message/html/comment.mcssView
@@ -21,9 +21,14 @@
2121 div.name {
2222 font-size: 1.2rem
2323 margin-right: 1rem
2424 }
25- div.Timeago {}
25+ div.Timeago {
26+ margin-right: 1.5rem
27+ }
28+ a.rootLink {
29+ font-size: .8rem
30+ }
2631 }
2732
2833 section.content {
2934 font-size: .95rem
ssb-server-blog-stats.jsView
@@ -125,32 +125,52 @@
125125
126126 return view.read(query)
127127 }
128128
129- function readAllComments () {
129+ function readAllComments (opts = {}) {
130130 var source = defer.source()
131131
132132 getBlogs({ keys: true, values: false }, (err, data) => {
133133 if (err) throw err
134134
135135 const blogIds = data.map(d => d[1])
136136
137+ opts.type = 'post'
138+ var limit = opts.limit
139+ delete opts.limit
140+ // have to remove limit from the query otherwise Next stalls out if it doesn't get a new result
141+
137142 const _source = pull(
138- view.read({
139- // live: true,
140- gt: [ 'C', null, null ],
141- lt: [ 'C', undefined, undefined ],
142- reverse: true,
143- values: true,
144- keys: true,
145- seqs: false
143+ server.messagesByType(opts),
144+ pull.filter(msg => {
145+ if (msg.value.author === server.id) return false // exclude my posts
146+ if (msg.value.content.root === undefined) return false // want only 'comments' (reply posts)
147+
148+ return blogIds.includes(msg.value.content.root) // is about one of my blogs
146149 }),
147- pull.filter(result => {
148- return blogIds.includes(result.key[1])
149- }),
150- pull.map(result => result.value)
150+ limit ? pull.take(limit) : true
151151 )
152152
153+ // I don't know what order results some out of flumeview-level read
154+ // which makes this perhaps unideal for Next / mutant-scroll
155+ // const query = {
156+ // gt: [ 'C', null, opts.gt || null ],
157+ // lt: [ 'C', undefined, opts.lt || undefined ],
158+ // reverse: opts.reverse === undefined ? true : opts.reverse,
159+ // live: opts.reverse === undefined ? true : opts.reverse,
160+ // values: true,
161+ // keys: true,
162+ // seqs: false
163+ // }
164+ // const _source = pull(
165+ // view.read(query),
166+ // pull.filter(result => {
167+ // return blogIds.includes(result.key[1])
168+ // }),
169+ // pull.map(result => result.value),
170+ // pull.take(opts.limit)
171+ // )
172+
153173 source.resolve(_source)
154174 })
155175
156176 return pull(

Built with git-ssb-web