Files: f50400a95d8c5a977f0f39ee8b857c5ee9cdd630 / lib / depject / contact / html / follow-toggle.js
6620 bytesRaw
1 | const nest = require('depnest') |
2 | const electron = require('electron') |
3 | const { h, when, computed } = require('mutant') |
4 | |
5 | exports.gives = nest('contact.html.followToggle') |
6 | exports.needs = nest({ |
7 | 'intl.sync.i18n': 'first', |
8 | 'keys.sync.id': 'first', |
9 | 'message.async.publish': 'first', |
10 | 'sbot.async.publish': 'first', |
11 | 'contact.obs.states': 'first', |
12 | 'contact.obs.followers': 'first', |
13 | 'contact.obs.blockers': 'first', |
14 | 'contact.obs.ignores': 'first' |
15 | }) |
16 | |
17 | exports.create = function (api) { |
18 | const i18n = api.intl.sync.i18n |
19 | return nest('contact.html.followToggle', function (id, opts) { |
20 | const yourId = api.keys.sync.id() |
21 | |
22 | const states = api.contact.obs.states(yourId) |
23 | const yourFollowers = api.contact.obs.followers(yourId) |
24 | const ignores = api.contact.obs.ignores() |
25 | |
26 | const followsYou = computed([yourFollowers], function (yourFollowers) { |
27 | return yourFollowers.includes(id) |
28 | }) |
29 | |
30 | const youIgnore = computed([ignores], function (ignores) { |
31 | return !!ignores[id] |
32 | }) |
33 | |
34 | const youListening = computed([ignores], function (ignores) { |
35 | return ignores[id] === false |
36 | }) |
37 | |
38 | const youFollow = computed([states], function (states) { |
39 | return states[id] === true |
40 | }) |
41 | |
42 | const youBlock = computed([states], function (states) { |
43 | return states[id] === false |
44 | }) |
45 | |
46 | const isFriends = computed([followsYou, youFollow], function (a, b) { |
47 | return a && b |
48 | }) |
49 | |
50 | const ignoreText = when(youIgnore, ' ' + i18n('(ignored)')) |
51 | const listeningText = when(youListening, ' ' + i18n('(listening)')) |
52 | |
53 | const showBlockButton = computed([opts && opts.block], (block) => block !== false) |
54 | |
55 | if (id !== yourId) { |
56 | return [ |
57 | when(youBlock, [ |
58 | h('a ToggleButton -unblocking', { |
59 | href: '#', |
60 | title: i18n('Click to unblock'), |
61 | 'ev-click': () => setStatus(id, null, { states, ignores }) |
62 | }, [i18n('Blocked'), listeningText]) |
63 | ], [ |
64 | when(youFollow, |
65 | h('a ToggleButton -unsubscribe', { |
66 | href: '#', |
67 | title: i18n('Click to unfollow'), |
68 | 'ev-click': () => setStatus(id, null, { states, ignores }) |
69 | }, [when(isFriends, i18n('Friends'), i18n('Following')), ignoreText]), |
70 | h('a ToggleButton -subscribe', { |
71 | href: '#', |
72 | 'ev-click': () => setStatus(id, true, { states, ignores }) |
73 | }, [when(followsYou, i18n('Follow Back'), i18n('Follow')), ignoreText]) |
74 | ) |
75 | ]), |
76 | when(showBlockButton, h('a ToggleButton -drop -options', { |
77 | href: '#', |
78 | title: i18n('Click for options to block syncing with this person and/or hide their posts'), |
79 | 'ev-click': (ev) => popupContactMenu(ev.currentTarget, id, { states, ignores }) |
80 | }, i18n('Options'))) |
81 | ] |
82 | } else { |
83 | return [] |
84 | } |
85 | }) |
86 | |
87 | function popupContactMenu (element, id, { states, ignores }) { |
88 | const rects = element.getBoundingClientRect() |
89 | const status = states()[id] |
90 | const ignoring = ignores()[id] |
91 | |
92 | // the actual listening state (use the explicit ignore if available, otherwise depends if blocking) |
93 | const resolvedIgnoring = ignoring != null |
94 | ? ignoring |
95 | : status === false |
96 | |
97 | const factor = electron.remote.getCurrentWindow().webContents.getZoomFactor() |
98 | const menu = electron.remote.Menu.buildFromTemplate([ |
99 | { |
100 | type: 'radio', |
101 | label: i18n('Neutral'), |
102 | checked: status == null, |
103 | click: () => setStatus(id, null, { states, ignores }) |
104 | }, |
105 | { |
106 | type: 'radio', |
107 | label: i18n('Follow'), |
108 | checked: status === true, |
109 | click: () => setStatus(id, true, { states, ignores }) |
110 | }, |
111 | { |
112 | type: 'radio', |
113 | label: i18n('Block'), |
114 | checked: status === false, |
115 | click: () => setStatus(id, false, { states, ignores }) |
116 | }, |
117 | { type: 'separator' }, |
118 | { |
119 | type: 'radio', |
120 | label: i18n('Listen'), |
121 | checked: !resolvedIgnoring, |
122 | click: () => setIgnore(id, false, { states, ignores }) |
123 | }, |
124 | { |
125 | type: 'radio', |
126 | label: i18n('Ignore'), |
127 | checked: resolvedIgnoring, |
128 | click: () => setIgnore(id, true, { states, ignores }) |
129 | } |
130 | ]) |
131 | menu.popup({ |
132 | window: electron.remote.getCurrentWindow(), |
133 | x: Math.round(rects.left * factor), |
134 | y: Math.round(rects.bottom * factor) + 4 |
135 | }) |
136 | } |
137 | |
138 | function setStatus (id, status, { states, ignores }) { |
139 | const currentStatus = states()[id] |
140 | const currentIgnoring = ignores()[id] |
141 | |
142 | if (!looseMatch(status, currentStatus)) { |
143 | const message = { |
144 | type: 'contact', |
145 | contact: id |
146 | } |
147 | |
148 | if (status === true) { // FOLLOW |
149 | message.following = true |
150 | } else if (status === false) { // BLOCK |
151 | message.blocking = true |
152 | } else if (currentStatus === true) { // UNFOLLOW |
153 | message.following = false |
154 | } else if (currentStatus === false) { // UNBLOCK |
155 | message.blocking = false |
156 | } |
157 | |
158 | api.message.async.publish(message, (err) => { |
159 | if (!err && currentIgnoring && status !== false) { |
160 | // if we are currently ignoring (private blocking) |
161 | // renew the action for ssb-friends compatibility |
162 | // unless this is a block action |
163 | api.sbot.async.publish({ |
164 | recps: [api.keys.sync.id()], |
165 | type: 'contact', |
166 | contact: id, |
167 | blocking: true |
168 | }) |
169 | } |
170 | }) |
171 | } |
172 | } |
173 | |
174 | function setIgnore (id, ignoring, { states, ignores }) { |
175 | const currentStatus = states()[id] |
176 | const currentIgnoring = ignores()[id] |
177 | const yourId = api.keys.sync.id() |
178 | |
179 | if (!looseMatch(ignoring, currentIgnoring)) { |
180 | if (ignoring === false && currentStatus === false) { |
181 | // user is publicly blocking, but wants to still see this feed |
182 | api.sbot.async.publish({ |
183 | recps: [yourId], |
184 | type: 'contact', |
185 | blocking: false, |
186 | following: false, |
187 | contact: id |
188 | }) |
189 | } else if (ignoring === true) { |
190 | // user wants to ignore (privately block) this feed |
191 | api.sbot.async.publish({ |
192 | recps: [yourId], |
193 | type: 'contact', |
194 | blocking: true, |
195 | contact: id |
196 | }) |
197 | } else { |
198 | // user wants to stop ignoring this feed (remove ignore) |
199 | api.sbot.async.publish({ |
200 | recps: [yourId], |
201 | type: 'contact', |
202 | contact: id, |
203 | following: currentStatus === true |
204 | }) |
205 | } |
206 | } |
207 | } |
208 | } |
209 | |
210 | function looseMatch (a, b) { |
211 | return a === b || (a == null && b == null) |
212 | } |
213 |
Built with git-ssb-web