git ssb

10+

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