Files: 2b05be8ee023fdd9580d6a845ba074a425756685 / contact / html / relationships.js
4982 bytesRaw
1 | const nest = require('depnest') |
2 | const { h, map, computed, when, Value } = require('mutant') |
3 | |
4 | exports.gives = nest('contact.html.relationships') |
5 | |
6 | exports.needs = nest({ |
7 | // 'about.html.image': 'first', |
8 | 'about.html.avatar': 'first', |
9 | // 'about.obs.name': 'first', |
10 | 'contact.async.follow': 'first', |
11 | 'contact.async.unfollow': 'first', |
12 | 'contact.async.block': 'first', |
13 | 'contact.async.unblock': 'first', |
14 | 'contact.obs.followers': 'first', |
15 | 'contact.obs.following': 'first', |
16 | 'contact.obs.blockers': 'first', |
17 | 'contact.obs.blocking': 'first', |
18 | 'keys.sync.id': 'first' |
19 | }) |
20 | |
21 | exports.create = function (api) { |
22 | return nest({ |
23 | 'contact.html.relationships': relationships |
24 | }) |
25 | |
26 | function relationships (feedId) { |
27 | const rawFollowing = api.contact.obs.following(feedId) |
28 | const rawFollowers = api.contact.obs.followers(feedId) |
29 | |
30 | // mix: TODO rework this |
31 | const friends = computed([rawFollowing, rawFollowers], (following, followers) => { |
32 | return [...following].filter(follow => followers.includes(follow)) |
33 | }) |
34 | const following = computed([rawFollowing, friends], (following, friends) => { |
35 | return [...following].filter(follow => !friends.includes(follow)) |
36 | }) |
37 | const followers = computed([rawFollowers, friends], (followers, friends) => { |
38 | return [...followers].filter(follower => !friends.includes(follower)) |
39 | }) |
40 | const blockers = api.contact.obs.blockers(feedId) |
41 | const blocking = api.contact.obs.blocking(feedId) |
42 | |
43 | const modes = [ |
44 | { label: 'Friends', data: friends }, |
45 | { label: 'Follows', data: following }, |
46 | { label: 'Followers', data: followers }, |
47 | { label: 'Blocked by', data: blockers, hideEmpty: true }, |
48 | { label: 'Blocking', data: blocking, hideEmpty: true } |
49 | ] |
50 | const mode = Value(0) |
51 | const setMode = (i) => { |
52 | if (mode() === i) mode.set() |
53 | else mode.set(i) |
54 | } |
55 | |
56 | return h('Relationships', [ |
57 | h('header', 'Relationships'), |
58 | RelationshipStatus({ feedId, rawFollowing, blockers, api }), |
59 | |
60 | h('div.groups', [ |
61 | h('div.tabs', modes.map(({ label, data, hideEmpty }, i) => { |
62 | return computed([data, mode], (d, mode) => { |
63 | if (hideEmpty && !d.length) return |
64 | |
65 | return h('div.tab', |
66 | { |
67 | className: mode === i ? '-active' : '', |
68 | 'ev-click': () => setMode(i) |
69 | }, |
70 | [ |
71 | h('div.label', label), |
72 | h('div.count', d.length > 50 ? '50+' : d.length) |
73 | ] |
74 | ) |
75 | }) |
76 | })), |
77 | h('div.group', computed(mode, i => { |
78 | if (i === null) return |
79 | |
80 | const { data } = modes[i] |
81 | // NOTE - there's a race condition where putting a breakpoint here |
82 | // gives the cache time to load or something? |
83 | // INTERMITENT BUG perhaps with api.contacts.obs ? |
84 | return map(data, api.about.html.avatar) |
85 | })) |
86 | ]) |
87 | ]) |
88 | } |
89 | } |
90 | |
91 | function RelationshipStatus ({ feedId, rawFollowing, blockers, api }) { |
92 | const myId = api.keys.sync.id() |
93 | if (feedId === myId) return |
94 | |
95 | // mix: TODO oh lord this is ugly, refactor it ! |
96 | const ImFollowing = api.contact.obs.following(myId) |
97 | const IFollowThem = computed([ImFollowing], ImFollowing => ImFollowing.includes(feedId)) |
98 | const theyFollowMe = computed([rawFollowing], following => following.includes(myId)) |
99 | const ImBlockingThem = computed(blockers, blockers => blockers.includes(myId)) |
100 | |
101 | const relationshipStatus = computed([IFollowThem, theyFollowMe], (IFollowThem, theyFollowMe) => { |
102 | return IFollowThem && theyFollowMe ? '- you are friends' |
103 | : IFollowThem ? '- you follow them' |
104 | : theyFollowMe ? '- they follow you' |
105 | : '' |
106 | }) |
107 | const { unfollow, follow, block, unblock } = api.contact.async |
108 | |
109 | return h('div.relationship-status', [ |
110 | h('section -friendship', [ |
111 | when(ImFollowing.sync, |
112 | when(IFollowThem, |
113 | h('button', { 'ev-click': () => unfollow(feedId) }, 'Unfollow'), |
114 | h('button', { 'ev-click': () => follow(feedId) }, 'Follow') |
115 | ), |
116 | h('button', { disabled: 'disabled' }, 'Loading...') |
117 | ), |
118 | when(ImFollowing.sync, h('div.relationship-status', relationshipStatus)) |
119 | ]), |
120 | h('section -blocking', [ |
121 | when(ImBlockingThem, |
122 | h('button -subtle', { 'ev-click': () => unblock(feedId, console.log) }, [ h('i.fa.fa-ban'), 'unblock' ]), |
123 | h('button -subtle', { 'ev-click': () => block(feedId, console.log) }, [ h('i.fa.fa-ban'), 'BLOCK' ]) |
124 | ), |
125 | h('div.explainer', [ |
126 | "Blocking tells everyone you don't want to communicate with a person.", |
127 | h('ul', [ |
128 | h('li', 'You will no longer receive messages from this person'), |
129 | h('li', "This person won't get any new information about you (including this block)"), |
130 | h('li', "Your followers will see you have blocked this person - their apps need to know so that they don't pass your information on.") |
131 | ]) |
132 | ]) |
133 | ]) |
134 | ]) |
135 | } |
136 |
Built with git-ssb-web