blob: 41777caca925b062e4b1ce43b9ba7926f8e0cf1e [file] [log] [blame]
avm99963d3f4ac02021-08-12 18:36:58 +02001import {CCApi} from '../../common/api.js';
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +02002import {getAuthUser} from '../../common/communityConsoleUtils.js';
3
avm99963d3f4ac02021-08-12 18:36:58 +02004import {createExtBadge} from './utils/common.js';
5
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +02006var authuser = getAuthUser();
7
avm99963d3f4ac02021-08-12 18:36:58 +02008const intervalMs = 3 * 60 * 1000; // 3 minutes
9const firstCallDelayMs = 3 * 1000; // 3 seconds
10
11export default class AutoRefresh {
12 constructor() {
13 this.isLookingForUpdates = false;
14 this.isUpdatePromptShown = false;
15 this.lastTimestamp = null;
16 this.filter = null;
17 this.path = null;
18 this.snackbar = null;
19 this.interval = null;
20 this.firstCallTimeout = null;
21 }
22
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +020023 getStartupData() {
24 return JSON.parse(
25 document.querySelector('html').getAttribute('data-startup'));
avm99963d3f4ac02021-08-12 18:36:58 +020026 }
27
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +020028 isOrderedByTimestampDescending() {
29 var startup = this.getStartupData();
30 // Returns orderOptions.by == TIMESTAMP && orderOptions.desc == true
31 return (
32 startup?.[1]?.[1]?.[3]?.[14]?.[1] == 1 &&
33 startup?.[1]?.[1]?.[3]?.[14]?.[2] == true);
avm99963d3f4ac02021-08-12 18:36:58 +020034 }
35
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +020036 getCustomFilter(path) {
37 var searchRegex = /^\/s\/community\/search\/([^\/]*)/;
38 var matches = path.match(searchRegex);
39 if (matches !== null && matches.length > 1) {
40 var search = decodeURIComponent(matches[1]);
41 var params = new URLSearchParams(search);
42 return params.get('query') || '';
43 }
44
45 return '';
avm99963d3f4ac02021-08-12 18:36:58 +020046 }
47
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +020048 filterHasOverride(filter, override) {
49 var escapedOverride = override.replace(/([^\w\d\s])/gi, '\\$1');
50 var regex = new RegExp('[^a-zA-Z0-9]?' + escapedOverride + ':');
51 return regex.test(filter);
avm99963d3f4ac02021-08-12 18:36:58 +020052 }
53
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +020054 getFilter(path) {
55 var query = this.getCustomFilter(path);
56
57 // Note: This logic has been copied and adapted from the
58 // _buildQuery$1$threadId function in the Community Console
59 var conditions = '';
60 var startup = this.getStartupData();
61
62 // TODO(avm99963): if the selected forums are changed without reloading the
63 // page, this will get the old selected forums. Fix this.
64 var forums = startup?.[1]?.[1]?.[3]?.[8] ?? [];
65 if (!this.filterHasOverride(query, 'forum') && forums !== null &&
66 forums.length > 0)
67 conditions += ' forum:(' + forums.join(' | ') + ')';
68
69 var langs = startup?.[1]?.[1]?.[3]?.[5] ?? [];
70 if (!this.filterHasOverride(query, 'lang') && langs !== null &&
71 langs.length > 0)
72 conditions += ' lang:(' + langs.map(l => '"' + l + '"').join(' | ') + ')';
73
74 if (query.length !== 0 && conditions.length !== 0)
75 return '(' + query + ')' + conditions;
76 return query + conditions;
avm99963d3f4ac02021-08-12 18:36:58 +020077 }
78
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +020079 getLastTimestamp() {
avm99963d3f4ac02021-08-12 18:36:58 +020080 return CCApi(
81 'ViewForum', {
82 1: '0', // TODO: Change, when only a forum is selected, it
83 // should be set here
84 // options
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +020085 2: {
avm99963d3f4ac02021-08-12 18:36:58 +020086 // pagination
87 1: {
88 2: 2, // maxNum
89 },
90 // order
91 2: {
92 1: 1, // by
93 2: true, // desc
94 },
95 12: this.filter, // forumViewFilters
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +020096 },
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +020097 },
avm99963d3f4ac02021-08-12 18:36:58 +020098 /* authenticated = */ true, authuser)
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +020099 .then(body => {
100 var timestamp = body?.[1]?.[2]?.[0]?.[2]?.[17];
101 if (timestamp === undefined)
102 throw new Error(
103 'Unexpected body of response (' +
104 (body?.[1]?.[2]?.[0] === undefined ?
105 'no threads were returned' :
106 'the timestamp value is not present in the first thread') +
107 ').');
108
109 return timestamp;
110 });
111 // TODO(avm99963): Add retry mechanism (sometimes thread lists are empty,
112 // but when loading the next page the thread appears).
113 //
114 // NOTE(avm99963): It seems like loading the first 2 threads instead of only
115 // the first one fixes this (empty lists are now rarely returned).
avm99963d3f4ac02021-08-12 18:36:58 +0200116 }
117
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +0200118 unregister() {
119 console.debug('autorefresh_list: unregistering');
120
121 if (!this.isLookingForUpdates) return;
122
123 window.clearTimeout(this.firstCallTimeout);
124 window.clearInterval(this.interval);
125 this.isUpdatePromptShown = false;
126 this.isLookingForUpdates = false;
avm99963d3f4ac02021-08-12 18:36:58 +0200127 }
128
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +0200129 showUpdatePrompt() {
130 this.snackbar.classList.remove('TWPT-hidden');
131 document.title = '[!!!] ' + document.title.replace('[!!!] ', '');
132 this.isUpdatePromptShown = true;
avm99963d3f4ac02021-08-12 18:36:58 +0200133 }
134
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +0200135 hideUpdatePrompt() {
136 this.snackbar.classList.add('TWPT-hidden');
137 document.title = document.title.replace('[!!!] ', '');
138 this.isUpdatePromptShown = false;
avm99963d3f4ac02021-08-12 18:36:58 +0200139 }
140
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +0200141 injectUpdatePrompt() {
142 var pane = document.createElement('div');
143 pane.classList.add('TWPT-pane-for-snackbar');
144
145 var snackbar = document.createElement('material-snackbar-panel');
146 snackbar.classList.add('TWPT-snackbar');
147 snackbar.classList.add('TWPT-hidden');
148
149 var ac = document.createElement('div');
150 ac.classList.add('TWPT-animation-container');
151
152 var nb = document.createElement('div');
153 nb.classList.add('TWPT-notification-bar');
154
155 var ft = document.createElement('focus-trap');
156
157 var content = document.createElement('div');
158 content.classList.add('TWPT-focus-content-wrapper');
159
160 var badge = createExtBadge();
161
162 var message = document.createElement('div');
163 message.classList.add('TWPT-message');
164 message.textContent =
165 chrome.i18n.getMessage('inject_autorefresh_list_snackbar_message');
166
167 var action = document.createElement('div');
168 action.classList.add('TWPT-action');
169 action.textContent =
170 chrome.i18n.getMessage('inject_autorefresh_list_snackbar_action');
171
172 action.addEventListener('click', e => {
173 this.hideUpdatePrompt();
174 document.querySelector('.app-title-button').click();
175 });
176
177 content.append(badge, message, action);
178 ft.append(content);
179 nb.append(ft);
180 ac.append(nb);
181 snackbar.append(ac);
182 pane.append(snackbar);
183 document.getElementById('default-acx-overlay-container').append(pane);
184 this.snackbar = snackbar;
avm99963d3f4ac02021-08-12 18:36:58 +0200185 }
186
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +0200187 checkUpdate() {
188 if (location.pathname != this.path) {
189 this.unregister();
190 return;
191 }
192
193 if (this.isUpdatePromptShown) return;
194
195 console.debug('Checking for update at: ', new Date());
196
197 this.getLastTimestamp()
198 .then(timestamp => {
199 if (timestamp != this.lastTimestamp) this.showUpdatePrompt();
200 })
201 .catch(
202 err => console.error(
203 'Coudln\'t get last timestamp (while updating): ', err));
avm99963d3f4ac02021-08-12 18:36:58 +0200204 }
205
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +0200206 firstCall() {
207 console.debug(
208 'autorefresh_list: now performing first call to finish setup (filter: [' +
209 this.filter + '])');
210
211 if (location.pathname != this.path) {
212 this.unregister();
213 return;
214 }
215
216 this.getLastTimestamp()
217 .then(timestamp => {
218 this.lastTimestamp = timestamp;
219 var checkUpdateCallback = this.checkUpdate.bind(this);
avm99963d3f4ac02021-08-12 18:36:58 +0200220 this.interval = window.setInterval(checkUpdateCallback, intervalMs);
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +0200221 })
222 .catch(
223 err => console.error(
224 'Couldn\'t get last timestamp (while setting up): ', err));
avm99963d3f4ac02021-08-12 18:36:58 +0200225 }
226
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +0200227 setUp() {
228 if (!this.isOrderedByTimestampDescending()) return;
229
230 this.unregister();
231
232 console.debug('autorefresh_list: starting set up...');
233
234 if (this.snackbar === null) this.injectUpdatePrompt();
235 this.isLookingForUpdates = true;
236 this.path = location.pathname;
237 this.filter = this.getFilter(this.path);
238
239 var firstCall = this.firstCall.bind(this);
avm99963d3f4ac02021-08-12 18:36:58 +0200240 this.firstCallTimeout = window.setTimeout(firstCall, firstCallDelayMs);
241 }
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +0200242};