git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Commit fd15190d894f51e76a7c0fd5f53b0db037f171b8

use mutual friends / followers to determine distance instead of buggy hops

also more detail about two way directional message flow.

ssbc/ssb-friends#3
Matt McKegg committed on 10/18/2017, 1:59:48 AM
Parent: 461fd4459a46469a272bb9fe6c369b1dd1c9323c

Files changed

locales/en.jsonchanged
modules/page/html/render/profile.jschanged
modules/profile/html/preview.jschanged
modules/profile/obs/hops.jsdeleted
modules/profile/obs/contact.jsadded
sbot/index.jschanged
sbot/hops.jsdeleted
locales/en.jsonView
@@ -165,6 +165,10 @@
165165 "This person is blocked by %s of your friends.": {
166166 "one": "This person is blocked by %s of your friends.",
167167 "other": "This person is blocked by %s of your friends."
168168 },
169- "Blocked by": "Blocked by"
169+ "Blocked by": "Blocked by",
170+ "However, this person should be able to see your posts.": "However, this person should be able to see your posts.",
171+ "However, since they follow someone that follows you, they should be able to see your posts.": "However, since they follow someone that follows you, they should be able to see your posts.",
172+ "They might not be able to see your posts either.": "They might not be able to see your posts either.",
173+ "However, since you follow someone that follows them, you should be able to see their latest posts.": "However, since you follow someone that follows them, you should be able to see their latest posts."
170174 }
modules/page/html/render/profile.jsView
@@ -1,8 +1,7 @@
11 var nest = require('depnest')
22 var ref = require('ssb-ref')
33 var {h, when, computed, map, send, dictToCollection, resolve} = require('mutant')
4-var extend = require('xtend')
54
65 exports.needs = nest({
76 'about.obs': {
87 name: 'first',
@@ -23,17 +22,12 @@
2322 'sheet.display': 'first',
2423 'profile.obs.rank': 'first',
2524 'profile.sheet.edit': 'first',
2625 'app.navigate': 'first',
27- 'contact.obs': {
28- followers: 'first',
29- following: 'first',
30- blockers: 'first'
31- },
26+ 'profile.obs.contact': 'first',
3227 'contact.html.followToggle': 'first',
3328 'intl.sync.i18n': 'first',
3429 'intl.sync.i18n_n': 'first',
35- 'profile.obs.hops': 'first',
3630 'sheet.profiles': 'first'
3731 })
3832 exports.gives = nest('page.html.render')
3933
@@ -41,44 +35,28 @@
4135 const i18n = api.intl.sync.i18n
4236 const plural = api.intl.sync.i18n_n
4337 return nest('page.html.render', function profile (id) {
4438 if (!ref.isFeed(id)) return
45-
39+ var yourId = api.keys.sync.id()
4640 var name = api.about.obs.name(id)
4741 var description = api.about.obs.description(id)
48- var yourId = api.keys.sync.id()
49- var yourFollows = api.contact.obs.following(yourId)
50- var yourFollowers = api.contact.obs.followers(yourId)
42+ var contact = api.profile.obs.contact(id)
5143
52- var rawFollowers = api.contact.obs.followers(id)
53- var rawFollowing = api.contact.obs.following(id)
54- var friendsLoaded = computed([rawFollowers.sync, rawFollowing.sync], (...x) => x.every(Boolean))
55-
56- var hops = api.profile.obs.hops(yourId, id)
57-
58- var friends = computed([rawFollowing, rawFollowers], (following, followers) => {
44+ var friends = computed([contact.following, contact.followers], (following, followers) => {
5945 return Array.from(following).filter(follow => followers.includes(follow))
6046 })
6147
62- var following = computed([rawFollowing, friends], (following, friends) => {
63- return Array.from(following).filter(follow => !friends.includes(follow))
48+ var following = computed([contact.following, friends], (following, friends) => {
49+ return following.filter(follow => !friends.includes(follow))
6450 })
6551
66- var followers = computed([rawFollowers, friends], (followers, friends) => {
67- return Array.from(followers).filter(follower => !friends.includes(follower))
52+ var followers = computed([contact.followers, friends], (followers, friends) => {
53+ return followers.filter(follower => !friends.includes(follower))
6854 })
6955
70- var blockers = api.contact.obs.blockers(id)
71- var youBlock = computed(blockers, function (blockers) {
72- return blockers.includes(yourId)
73- })
56+ var names = computed([api.about.obs.names(id), contact.yourFollowing, contact.following, yourId, id], filterByValues)
57+ var images = computed([api.about.obs.images(id), contact.yourFollowing, contact.following, yourId, id], filterByValues)
7458
75- var yourBlockingFriends = computed([yourFollowers, yourFollows, blockers], inAllSets)
76- var mutualFriends = computed([yourFollowers, yourFollows, rawFollowers, rawFollowing], inAllSets)
77-
78- var names = computed([api.about.obs.names(id), yourFollows, rawFollowing, yourId, id], filterByValues)
79- var images = computed([api.about.obs.images(id), yourFollows, rawFollowing, yourId, id], filterByValues)
80-
8159 var namePicker = h('div', {className: 'Picker'}, [
8260 map(dictToCollection(names), (item) => {
8361 var isSelf = computed(item.value, (ids) => ids.includes(id))
8462 var isAssigned = computed(item.value, (ids) => ids.includes(yourId))
@@ -156,44 +134,46 @@
156134 h('section -publicKey', [
157135 h('pre', {title: i18n('Public key for this profile')}, id)
158136 ]),
159137
160- computed([hops, yourBlockingFriends, youBlock], (value, yourBlockingFriends, youBlock) => {
161- if (value) {
162- if ((value[0] > 1 || youBlock) && yourBlockingFriends.length > 0) {
163- return h('section -blockWarning', [
164- h('a', {
165- href: '#',
166- 'ev-click': send(displayBlockingFriends, yourBlockingFriends)
167- }, [
168- '⚠️ ', plural('This person is blocked by %s of your friends.', yourBlockingFriends.length)
169- ])
170- ])
171- } else if (value[0] > 2 || value[0] === undefined) {
172- return h('section -distanceWarning', [
173- h('h1', i18n(`You don't follow anyone who follows this person`)),
174- h('p', i18n('You might not be seeing their latest messages. You could try joining a pub that they are a member of.'))
175- ])
176- } else if (value[1] > 2 || value[1] === undefined) {
177- return h('section -distanceWarning', [
138+ when(contact.notFollowing, [
139+ when(contact.blockingFriendsCount, h('section -blockWarning', [
140+ h('a', {
141+ href: '#',
142+ 'ev-click': send(displayBlockingFriends, contact.blockingFriends)
143+ }, [
144+ '⚠️ ', computed(['This person is blocked by %s of your friends.', contact.blockingFriendsCount], plural)
145+ ])
146+ ])),
147+
148+ when(contact.noOutgoing,
149+ h('section -distanceWarning', [
150+ h('h1', i18n(`You don't follow anyone who follows this person`)),
151+ h('p', i18n('You might not be seeing their latest messages. You could try joining a pub that they are a member of.')),
152+ when(contact.hasIncoming,
153+ h('p', i18n('However, since they follow someone that follows you, they should be able to see your posts.')),
154+ h('p', i18n(`They might not be able to see your posts either.`))
155+ )
156+ ]),
157+ when(contact.noIncoming,
158+ h('section -distanceWarning', [
178159 h('h1', i18n('This person does not follow anyone that follows you')),
179- h('p', i18n('They might not receive your private messages or replies. You could try joining a pub that they are a member of.'))
180- ])
181- } else if (value[0] === 2) {
182- return h('section -mutualFriends', [
183- h('a', {
184- href: '#',
185- 'ev-click': send(displayMutualFriends, mutualFriends)
186- }, [
187- '👥 ',
188- computed(mutualFriends, (items) => {
189- return plural('You share %s mutual friends with this person.', items.length)
190- })
160+ h('p', i18n('They might not receive your private messages or replies. You could try joining a pub that they are a member of.')),
161+ h('p', i18n('However, since you follow someone that follows them, you should be able to see their latest posts.'))
162+ ]),
163+ when(contact.mutualFriendsCount,
164+ h('section -mutualFriends', [
165+ h('a', {
166+ href: '#',
167+ 'ev-click': send(displayMutualFriends, contact.mutualFriends)
168+ }, [
169+ '👥 ', computed(['You share %s mutual friends with this person.', contact.mutualFriendsCount], plural)
170+ ])
191171 ])
192- ])
193- }
194- }
195- }),
172+ )
173+ )
174+ )
175+ ]),
196176
197177 h('section -description', [
198178 computed(description, (text) => {
199179 if (typeof text === 'string') {
@@ -207,9 +187,9 @@
207187
208188 var feedView = api.feed.html.rollup(api.feed.pull.profile(id), {
209189 prepend,
210190 displayFilter: (msg) => msg.value.author === id,
211- rootFilter: (msg) => !youBlock(),
191+ rootFilter: (msg) => !contact.youBlock(),
212192 bumpFilter: (msg) => msg.value.author === id
213193 })
214194
215195 var container = h('div', {className: 'SplitView'}, [
@@ -217,22 +197,22 @@
217197 feedView
218198 ]),
219199 h('div.side.-right', [
220200 h('button PrivateMessageButton', {'ev-click': () => api.app.navigate('/private', {compose: {to: id}})}, i18n('Send Private Message')),
221- when(friendsLoaded,
201+ when(contact.sync,
222202 h('div', [
223- renderContactBlock(i18n('Friends'), friends, yourFollows),
224- renderContactBlock(i18n('Followers'), followers, yourFollows),
225- renderContactBlock(i18n('Following'), following, yourFollows),
226- renderContactBlock(i18n('Blocked by'), yourBlockingFriends, yourFollows)
203+ renderContactBlock(i18n('Friends'), friends, contact.yourFollowing),
204+ renderContactBlock(i18n('Followers'), followers, contact.yourFollowing),
205+ renderContactBlock(i18n('Following'), following, contact.yourFollowing),
206+ renderContactBlock(i18n('Blocked by'), contact.blockingFriends, contact.yourFollowing)
227207 ]),
228208 h('div', {className: 'Loading'})
229209 )
230210 ])
231211 ])
232212
233213 // refresh feed (to hide all posts) when blocked
234- youBlock(feedView.reload)
214+ contact.youBlock(feedView.reload)
235215
236216 container.pendingUpdates = feedView.pendingUpdates
237217 container.reload = feedView.reload
238218 return container
@@ -245,17 +225,17 @@
245225 function displayBlockingFriends (profiles) {
246226 api.sheet.profiles(profiles, i18n('Blocked by'))
247227 }
248228
249- function renderContactBlock (title, profiles, yourFollows) {
229+ function renderContactBlock (title, profiles, yourFollowing) {
250230 profiles = api.profile.obs.rank(profiles)
251231 return [
252232 when(computed(profiles, x => x.length), h('h2', title)),
253233 h('div', {
254234 classList: 'ProfileList'
255235 }, [
256236 map(profiles, (id) => {
257- var following = computed(yourFollows, f => f.includes(id))
237+ var following = computed(yourFollowing, f => f.includes(id))
258238 return h('a.profile', {
259239 href: id,
260240 classList: [
261241 when(following, '-following')
@@ -361,8 +341,4 @@
361341 }
362342 return result
363343 }, {})
364344 }
365-
366-function inAllSets (first, ...rest) {
367- return first.filter(value => rest.every((collection) => collection.includes(value)))
368-}
modules/profile/html/preview.jsView
@@ -1,6 +1,7 @@
11 var nest = require('depnest')
22 var h = require('mutant/h')
3+var when = require('mutant/when')
34 var computed = require('mutant/computed')
45 var send = require('mutant/send')
56
67 exports.needs = nest({
@@ -8,20 +9,13 @@
89 'about.html.image': 'first',
910 'keys.sync.id': 'first',
1011 'sheet.display': 'first',
1112 'app.navigate': 'first',
12- 'contact.obs': {
13- followers: 'first',
14- following: 'first',
15- blockers: 'first'
16- },
17- 'contact.async.block': 'first',
18- 'contact.async.unblock': 'first',
1913 'intl.sync.i18n': 'first',
2014 'intl.sync.i18n_n': 'first',
21- 'profile.obs.hops': 'first',
2215 'sheet.profiles': 'first',
23- 'contact.html.followToggle': 'first'
16+ 'contact.html.followToggle': 'first',
17+ 'profile.obs.contact': 'first'
2418 })
2519
2620 exports.gives = nest('profile.html.preview')
2721
@@ -30,23 +24,10 @@
3024 const plural = api.intl.sync.i18n_n
3125
3226 return nest('profile.html.preview', function (id) {
3327 var name = api.about.obs.name(id)
34- var yourId = api.keys.sync.id()
35- var yourFollows = api.contact.obs.following(yourId)
36- var yourFollowers = api.contact.obs.followers(yourId)
28+ var contact = api.profile.obs.contact(id)
3729
38- var rawFollowers = api.contact.obs.followers(id)
39- var rawFollowing = api.contact.obs.following(id)
40- var hops = api.profile.obs.hops(yourId, id)
41-
42- var blockers = api.contact.obs.blockers(id)
43- var youBlock = computed(blockers, function (blockers) {
44- return blockers.includes(yourId)
45- })
46- var yourBlockingFriends = computed([yourFollowers, yourFollows, blockers], inAllSets)
47- var mutualFriends = computed([yourFollowers, yourFollows, rawFollowers, rawFollowing], inAllSets)
48-
4930 return h('ProfilePreview', [
5031 h('header', [
5132 h('div.image', api.about.html.image(id)),
5233 h('div.main', [
@@ -62,45 +43,45 @@
6243 h('pre', {title: i18n('Public key for this profile')}, id)
6344 ])
6445 ])
6546 ]),
66- computed([hops, yourBlockingFriends, youBlock], (value, yourBlockingFriends, youBlock) => {
67- if (value) {
68- if ((value[0] > 1 || youBlock) && yourBlockingFriends.length > 0) {
69- return h('section -blockWarning', [
70- h('a', {
71- href: '#',
72- 'ev-click': send(displayBlockingFriends, yourBlockingFriends)
73- }, [
74- '⚠️ ', plural('This person is blocked by %s of your friends.', yourBlockingFriends.length)
75- ])
47+
48+ when(contact.isYou, h('section -you', [
49+ i18n('This is you.')
50+ ])),
51+
52+ when(contact.notFollowing, [
53+ when(contact.blockingFriendsCount,
54+ h('section -blockWarning', [
55+ h('a', {
56+ href: '#',
57+ 'ev-click': send(displayBlockingFriends, contact.blockingFriends)
58+ }, [
59+ '⚠️ ', computed(['This person is blocked by %s of your friends.', contact.blockingFriendsCount], plural)
7660 ])
77- } else if (value[0] > 2 || value[0] === undefined) {
78- return h('section -distanceWarning', [
61+ ]),
62+ when(contact.noOutgoing,
63+ h('section -distanceWarning', [
7964 '⚠️ ', i18n(`You don't follow anyone who follows this person`)
80- ])
81- } else if (value[1] > 2 || value[1] === undefined) {
82- return h('section -distanceWarning', [
83- '⚠️ ', i18n('This person does not follow anyone that follows you')
84- ])
85- } else if (value[0] === 2) {
86- return h('section -mutualFriends', [
87- h('a', {
88- href: '#',
89- 'ev-click': send(displayMutualFriends, mutualFriends)
90- }, [
91- computed(mutualFriends, (items) => {
92- return plural('You share %s mutual friends with this person.', items.length)
93- })
94- ])
95- ])
96- } else if (value[0] === 0) {
97- return h('section -you', [
98- i18n('This is you.')
99- ])
100- }
101- }
102- })
65+ ]),
66+ when(contact.noIncoming,
67+ h('section -distanceWarning', [
68+ '⚠️ ', i18n('This person does not follow anyone that follows you')
69+ ]),
70+ when(contact.mutualFriendsCount,
71+ h('section -mutualFriends', [
72+ h('a', {
73+ href: '#',
74+ 'ev-click': send(displayMutualFriends, contact.mutualFriends)
75+ }, [
76+ '👥 ', computed(['You share %s mutual friends with this person.', contact.mutualFriendsCount], plural)
77+ ])
78+ ])
79+ )
80+ )
81+ )
82+ )
83+ ])
10384 ])
10485 })
10586
10687 function displayMutualFriends (profiles) {
@@ -110,8 +91,4 @@
11091 function displayBlockingFriends (profiles) {
11192 api.sheet.profiles(profiles, i18n('Blocked by'))
11293 }
11394 }
114-
115-function inAllSets (first, ...rest) {
116- return first.filter(value => rest.every((collection) => collection.includes(value)))
117-}
modules/profile/obs/hops.jsView
@@ -1,60 +1,0 @@
1-var Value = require('mutant/value')
2-var computed = require('mutant/computed')
3-var Sustained = require('../../../lib/sustained')
4-var nest = require('depnest')
5-
6-exports.needs = nest({
7- 'profile.obs.recentlyUpdated': 'first',
8- 'contact.obs.following': 'first',
9- 'sbot.obs.connection': 'first'
10-})
11-
12-exports.gives = nest('profile.obs.hops')
13-
14-exports.create = function (api) {
15- return nest('profile.obs.hops', function (from, to) {
16- // create observable value that refreshes hops whenever friend graph changes
17- // and has stabilized for more than 500 ms
18- // also only watches if observable is currently being observed
19-
20- var value = Value()
21- var updates = IncrementableValue()
22- var releases = []
23-
24- var fromFollowing = api.contact.obs.following(from)
25- var toFollowing = api.contact.obs.following(to)
26-
27- return computed([value], (value) => value, {
28- onListen: function () {
29- releases.push(fromFollowing(updates.increment))
30- releases.push(toFollowing(updates.increment))
31- releases.push(api.sbot.obs.connection(updates.increment))
32- releases.push(Sustained(updates, 500)(refresh))
33- refresh()
34- },
35- onUnlisten: function () {
36- while (releases.length) {
37- releases.pop()()
38- }
39- }
40- })
41-
42- // scoped
43- function refresh () {
44- if (api.sbot.obs.connection()) {
45- api.sbot.obs.connection().patchwork.getHops({from, to}, (err, result) => {
46- if (err) return console.log(err)
47- value.set(result)
48- })
49- }
50- }
51- })
52-}
53-
54-function IncrementableValue () {
55- var value = Value(0)
56- value.increment = function () {
57- value.set((value() || 0) + 1)
58- }
59- return value
60-}
modules/profile/obs/contact.jsView
@@ -1,0 +1,79 @@
1+var computed = require('mutant/computed')
2+var nest = require('depnest')
3+
4+exports.needs = nest({
5+ 'keys.sync.id': 'first',
6+ 'contact.obs': {
7+ followers: 'first',
8+ following: 'first',
9+ blockers: 'first'
10+ }
11+})
12+
13+exports.gives = nest('profile.obs.contact')
14+
15+exports.create = function (api) {
16+ return nest('profile.obs.contact', function (id) {
17+ var yourId = api.keys.sync.id()
18+ var yourFollowing = api.contact.obs.following(yourId)
19+ var yourFollowers = api.contact.obs.followers(yourId)
20+
21+ var followers = api.contact.obs.followers(id)
22+ var following = api.contact.obs.following(id)
23+ var sync = computed([followers.sync, following.sync], (...x) => x.every(Boolean))
24+
25+ var blockers = api.contact.obs.blockers(id)
26+ var youBlock = computed(blockers, function (blockers) {
27+ return blockers.includes(yourId)
28+ })
29+
30+ var youFollow = computed([yourFollowing], function (yourFollowing) {
31+ return yourFollowing.includes(id)
32+ })
33+
34+ var blockingFriends = computed([yourFollowers, yourFollowing, blockers], inAllSets)
35+ var mutualFriends = computed([yourFollowers, yourFollowing, followers, following], inAllSets)
36+ var hasOutgoing = computed([yourFollowers, following], (a, b) => {
37+ return a.some((id) => b.includes(id))
38+ })
39+ var hasIncoming = computed([followers, yourFollowing], (a, b) => {
40+ return a.some((id) => b.includes(id))
41+ })
42+
43+ var isYou = computed([yourId, id], (a, b) => a === b)
44+
45+ return {
46+ followers,
47+ following,
48+ blockers,
49+ blockingFriends,
50+ blockingFriendsCount: count(blockingFriends),
51+ mutualFriends,
52+ mutualFriendsCount: count(mutualFriends),
53+ hasOutgoing,
54+ noOutgoing: not(hasOutgoing, isYou),
55+ hasIncoming,
56+ noIncoming: not(hasIncoming, isYou),
57+ yourId,
58+ yourFollowing,
59+ yourFollowers,
60+ youFollow,
61+ youBlock,
62+ isYou,
63+ notFollowing: not(youFollow, isYou),
64+ sync
65+ }
66+ })
67+}
68+
69+function inAllSets (first, ...rest) {
70+ return first.filter(value => rest.every((collection) => collection.includes(value)))
71+}
72+
73+function not (obs, isFalse) {
74+ return computed([obs, isFalse], (x, isFalse) => isFalse ? false : !x)
75+}
76+
77+function count (obs) {
78+ return computed(obs, (x) => x.length)
79+}
sbot/index.jsView
@@ -5,9 +5,8 @@
55 var Progress = require('./progress')
66 var Search = require('./search')
77 var RecentFeeds = require('./recent-feeds')
88 var LiveBacklinks = require('./live-backlinks')
9-var Hops = require('./hops')
109
1110 exports.name = 'patchwork'
1211 exports.version = require('../package.json').version
1312 exports.manifest = {
@@ -21,9 +20,8 @@
2120 heartbeat: 'source',
2221
2322 getSubscriptions: 'async',
2423 getChannels: 'async',
25- getHops: 'async',
2624
2725 liveBacklinks: {
2826 subscribe: 'sync',
2927 unsubscribe: 'sync',
@@ -49,8 +47,7 @@
4947 recentFeeds: recentFeeds.stream,
5048 linearSearch: search.linear,
5149 getSubscriptions: subscriptions.get,
5250 getChannels: channels.get,
53- liveBacklinks: LiveBacklinks(ssb, config),
54- getHops: Hops(ssb, config)
51+ liveBacklinks: LiveBacklinks(ssb, config)
5552 }
5653 }
sbot/hops.jsView
@@ -1,11 +1,0 @@
1-module.exports = function (ssb, config) {
2- return function getHops ({from, to}, cb) {
3- ssb.friends.hops(from, (err, result) => {
4- if (err) return cb(err)
5- ssb.friends.hops(to, (err, reverseResult) => {
6- if (err) return cb(err)
7- cb(null, [result[to], reverseResult[from]])
8- })
9- })
10- }
11-}

Built with git-ssb-web