git ssb

10+

Matt McKegg / patchwork



Commit 5e06ff6caacaf2d29ccced2d9c39800bbf1923de

highlight new messages, don't show replies from strangers to old posts, bump refactor

#470
Matt McKegg committed on 4/8/2017, 5:39:32 AM
Parent: fe2173f46498c968ad5bd79d249d023ca4998f88

Files changed

modules/feed/html/rollup.jschanged
modules/feed/pull/summary.jschanged
modules/page/html/render/public.jschanged
plugs/message/html/layout/default.jschanged
styles/message.mcsschanged
modules/feed/html/rollup.jsView
@@ -45,8 +45,12 @@
4545 var bumpFilter = opts && opts.bumpFilter
4646 var windowSize = opts && opts.windowSize
4747 var waitFor = opts && opts.waitFor || true
4848
49 + var newSinceRefresh = new Set()
50 + var newInSession = new Set()
51 + var prioritized = {}
52 +
4953 var updateLoader = h('a Notifier -loader', {
5054 href: '#',
5155 'ev-click': refresh
5256 }, [
@@ -73,8 +77,12 @@
7377 getStream({old: false}),
7478 pull.drain((item) => {
7579 var type = item && item.value && item.value.content.type
7680
81 + // prioritize new messages on next refresh
82 + newInSession.add(item.key)
83 + newSinceRefresh.add(item.key)
84 +
7785 // ignore message handled by another app
7886 if (api.app.sync.externalHandler(item)) return
7987
8088 if (type && type !== 'vote' && typeof item.value.content === 'object' && item.value.timestamp > twoDaysAgo()) {
@@ -139,10 +147,15 @@
139147
140148 var abortable = Abortable()
141149 abortLastFeed = abortable.abort
142150
151 + prioritized = {}
152 + newSinceRefresh.forEach(x => {
153 + prioritized[x] = 2
154 + })
155 +
143156 pull(
144- api.feed.pull.summary(getStream, {windowSize, bumpFilter}, () => {
157 + api.feed.pull.summary(getStream, {windowSize, bumpFilter, prioritized}, () => {
145158 sync.set(true)
146159 }),
147160 pull.asyncMap(ensureMessageAndAuthor),
148161 pull.filter((item) => {
@@ -156,16 +169,24 @@
156169 }),
157170 abortable,
158171 Scroller(container, content(), renderItem, false, false)
159172 )
173 +
174 + // clear high prioritized items
175 + newSinceRefresh.clear()
160176 }
161177
162178 function renderItem (item) {
163179 if (item.type === 'message') {
164180 var meta = null
165181 var previousId = item.messageId
166182 var replies = item.replies.slice(-4).map((msg) => {
167- var result = api.message.html.render(msg, {inContext: true, inSummary: true, previousId})
183 + var result = api.message.html.render(msg, {
184 + inContext: true,
185 + inSummary: true,
186 + previousId,
187 + priority: prioritized[msg.key]
188 + })
168189 previousId = msg.key
169190 return result
170191 })
171192 var renderedMessage = item.message ? api.message.html.render(item.message, {inContext: true}) : null
@@ -194,9 +215,11 @@
194215 h('div.replies', replies)
195216 ])
196217 ])
197218 } else {
198- if (item.lastUpdateType === 'reply' && item.repliesFrom.size) {
219 + // when there is no root message in this window,
220 + // try and show reply message, only show like message if we have nothing else to give
221 + if (item.repliesFrom.size) {
199222 meta = h('div.meta', {
200223 title: names(item.repliesFrom)
201224 }, [
202225 many(item.repliesFrom, api.profile.html.person), ' replied to ', api.message.html.link(item.messageId)
@@ -208,9 +231,10 @@
208231 many(item.likes, api.profile.html.person), ' liked ', api.message.html.link(item.messageId)
209232 ])
210233 }
211234
212- if (meta || replies.length) {
235 + // only show this event if it has a meta description
236 + if (meta) {
213237 return h('FeedEvent', [
214238 meta, h('div.replies', replies)
215239 ])
216240 }
modules/feed/pull/summary.jsView
@@ -17,8 +17,9 @@
1717
1818 function summary (source, opts, cb) {
1919 var bumpFilter = opts && opts.bumpFilter
2020 var windowSize = opts && opts.windowSize || 1000
21 + var prioritized = opts && opts.prioritized || {}
2122 var last = null
2223 var returned = false
2324 var done = false
2425 return pullNext(() => {
@@ -39,9 +40,9 @@
3940 returned = true
4041 } else {
4142 var fromTime = last && last.timestamp || Date.now()
4243 last = values[values.length - 1]
43- groupMessages(values, fromTime, bumpFilter, (err, result) => {
44 + groupMessages(values, fromTime, {bumpFilter, prioritized}, (err, result) => {
4445 if (err) throw err
4546 deferred.resolve(
4647 pull.values(result)
4748 )
@@ -55,46 +56,42 @@
5556 return deferred
5657 })
5758 }
5859
59-function groupMessages (messages, fromTime, bumpFilter, cb) {
60 +function groupMessages (messages, fromTime, opts, cb) {
6061 var subscribes = {}
6162 var follows = {}
6263 var messageUpdates = {}
6364 reverseForEach(messages, function (msg) {
6465 if (!msg.value) return
6566 var c = msg.value.content
6667 if (c.type === 'contact') {
67- updateContact(msg, follows)
68 + updateContact(msg, follows, opts)
6869 } else if (c.type === 'channel') {
69- updateChannel(msg, subscribes)
70 + updateChannel(msg, subscribes, opts)
7071 } else if (c.type === 'vote') {
7172 if (c.vote && c.vote.link) {
7273 // only show likes of posts added in the current window
7374 // and only for the main post
7475 const group = messageUpdates[c.vote.link]
7576 if (group) {
7677 if (c.vote.value > 0) {
77- group.lastUpdateType = 'like'
7878 group.likes.add(msg.value.author)
79- bumpIfNeeded(group, msg, bumpFilter)
79 + group.relatedMessages.push(msg)
8080 } else {
8181 group.likes.delete(msg.value.author)
82- if (group.lastUpdateType === 'like' && !group.likes.size && !group.replies.length) {
83- group.lastUpdateType = 'reply'
84- }
82 + group.relatedMessages.push(msg)
8583 }
8684 }
8785 }
8886 } else {
8987 if (c.root) {
9088 const group = ensureMessage(c.root, messageUpdates)
9189 group.fromTime = fromTime
92- group.lastUpdateType = 'reply'
9390 group.repliesFrom.add(msg.value.author)
9491 SortedArray.add(group.replies, msg, compareUserTimestamp)
9592 group.channel = group.channel || msg.value.content.channel
96- bumpIfNeeded(group, msg, bumpFilter)
93 + group.relatedMessages.push(msg)
9794 } else {
9895 const group = ensureMessage(msg.key, messageUpdates)
9996 group.fromTime = fromTime
10097 group.lastUpdateType = 'post'
@@ -107,36 +104,66 @@
107104 }
108105 }, () => {
109106 var result = []
110107 Object.keys(follows).forEach((key) => {
108 + bumpIfNeeded(follows[key], opts)
111109 if (follows[key].updated) {
112110 SortedArray.add(result, follows[key], compareUpdated)
113111 }
114112 })
115113 Object.keys(subscribes).forEach((key) => {
114 + bumpIfNeeded(subscribes[key], opts)
116115 if (subscribes[key].updated) {
117116 SortedArray.add(result, subscribes[key], compareUpdated)
118117 }
119118 })
120119 Object.keys(messageUpdates).forEach((key) => {
120 + bumpIfNeeded(messageUpdates[key], opts)
121121 if (messageUpdates[key].updated) {
122122 SortedArray.add(result, messageUpdates[key], compareUpdated)
123123 }
124124 })
125125 cb(null, result)
126126 })
127127 }
128128
129-function bumpIfNeeded (group, msg, bumpFilter) {
130- // only bump when filter passes
131- var newUpdated = msg.timestamp || msg.value.sequence
132- if (!group.updated || ((!bumpFilter || bumpFilter(msg, group)) && newUpdated > group.updated)) {
133- group.updated = newUpdated
134- }
129 +function bumpIfNeeded (group, {bumpFilter, prioritized}) {
130 + group.relatedMessages.forEach(msg => {
131 + if (prioritized[msg.key] && group.priority < prioritized[msg.key]) {
132 + group.priority = prioritized[msg.key]
133 + }
134 +
135 + var shouldBump = !bumpFilter || bumpFilter(msg, group)
136 +
137 + // only bump when filter passes
138 + var newUpdated = msg.timestamp || msg.value.sequence
139 + if (!group.updated || (shouldBump && newUpdated > group.updated)) {
140 + group.updated = newUpdated
141 + if (msg.value.content.type === 'vote') {
142 + if (group.likes.size) {
143 + group.lastUpdateType = 'like'
144 + } else if (group.repliesFrom.size) {
145 + group.lastUpdateType = 'reply'
146 + } else if (group.message) {
147 + group.lastUpdateType = 'post'
148 + }
149 + }
150 +
151 + if (msg.value.content.type === 'post') {
152 + if (msg.value.content.root) {
153 + group.lastUpdateType = 'reply'
154 + } else {
155 + group.lastUpdateType = 'post'
156 + }
157 + }
158 + }
159 + })
135160 }
136161
137162 function compareUpdated (a, b) {
138- return b.updated - a.updated
163 + // highest priority first
164 + // then most recent date
165 + return b.priority - a.priority || b.updated - a.updated
139166 }
140167
141168 function reverseForEach (items, fn, cb) {
142169 var i = items.length - 1
@@ -157,26 +184,28 @@
157184 }
158185 }
159186 }
160187
161-function updateContact (msg, groups) {
188 +function updateContact (msg, groups, opts) {
162189 var c = msg.value.content
163190 var id = msg.value.author
164191 var group = groups[id]
165192 if (ref.isFeed(c.contact)) {
166193 if (c.following) {
167194 if (!group) {
168195 group = groups[id] = {
169196 type: 'follow',
197 + priority: 0,
198 + relatedMessages: [],
170199 lastUpdateType: null,
171200 contacts: new Set(),
172201 updated: 0,
173202 author: id,
174203 id: id
175204 }
176205 }
177206 group.contacts.add(c.contact)
178- group.updated = msg.timestamp || msg.value.sequence
207 + group.relatedMessages.push(msg)
179208 } else {
180209 if (group) {
181210 group.contacts.delete(c.contact)
182211 if (!group.contacts.size) {
@@ -186,25 +215,27 @@
186215 }
187216 }
188217 }
189218
190-function updateChannel (msg, groups) {
219 +function updateChannel (msg, groups, opts) {
191220 var c = msg.value.content
192221 var channel = c.channel
193222 var group = groups[channel]
194223 if (typeof channel === 'string') {
195224 if (c.subscribed) {
196225 if (!group) {
197226 group = groups[channel] = {
198227 type: 'subscribe',
228 + priority: 0,
229 + relatedMessages: [],
199230 lastUpdateType: null,
200231 subscribers: new Set(),
201232 updated: 0,
202233 channel
203234 }
204235 }
205236 group.subscribers.add(msg.value.author)
206- group.updated = msg.timestamp || msg.value.sequence
237 + group.relatedMessages.push(msg)
207238 } else {
208239 if (group) {
209240 group.subscribers.delete(msg.value.author)
210241 if (!group.subscribers.size) {
@@ -219,9 +250,11 @@
219250 var group = groups[id]
220251 if (!group) {
221252 group = groups[id] = {
222253 type: 'message',
254 + priority: 0,
223255 repliesFrom: new Set(),
256 + relatedMessages: [],
224257 replies: [],
225258 message: null,
226259 messageId: id,
227260 likes: new Set(),
modules/page/html/render/public.jsView
@@ -73,18 +73,9 @@
7373 subscribedChannels.sync
7474 ], (...x) => x.every(Boolean)),
7575 windowSize: 500,
7676 filter: (item) => {
77- if (item.type === 'subscribe') {
78- // HACK: hide people you don't follow from subscribe notifications:
79- Array.from(item.subscribers).forEach(id => {
80- if (!following().has(id)) {
81- item.subscribers.delete(id)
82- }
83- })
84- }
85-
86- return !item.boxed && (
77 + return !item.boxed && (item.lastUpdateType !== 'post' || item.message) && (
8778 id === item.author ||
8879 (item.author && following().has(item.author)) ||
8980 (item.type === 'message' && subscribedChannels().has(item.channel)) ||
9081 (item.type === 'subscribe' && item.subscribers.size) ||
@@ -92,8 +83,24 @@
9283 item.likes && item.likes.has(id)
9384 )
9485 },
9586 bumpFilter: (msg, group) => {
87 + if (group.type === 'subscribe') {
88 + removeStrangers(group.subscribers)
89 + }
90 +
91 + if (group.type === 'message') {
92 + removeStrangers(group.likes)
93 + removeStrangers(group.repliesFrom)
94 +
95 + if (!group.message) {
96 + // if message is old, only show replies from friends
97 + group.replies = group.replies.filter(x => {
98 + return (x.value.author === id || following().has(x.value.author))
99 + })
100 + }
101 + }
102 +
96103 if (!group.message) {
97104 return (
98105 isMentioned(id, msg.value.content.mentions) ||
99106 msg.value.author === id || (
@@ -119,8 +126,18 @@
119126 result.reload = feedView.reload
120127
121128 return result
122129
130 + function removeStrangers (set) {
131 + if (set) {
132 + Array.from(set).forEach(key => {
133 + if (!following().has(key) && key !== id) {
134 + set.delete(key)
135 + }
136 + })
137 + }
138 + }
139 +
123140 function getSidebar () {
124141 var whoToFollow = computed([following, api.profile.obs.recentlyUpdated(), localPeers], (following, recent, peers) => {
125142 return Array.from(recent).filter(x => x !== id && !following.has(x) && !peers.includes(x)).slice(0, 10)
126143 })
plugs/message/html/layout/default.jsView
@@ -38,12 +38,16 @@
3838 } else if (msg.value.content.project) {
3939 replyInfo = h('span', ['on ', api.message.html.link(msg.value.content.project)])
4040 }
4141
42 + if (opts.priority === 2) {
43 + classList.push('-new')
44 + }
45 +
4246 return h('div', {
4347 classList
4448 }, [
45- messageHeader(msg, replyInfo),
49 + messageHeader(msg, replyInfo, opts.priority),
4650 h('section', [opts.content]),
4751 computed(msg.key, (key) => {
4852 if (ref.isMsg(key)) {
4953 return h('footer', [
@@ -64,9 +68,13 @@
6468 ])
6569
6670 // scoped
6771
68- function messageHeader (msg, replyInfo) {
72 + function messageHeader (msg, replyInfo, priority) {
73 + var additionalMeta = []
74 + if (opts.priority >= 2) {
75 + additionalMeta.push(h('span.flag -new', {title: 'New Message'}))
76 + }
6977 return h('header', [
7078 h('div.main', [
7179 h('a.avatar', {href: `${msg.value.author}`}, [
7280 api.about.html.image(msg.value.author)
@@ -79,9 +87,12 @@
7987 api.message.html.timestamp(msg), ' ', replyInfo
8088 ])
8189 ])
8290 ]),
83- h('div.meta', api.message.html.meta(msg))
91 + h('div.meta', [
92 + api.message.html.meta(msg),
93 + additionalMeta
94 + ])
8495 ])
8596 }
8697 }
8798 }
styles/message.mcssView
@@ -51,8 +51,12 @@
5151 }
5252 }
5353 }
5454
55 + -new {
56 + border-color: #ffe794
57 + }
58 +
5559 header {
5660 margin: 15px 15px
5761 margin-bottom: 5px;
5862 display: flex
@@ -83,16 +87,33 @@
8387 font-size: 90%
8488 }
8589 margin-left: 10px
8690 }
91 + }
8792
88- div.meta {
93 + div.meta {
8994
95 + span.flag {
96 + width: 12px
97 + height: 12px
98 +
99 + background-repeat: no-repeat
100 + background-position: center
101 + display: inline-block
102 + vertical-align: middle;
103 + margin-top: -3px;
104 +
105 + -new {
106 + background-image: svg(new)
107 + }
108 +
109 + @svg new {
110 + width: 12px
111 + height: 12px
112 + content: "<circle cx='6' stroke='none' fill='#62baff' cy='6' r='5' />"
113 + }
90114 }
91- }
92115
93- div.meta {
94-
95116 em {
96117 display: inline-block
97118 padding: 4px
98119 }

Built with git-ssb-web