Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 1 | import {waitFor} from 'poll-until-promise'; |
| 2 | |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 3 | import {CCApi} from '../../common/api.js'; |
| 4 | import {parseUrl} from '../../common/commonUtils.js'; |
Adrià Vilanova Martínez | 8300cc4 | 2024-05-11 12:42:52 +0200 | [diff] [blame] | 5 | import PartialOptionsWatcher from '../../common/partialOptionsWatcher.js'; |
avm99963 | 2485a3e | 2021-09-08 22:18:38 +0200 | [diff] [blame] | 6 | import {createPlainTooltip} from '../../common/tooltip.js'; |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 7 | |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 8 | import AvatarsDB from './utils/AvatarsDB.js' |
| 9 | |
| 10 | export default class AvatarsHandler { |
| 11 | constructor() { |
| 12 | this.isFilterSetUp = false; |
| 13 | this.privateForums = []; |
| 14 | this.db = new AvatarsDB(); |
Adrià Vilanova Martínez | 8300cc4 | 2024-05-11 12:42:52 +0200 | [diff] [blame] | 15 | this.optionsWatcher = new PartialOptionsWatcher(['threadlistavatars']); |
Adrià Vilanova Martínez | d269c62 | 2021-09-04 18:35:55 +0200 | [diff] [blame] | 16 | |
| 17 | // Preload whether the option is enabled or not. This is because in the case |
| 18 | // avatars should be injected, if we don't preload this the layout will |
| 19 | // shift when injecting the first avatar. |
Adrià Vilanova Martínez | d03e39d | 2022-01-15 18:23:51 +0100 | [diff] [blame] | 20 | this.isEnabled().then(isEnabled => { |
Adrià Vilanova Martínez | d269c62 | 2021-09-04 18:35:55 +0200 | [diff] [blame] | 21 | if (isEnabled) |
| 22 | document.body.classList.add('TWPT-threadlistavatars-enabled'); |
| 23 | }); |
Adrià Vilanova Martínez | d03e39d | 2022-01-15 18:23:51 +0100 | [diff] [blame] | 24 | } |
| 25 | |
| 26 | // Returns a promise resolving to whether the threadlistavatars feature is |
| 27 | // enabled. |
| 28 | isEnabled() { |
Adrià Vilanova Martínez | 6e4f9c7 | 2022-01-24 23:27:11 +0100 | [diff] [blame] | 29 | return this.optionsWatcher.isEnabled('threadlistavatars'); |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 30 | } |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 31 | |
| 32 | // Gets a list of private forums. If it is already cached, the cached list is |
| 33 | // returned; otherwise it is also computed and cached. |
| 34 | getPrivateForums() { |
| 35 | return new Promise((resolve, reject) => { |
| 36 | if (this.isFilterSetUp) return resolve(this.privateForums); |
| 37 | |
| 38 | if (!document.documentElement.hasAttribute('data-startup')) |
| 39 | return reject('[threadListAvatars] Couldn\'t get startup data.'); |
| 40 | |
| 41 | var startupData = |
| 42 | JSON.parse(document.documentElement.getAttribute('data-startup')); |
| 43 | var forums = startupData?.['1']?.['2']; |
| 44 | if (forums === undefined) |
| 45 | return reject( |
| 46 | '[threadListAvatars] Couldn\'t retrieve forums from startup data.'); |
| 47 | |
| 48 | for (var f of forums) { |
| 49 | var forumId = f?.['2']?.['1']?.['1']; |
| 50 | var forumVisibility = f?.['2']?.['18']; |
| 51 | if (forumId === undefined || forumVisibility === undefined) { |
| 52 | console.warn( |
| 53 | '[threadListAvatars] Coudln\'t retrieve forum id and/or forum visibility for the following forum:', |
| 54 | f); |
| 55 | continue; |
| 56 | } |
| 57 | |
| 58 | // forumVisibility's value 1 means "PUBLIC". |
| 59 | if (forumVisibility != 1) this.privateForums.push(forumId); |
| 60 | } |
| 61 | |
| 62 | // Forum 51488989 is marked as public but it is in fact private. |
| 63 | this.privateForums.push('51488989'); |
| 64 | |
| 65 | this.isFilterSetUp = true; |
| 66 | return resolve(this.privateForums); |
| 67 | }); |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 68 | } |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 69 | |
| 70 | // Some threads belong to private forums, and this feature will not be able to |
| 71 | // get its avatars since it makes an anonymomus call to get the contents of |
| 72 | // the thread. |
| 73 | // |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 74 | // This function returns whether the thread belongs to a known private forum. |
| 75 | isPrivateThread(thread) { |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 76 | return this.getPrivateForums().then(privateForums => { |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 77 | if (privateForums.includes(thread.forum)) return true; |
Adrià Vilanova Martínez | c41edf4 | 2021-07-18 02:06:55 +0200 | [diff] [blame] | 78 | |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 79 | return this.db.isForumUnauthorized(thread.forum); |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 80 | }); |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 81 | } |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 82 | |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 83 | // Get an object with the author of the thread, an array of the first |num| |
| 84 | // replies from the thread |thread|, and additional information about the |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 85 | // thread. |
| 86 | // |
| 87 | // It also returns |state| which can be 'ok', 'private' or 'notVisible'. If it |
| 88 | // is 'private' or 'notVisible', the previous properties will be missing. |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 89 | getFirstMessages(thread, num = 15) { |
| 90 | return CCApi( |
| 91 | 'ViewThread', { |
| 92 | 1: thread.forum, |
| 93 | 2: thread.thread, |
| 94 | // options |
| 95 | 3: { |
| 96 | // pagination |
| 97 | 1: { |
| 98 | 2: num, // maxNum |
| 99 | }, |
| 100 | 3: true, // withMessages |
| 101 | 5: true, // withUserProfile |
| 102 | 10: false, // withPromotedMessages |
| 103 | 16: false, // withThreadNotes |
| 104 | 18: true, // sendNewThreadIfMoved |
Adrià Vilanova Martínez | 4b6c1cb | 2022-09-30 13:54:18 +0200 | [diff] [blame] | 105 | 23: true, // withFlattenedMessages |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 106 | } |
| 107 | }, |
| 108 | // |authentication| is false because otherwise this would mark |
| 109 | // the thread as read as a side effect, and that would mark all |
| 110 | // threads in the list as read. |
| 111 | // |
| 112 | // Due to the fact that we have to call this endpoint |
| 113 | // anonymously, this means we can't retrieve information about |
| 114 | // threads in private forums. |
Adrià Vilanova Martínez | c41edf4 | 2021-07-18 02:06:55 +0200 | [diff] [blame] | 115 | /* authentication = */ false, /* authuser = */ 0, |
| 116 | /* returnUnauthorizedStatus = */ true) |
| 117 | .then(response => { |
| 118 | if (response.unauthorized) |
| 119 | return this.db.putUnauthorizedForum(thread.forum).then(() => { |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 120 | return { |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 121 | state: 'private', |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 122 | }; |
Adrià Vilanova Martínez | c41edf4 | 2021-07-18 02:06:55 +0200 | [diff] [blame] | 123 | }); |
| 124 | |
| 125 | var data = response.body; |
| 126 | |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 127 | var numMessages = data?.['1']?.['8']; |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 128 | if (numMessages === undefined) { |
| 129 | if (data?.['1']?.['10'] === false) { |
| 130 | return { |
| 131 | state: 'notVisible', |
| 132 | }; |
| 133 | } else { |
| 134 | throw new Error( |
| 135 | 'Request to view thread doesn\'t include the number of messages'); |
| 136 | } |
| 137 | } |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 138 | |
| 139 | var messages = numMessages == 0 ? [] : data?.['1']['3']; |
| 140 | if (messages === undefined) |
| 141 | throw new Error( |
| 142 | 'numMessages was ' + numMessages + |
| 143 | ' but the response didn\'t include any message.'); |
| 144 | |
| 145 | var author = data?.['1']?.['4']; |
| 146 | if (author === undefined) |
| 147 | throw new Error( |
| 148 | 'Author isn\'t included in the ViewThread response.'); |
| 149 | |
| 150 | return { |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 151 | state: 'ok', |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 152 | messages, |
| 153 | author, |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 154 | |
| 155 | // The following fields are useful for the cache and can be |
| 156 | // undefined, but this is checked before adding an entry to the |
| 157 | // cache. |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 158 | lastMessageId: data?.['1']?.['2']?.['10'], |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 159 | }; |
Adrià Vilanova Martínez | 96382a9 | 2022-04-03 20:48:36 +0200 | [diff] [blame] | 160 | }) |
| 161 | .catch(cause => { |
| 162 | throw new Error('Failed ViewThread request.', {cause}); |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 163 | }); |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 164 | } |
| 165 | |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 166 | // Get the following data: |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 167 | // - |state|: the state of the request (can be 'ok', 'private' or |
| 168 | // 'notVisible'). |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 169 | // - |avatars|: a list of at most |num| avatars for thread |thread| by calling |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 170 | // the API, if |state| is 'ok'. |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 171 | getVisibleAvatarsFromServer(thread, num) { |
| 172 | return this.getFirstMessages(thread).then(result => { |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 173 | if (result.state != 'ok') |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 174 | return { |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 175 | state: result.state, |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 176 | }; |
| 177 | |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 178 | var messages = result.messages; |
| 179 | var author = result.author; |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 180 | var lastMessageId = result.lastMessageId; |
| 181 | |
Adrià Vilanova Martínez | 4b6c1cb | 2022-09-30 13:54:18 +0200 | [diff] [blame] | 182 | var avatars = []; |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 183 | |
| 184 | var authorUrl = author?.['1']?.['2']; |
Adrià Vilanova Martínez | 4b6c1cb | 2022-09-30 13:54:18 +0200 | [diff] [blame] | 185 | if (authorUrl !== undefined) avatars.push({url: authorUrl, timestamp: 0}); |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 186 | |
| 187 | for (var m of messages) { |
| 188 | var url = m?.['3']?.['1']?.['2']; |
Adrià Vilanova Martínez | 4b6c1cb | 2022-09-30 13:54:18 +0200 | [diff] [blame] | 189 | if (url === undefined) continue; |
| 190 | |
| 191 | var timestamp = m?.['1']?.['1']?.['2']; |
| 192 | avatars.push({url, timestamp}); |
| 193 | |
| 194 | m?.[12]?.forEach?.(messageOrGap => { |
| 195 | if (!messageOrGap[1]) return; |
| 196 | |
| 197 | var url = messageOrGap[1]?.[3]?.[1]?.[2]; |
| 198 | if (url === undefined) return; |
| 199 | |
| 200 | var timestamp = messageOrGap[1]?.[1]?.[1]?.[2]; |
| 201 | avatars.push({url, timestamp}); |
| 202 | }); |
| 203 | } |
| 204 | avatars.sort((a, b) => { |
| 205 | // If a timestamp is undefined, we'll push it to the end of the list |
| 206 | if (a === undefined && b === undefined) return 0; // both are equal |
| 207 | if (a === undefined) return 1; // b goes first |
| 208 | if (b === undefined) return -1; // a goes first |
| 209 | return a.timestamp - b.timestamp; // Old avatars go first |
| 210 | }); |
| 211 | |
| 212 | var avatarUrls = []; |
| 213 | |
| 214 | for (var a of avatars) { |
| 215 | var url = a.url; |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 216 | |
| 217 | if (url === undefined) continue; |
| 218 | if (!avatarUrls.includes(url)) avatarUrls.push(url); |
| 219 | if (avatarUrls.length == 3) break; |
| 220 | } |
| 221 | |
| 222 | // Add entry to cache if all the extra metadata could be retrieved. |
Adrià Vilanova Martínez | ac9fc9e | 2021-07-22 12:45:32 +0200 | [diff] [blame] | 223 | if (lastMessageId !== undefined) |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 224 | this.db.putCacheEntry({ |
| 225 | threadId: thread.thread, |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 226 | lastMessageId, |
| 227 | avatarUrls, |
| 228 | num, |
| 229 | lastUsedTimestamp: Math.floor(Date.now() / 1000), |
| 230 | }); |
| 231 | |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 232 | return { |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 233 | state: 'ok', |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 234 | avatars: avatarUrls, |
| 235 | }; |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 236 | }); |
| 237 | } |
| 238 | |
| 239 | // Returns an object with a cache entry that matches the request if found (via |
| 240 | // the |entry| property). The property |found| indicates whether the cache |
| 241 | // entry was found. |
Adrià Vilanova Martínez | 4cfa32f | 2021-07-22 17:29:03 +0200 | [diff] [blame] | 242 | // |
| 243 | // The |checkRecent| parameter is used to indicate whether lastUsedTimestamp |
| 244 | // must be within the last 30 seconds (which means that the thread has been |
| 245 | // checked for a potential invalidation). |
| 246 | getVisibleAvatarsFromCache(thread, num, checkRecent) { |
| 247 | return this.db.getCacheEntry(thread.thread).then(entry => { |
| 248 | if (entry === undefined || entry.num < num) |
| 249 | return { |
| 250 | found: false, |
| 251 | }; |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 252 | |
Adrià Vilanova Martínez | 4cfa32f | 2021-07-22 17:29:03 +0200 | [diff] [blame] | 253 | if (checkRecent) { |
| 254 | var now = Math.floor(Date.now() / 1000); |
| 255 | var diff = now - entry.lastUsedTimestamp; |
| 256 | if (diff > 30) |
| 257 | throw new Error( |
| 258 | 'lastUsedTimestamp isn\'t within the last 30 seconds (id: ' + |
| 259 | thread.thread + ' the difference is: ' + diff + ').'); |
| 260 | } |
| 261 | |
| 262 | return { |
| 263 | found: true, |
| 264 | entry, |
| 265 | }; |
| 266 | }); |
| 267 | } |
| 268 | |
| 269 | // Waits for the XHR interceptor to invalidate any outdated threads and |
| 270 | // returns what getVisibleAvatarsFromCache returns. If this times out, it |
| 271 | // returns the current cache entry anyways if it exists. |
| 272 | getVisibleAvatarsFromCacheAfterInvalidations(thread, num) { |
| 273 | return waitFor( |
| 274 | () => this.getVisibleAvatarsFromCache( |
| 275 | thread, num, /* checkRecent = */ true), |
| 276 | { |
| 277 | interval: 450, |
| 278 | timeout: 2 * 1000, |
| 279 | }) |
| 280 | .catch(err => { |
| 281 | console.debug( |
| 282 | '[threadListAvatars] Error while retrieving avatars from cache ' + |
| 283 | '(probably timed out waiting for lastUsedTimestamp to change):', |
| 284 | err); |
| 285 | |
| 286 | // Sometimes when going back to a thread list, the API call to load |
| 287 | // the thread list is not made, and so the previous piece of code |
| 288 | // times out waiting to intercept that API call and handle thread |
| 289 | // invalidations. |
| 290 | // |
| 291 | // If this is the case, this point will be reached. We'll assume we |
| 292 | // intercept all API calls, so reaching this point means that an API |
| 293 | // call wasn't made. Therefore, try again to get visible avatars from |
| 294 | // the cache without checking whether the entry has been checked for |
| 295 | // potential invalidation. |
| 296 | // |
| 297 | // See https://bugs.avm99963.com/p/twpowertools/issues/detail?id=10. |
| 298 | return this.getVisibleAvatarsFromCache( |
| 299 | thread, num, /* checkRecent = */ false); |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 300 | }); |
| 301 | } |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 302 | |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 303 | // Get an object with the following data: |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 304 | // - |state|: 'ok' (the avatars list could be retrieved), 'private' (the |
| 305 | // thread is in a private forum, so the avatars list could not be retrieved), |
| 306 | // or 'notVisible' (the thread has the visible field set to false). |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 307 | // - |avatars|: list of at most |num| avatars for thread |thread| |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 308 | getVisibleAvatars(thread, num = 3) { |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 309 | return this.isPrivateThread(thread).then(isPrivate => { |
| 310 | if (isPrivate) |
| 311 | return { |
| 312 | state: 'private', |
| 313 | avatars: [], |
| 314 | }; |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 315 | |
Adrià Vilanova Martínez | 4cfa32f | 2021-07-22 17:29:03 +0200 | [diff] [blame] | 316 | return this.getVisibleAvatarsFromCacheAfterInvalidations(thread, num) |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 317 | .then(res => { |
Adrià Vilanova Martínez | 4cfa32f | 2021-07-22 17:29:03 +0200 | [diff] [blame] | 318 | if (!res.found) { |
| 319 | var err = new Error('Cache entry doesn\'t exist.'); |
| 320 | err.name = 'notCached'; |
| 321 | throw err; |
| 322 | } |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 323 | return { |
| 324 | state: 'ok', |
| 325 | avatars: res.entry.avatarUrls, |
| 326 | }; |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 327 | }) |
| 328 | .catch(err => { |
Adrià Vilanova Martínez | 4cfa32f | 2021-07-22 17:29:03 +0200 | [diff] [blame] | 329 | // If the name is "notCached", then this is not an actual error so |
| 330 | // don't log an error, but still get avatars from the server. |
| 331 | if (err?.name !== 'notCached') |
| 332 | console.error( |
| 333 | '[threadListAvatars] Error while accessing avatars cache:', |
| 334 | err); |
| 335 | |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 336 | return this.getVisibleAvatarsFromServer(thread, num).then(res => { |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 337 | if (res.state != 'ok') |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 338 | return { |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 339 | state: res.state, |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 340 | avatars: [], |
| 341 | }; |
| 342 | |
| 343 | return { |
| 344 | state: 'ok', |
| 345 | avatars: res.avatars, |
| 346 | }; |
| 347 | }); |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 348 | }); |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 349 | }); |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 350 | } |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 351 | |
| 352 | // Inject avatars for thread summary (thread item) |node| in a thread list. |
| 353 | inject(node) { |
avm99963 | d08c37e | 2022-09-25 20:41:58 +0200 | [diff] [blame] | 354 | var header = |
| 355 | node.querySelector('ec-thread-summary .main-header .action .header'); |
| 356 | var headerContent = header.querySelector(':scope > .header-content'); |
| 357 | var expandBtn = header.querySelector(':scope > .expand-button'); |
| 358 | if (headerContent === null) { |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 359 | console.error( |
| 360 | '[threadListAvatars] Header is not present in the thread item\'s DOM.'); |
| 361 | return; |
| 362 | } |
avm99963 | d08c37e | 2022-09-25 20:41:58 +0200 | [diff] [blame] | 363 | if (expandBtn === null) { |
| 364 | console.error( |
| 365 | '[threadListAvatars] Expand button is not present in the thread item\'s DOM.'); |
| 366 | return; |
| 367 | } |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 368 | |
avm99963 | d08c37e | 2022-09-25 20:41:58 +0200 | [diff] [blame] | 369 | var thread = parseUrl(headerContent.href); |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 370 | if (thread === false) { |
| 371 | console.error('[threadListAvatars] Thread\'s link cannot be parsed.'); |
| 372 | return; |
| 373 | } |
| 374 | |
| 375 | this.getVisibleAvatars(thread) |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 376 | .then(res => { |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 377 | var avatarsContainer = document.createElement('div'); |
| 378 | avatarsContainer.classList.add('TWPT-avatars'); |
| 379 | |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 380 | var avatarUrls = res.avatars; |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 381 | |
avm99963 | 2485a3e | 2021-09-08 22:18:38 +0200 | [diff] [blame] | 382 | let singleAvatar; |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 383 | if (res.state == 'private' || res.state == 'notVisible') { |
avm99963 | 2485a3e | 2021-09-08 22:18:38 +0200 | [diff] [blame] | 384 | singleAvatar = document.createElement('div'); |
| 385 | singleAvatar.classList.add('TWPT-avatar-private-placeholder'); |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 386 | singleAvatar.textContent = |
| 387 | (res.state == 'private' ? 'person_off' : 'visibility_off'); |
avm99963 | 2485a3e | 2021-09-08 22:18:38 +0200 | [diff] [blame] | 388 | avatarsContainer.appendChild(singleAvatar); |
Adrià Vilanova Martínez | 87110e9 | 2021-08-11 19:23:16 +0200 | [diff] [blame] | 389 | } else { |
| 390 | for (var i = 0; i < avatarUrls.length; ++i) { |
| 391 | var avatar = document.createElement('div'); |
| 392 | avatar.classList.add('TWPT-avatar'); |
| 393 | avatar.style.backgroundImage = 'url(\'' + avatarUrls[i] + '\')'; |
| 394 | avatarsContainer.appendChild(avatar); |
| 395 | } |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 396 | } |
| 397 | |
avm99963 | d08c37e | 2022-09-25 20:41:58 +0200 | [diff] [blame] | 398 | header.insertBefore(avatarsContainer, expandBtn); |
avm99963 | 2485a3e | 2021-09-08 22:18:38 +0200 | [diff] [blame] | 399 | |
| 400 | if (res.state == 'private') { |
| 401 | var label = chrome.i18n.getMessage( |
| 402 | 'inject_threadlistavatars_private_thread_indicator_label'); |
| 403 | createPlainTooltip(singleAvatar, label); |
| 404 | } |
Adrià Vilanova Martínez | a086238 | 2022-02-02 19:05:28 +0100 | [diff] [blame] | 405 | if (res.state == 'notVisible') { |
| 406 | var label = chrome.i18n.getMessage( |
| 407 | 'inject_threadlistavatars_invisible_thread_indicator_label'); |
| 408 | createPlainTooltip(singleAvatar, label); |
| 409 | } |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 410 | }) |
| 411 | .catch(err => { |
| 412 | console.error( |
| 413 | '[threadListAvatars] Could not retrieve avatars for thread', |
| 414 | thread, err); |
| 415 | }); |
Adrià Vilanova Martínez | 27c6996 | 2021-07-17 23:32:51 +0200 | [diff] [blame] | 416 | } |
Adrià Vilanova Martínez | d269c62 | 2021-09-04 18:35:55 +0200 | [diff] [blame] | 417 | |
| 418 | // Inject avatars for thread summary (thread item) |node| in a thread list if |
| 419 | // the threadlistavatars option is enabled. |
| 420 | injectIfEnabled(node) { |
Adrià Vilanova Martínez | d03e39d | 2022-01-15 18:23:51 +0100 | [diff] [blame] | 421 | this.isEnabled().then(isEnabled => { |
Adrià Vilanova Martínez | d269c62 | 2021-09-04 18:35:55 +0200 | [diff] [blame] | 422 | if (isEnabled) { |
| 423 | document.body.classList.add('TWPT-threadlistavatars-enabled'); |
| 424 | this.inject(node); |
| 425 | } else { |
| 426 | document.body.classList.remove('TWPT-threadlistavatars-enabled'); |
| 427 | } |
| 428 | }); |
| 429 | } |
Adrià Vilanova Martínez | 3465e77 | 2021-07-11 19:18:41 +0200 | [diff] [blame] | 430 | }; |