git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Commit abef5a74d4b427f7a36f20b1be7dc85b9f22080a

initial hack at doing a compressed feed

- works fairly well but doesn’t update in realtime and doesn’t do
bottomless scrolling yet
Matt McKegg committed on 10/27/2016, 4:28:08 PM
Parent: 798364845d2d6269f19671d5e283dea786a1d408

Files changed

main-window.jschanged
package.jsonchanged
styles/patchbay-tweaks.csschanged
styles/feed-event.mcssadded
views/public-feed.jsadded
main-window.jsView
@@ -11,8 +11,9 @@
1111 module.exports = function (ssbClient, config) {
1212 var api = SbotApi(ssbClient, config)
1313 var modules = combine(extend(Modules, {
1414 'sbot-api.js': api,
15+ 'public.js': require('./views/public-feed.js'),
1516 'blob-url.js': {
1617 blob_url: function (link) {
1718 var prefix = config.blobsPrefix != null ? config.blobsPrefix : `http://localhost:${config.blobsPort}`
1819 if (typeof link.link === 'string') {
package.jsonView
@@ -22,8 +22,9 @@
2222 "pull-file": "^1.0.0",
2323 "pull-identify-filetype": "^1.1.0",
2424 "pull-stream": "^3.4.5",
2525 "scuttlebot": "^9.2.0",
26+ "sorted-array-functions": "^1.0.0",
2627 "ssb-blobs": "^0.1.7",
2728 "ssb-keys": "^7.0.0",
2829 "ssb-links": "^2.0.0",
2930 "ssb-query": "^0.1.1",
styles/patchbay-tweaks.cssView
@@ -1,6 +1,7 @@
11 .message {
22 background-color: white;
3+ padding: 10px;
34 }
45
56 .scroller__wrapper {
67 width: 600px;
@@ -12,9 +13,10 @@
1213 padding: 5px;
1314 }
1415
1516 a.avatar {
16- display: inline
17+ display: inline;
18+ font-weight: bold;
1719 }
1820
1921 div.avatar > a.avatar {
2022 display: flex;
styles/feed-event.mcssView
@@ -1,0 +1,37 @@
1+FeedEvent {
2+ display: flex
3+ flex: 1
4+ flex-direction: column
5+ background: white
6+ margin-top: 10px
7+
8+ div {
9+ flex: 1
10+ margin: 0
11+ font-size: 120%
12+ }
13+
14+ a.full {
15+ display: block
16+ padding: 10px;
17+ background: #d6e4ec;
18+ border-top: 1px solid #bbc9d2;
19+ border-bottom: 1px solid #bbc9d2;
20+ text-align: center;
21+ }
22+
23+ div.replies {
24+ font-size: 100%
25+ display: flex
26+ flex-direction: column
27+ div {
28+ flex: 1
29+ margin: 0
30+ }
31+ }
32+
33+ div.meta {
34+ font-size: 100%
35+ padding: 10px
36+ }
37+}
views/public-feed.jsView
@@ -1,0 +1,246 @@
1+var SortedArray = require('sorted-array-functions')
2+var Value = require('@mmckegg/mutant/value')
3+var MutantMap = require('@mmckegg/mutant/map')
4+var h = require('@mmckegg/mutant/html-element')
5+var when = require('@mmckegg/mutant/when')
6+var m = require('../lib/h')
7+
8+var pull = require('pull-stream')
9+
10+var plugs = require('patchbay/plugs')
11+var message_render = plugs.first(exports.message_render = [])
12+var message_compose = plugs.first(exports.message_compose = [])
13+var sbot_log = plugs.first(exports.sbot_log = [])
14+var avatar_name = plugs.first(exports.avatar_name = [])
15+var avatar_link = plugs.first(exports.avatar_link = [])
16+var message_link = plugs.first(exports.message_link = [])
17+
18+exports.screen_view = function (path, sbot) {
19+ if (path === '/public') {
20+ var sync = Value(false)
21+ var events = Value([])
22+
23+ var content = h('div.column.scroller__content', [
24+ MutantMap(events, (group) => {
25+ if (group.type === 'message') {
26+ var meta = null
27+ var replies = group.replies.slice(-3).map(message_render)
28+ var renderedMessage = group.message ? message_render(group.message) : null
29+ if (renderedMessage) {
30+ if (group.lastUpdateType === 'reply') {
31+ meta = m('div.meta', [
32+ manyPeople(group.repliesFrom), ' replied'
33+ ])
34+ } else if (group.lastUpdateType === 'dig') {
35+ meta = m('div.meta', [
36+ manyPeople(group.digs), ' dug this message'
37+ ])
38+ }
39+
40+ return m('FeedEvent', [
41+ meta,
42+ renderedMessage,
43+ when(replies.length, [
44+ when(group.replies > replies.length,
45+ m('a.full', {href: `#${group.messageId}`}, ['View full thread'])
46+ ),
47+ m('div.replies', replies)
48+ ])
49+ ])
50+ } else {
51+ if (group.lastUpdateType === 'reply') {
52+ meta = m('div.meta', [
53+ manyPeople(group.repliesFrom), ' replied to ', message_link(group.messageId)
54+ ])
55+ } else if (group.lastUpdateType === 'dig') {
56+ meta = m('div.meta', [
57+ manyPeople(group.digs), ' dug ', message_link(group.messageId)
58+ ])
59+ }
60+
61+ if (meta || replies.length) {
62+ return m('FeedEvent', [
63+ meta, m('div.replies', replies)
64+ ])
65+ }
66+ }
67+ } else if (group.type === 'follow') {
68+ return m('FeedEvent -follow', [
69+ m('div.meta', [
70+ person(group.id), ' followed ', manyPeople(group.contacts)
71+ ])
72+ ])
73+ }
74+ }, {maxTime: 5})
75+ ])
76+
77+ var div = h('div.column.scroller', {
78+ style: {
79+ 'overflow': 'auto'
80+ }
81+ }, [
82+ h('div.scroller__wrapper', [
83+ message_compose({type: 'post'}, {placeholder: 'Write a public message'}),
84+ content
85+ ])
86+ ])
87+
88+ pull(
89+ sbot_log({reverse: true, limit: 500, live: false}),
90+ pull.collect((err, values) => {
91+ if (err) throw err
92+ events.set(groupMessages(values))
93+ sync.set(true)
94+ })
95+ )
96+
97+
98+ // pull(
99+ // sbot_log({old: false}),
100+ // Scroller(div, content, message_render, true, false)
101+ // )
102+
103+ // pull(
104+ // u.next(sbot_log, {reverse: true, limit: 100, live: false}),
105+ // Scroller(div, content, message_render, false, false)
106+ // )
107+
108+ return div
109+ }
110+}
111+
112+function person (id) {
113+ return avatar_link(id, avatar_name(id), '')
114+}
115+
116+function manyPeople (ids) {
117+ ids = Array.from(ids)
118+ var featuredIds = ids.slice(-3).reverse()
119+
120+ if (ids.length > 3) {
121+ return [
122+ person(featuredIds[0]), ', ',
123+ person(featuredIds[1]),
124+ ' and ', ids.length - 2, ' others'
125+ ]
126+ } else if (ids.length === 3) {
127+ return [
128+ person(featuredIds[0]), ', ',
129+ person(featuredIds[1]), ' and ',
130+ person(featuredIds[2])
131+ ]
132+ } else if (ids.length === 2) {
133+ return [
134+ person(featuredIds[0]), ' and ',
135+ person(featuredIds[1])
136+ ]
137+ } else {
138+ return person(featuredIds[0])
139+ }
140+}
141+
142+function groupMessages (messages) {
143+ var follows = {}
144+ var messageUpdates = {}
145+ reverseForEach(messages, function (msg) {
146+ var c = msg.value.content
147+ if (c.type === 'contact') {
148+ updateContact(msg, follows)
149+ } else if (c.type === 'vote') {
150+ if (c.vote && c.vote.link) {
151+ const group = ensureMessage(c.root, messageUpdates)
152+ if (c.vote.value > 1) {
153+ group.lastUpdateType = 'vote'
154+ group.digs.add(msg.value.author)
155+ group.updated = msg.timestamp
156+ } else {
157+ group.digs.delete(msg.value.author)
158+ }
159+ }
160+ } else {
161+ if (c.root) {
162+ const group = ensureMessage(c.root, messageUpdates)
163+ group.lastUpdateType = 'reply'
164+ group.repliesFrom.add(msg.value.author)
165+ group.replies.push(msg)
166+ group.updated = msg.timestamp
167+ } else {
168+ const group = ensureMessage(msg.key, messageUpdates)
169+ group.lastUpdateType = 'post'
170+ group.updated = msg.timestamp
171+ group.message = msg
172+ }
173+ }
174+ })
175+
176+ var result = []
177+ Object.keys(follows).forEach((key) => {
178+ SortedArray.add(result, follows[key], compareUpdated)
179+ })
180+ Object.keys(messageUpdates).forEach((key) => {
181+ SortedArray.add(result, messageUpdates[key], compareUpdated)
182+ })
183+ return result
184+}
185+
186+function compareUpdated (a, b) {
187+ return b.updated - a.updated
188+}
189+
190+function reverseForEach (items, fn) {
191+ var i = items.length - 1
192+ var start = Date.now()
193+ nextBatch()
194+
195+ function nextBatch () {
196+ while (i >= 0 && Date.now() - start < 10) {
197+ fn(items[i], i)
198+ i -= 1
199+ }
200+
201+ if (i > 0) {
202+ setImmediate(nextBatch)
203+ }
204+ }
205+}
206+
207+function updateContact (msg, groups) {
208+ var c = msg.value.content
209+ var id = msg.value.author
210+ var group = groups[id]
211+ if (c.following) {
212+ if (!group) {
213+ group = groups[id] = {
214+ type: 'follow',
215+ contacts: new Set(),
216+ updated: 0,
217+ id: id
218+ }
219+ }
220+ group.contacts.add(c.contact)
221+ group.updated = msg.timestamp
222+ } else {
223+ if (group) {
224+ group.contacts.delete(c.contact)
225+ if (!group.contacts.size) {
226+ delete groups[id]
227+ }
228+ }
229+ }
230+}
231+
232+function ensureMessage (id, groups) {
233+ var group = groups[id]
234+ if (!group) {
235+ group = groups[id] = {
236+ type: 'message',
237+ repliesFrom: new Set(),
238+ replies: [],
239+ message: null,
240+ messageId: id,
241+ digs: new Set(),
242+ updated: 0
243+ }
244+ }
245+ return group
246+}

Built with git-ssb-web