blob: 1b5bb48425b9c2cc05ee1790bf4dcd63f8faf2c8 [file] [log] [blame]
avm99963f5923962020-12-07 16:44:37 +01001function parseUrl(url) {
2 var forum_a = url.match(/forum\/([0-9]+)/i);
3 var thread_a = url.match(/thread\/([0-9]+)/i);
4
5 if (forum_a === null || thread_a === null) {
6 return false;
7 }
8
9 return {
10 'forum': forum_a[1],
11 'thread': thread_a[1],
12 };
13}
14
15function recursiveParentElement(el, tag) {
16 while (el !== document.documentElement) {
17 el = el.parentNode;
18 if (el.tagName == tag) return el;
19 }
20 return undefined;
21}
22
23// Source:
24// https://stackoverflow.com/questions/33063774/communication-from-an-injected-script-to-the-content-script-with-a-response
25var contentScriptRequest = (function() {
26 var requestId = 0;
27 var prefix = 'TWPT-batchlock-generic';
28
29 function sendRequest(data) {
30 var id = requestId++;
31
32 return new Promise(function(resolve, reject) {
33 var listener = function(evt) {
34 if (evt.source === window && evt.data && evt.data.prefix === prefix &&
35 evt.data.requestId == id) {
36 // Deregister self
37 window.removeEventListener('message', listener);
38 resolve(evt.data.data);
39 }
40 };
41
42 window.addEventListener('message', listener);
43
44 var payload = {data, id, prefix};
45
46 window.dispatchEvent(
47 new CustomEvent('TWPT_sendRequest', {detail: payload}));
48 });
49 }
50
51 return {sendRequest: sendRequest};
52})();
53
54function enableEndButtons() {
55 var buttons = document.querySelectorAll(
56 '.pane[pane-id="default-1"] footer[data-footer-id="1"] material-button');
57 buttons.forEach(btn => {
58 btn.classList.remove('is-disabled');
59 });
60}
61
62function addLogEntry(success, action, url, threadId, errDetails = null) {
63 var p1 = contentScriptRequest.sendRequest({
64 action: 'geti18nMessage',
65 msg: 'inject_lockdialog_log_entry_beginning',
66 placeholders: [threadId],
67 });
68
69 var p2 = contentScriptRequest.sendRequest({
70 action: 'geti18nMessage',
71 msg: 'inject_lockdialog_log_entry_' + (success ? 'success' : 'error') +
72 '_' + action,
73 placeholders: [errDetails],
74 });
75
76 Promise.all([p1, p2]).then(strings => {
77 var log = document.getElementById('TWPT-lock-log');
78 var logEntry = document.createElement('p');
79 logEntry.classList.add(
80 'TWPT-log-entry', 'TWPT-log-entry--' + (success ? 'success' : 'error'));
81
82 var a = document.createElement('a');
83 a.href = url;
84 a.target = '_blank';
85 a.textContent = strings[0];
86
87 var end = document.createTextNode(': ' + strings[1]);
88
89 logEntry.append(a, end);
90 log.append(logEntry);
91 });
92}
93
94function lockThreads(action) {
95 var modal = document.querySelector('.pane[pane-id="default-1"]');
96 modal.querySelector('footer[data-footer-id="0"]').classList.add('is-hidden');
97 modal.querySelector('footer[data-footer-id="1"]')
98 .classList.remove('is-hidden');
99
100 var checkboxes = document.querySelectorAll(
101 '.thread-group material-checkbox[aria-checked="true"]');
102
103 var p = document.createElement('p');
104 p.style.textAlign = 'center';
105
106 var progress = document.createElement('progress');
107 progress.max = checkboxes.length;
108 progress.value = 0;
109
110 p.append(progress);
111
112 var log = document.createElement('div');
113 log.id = 'TWPT-lock-log';
114 log.classList.add('TWPT-log');
115
116 modal.querySelector('main').textContent = '';
117 modal.querySelector('main').append(p, log);
118
119 checkboxes.forEach(checkbox => {
120 var url = recursiveParentElement(checkbox, 'A').href;
121 var thread = parseUrl(url);
122 if (thread === false) {
123 console.error('Fatal error: thread URL ' + url + ' could not be parsed.');
124 return;
125 }
126 fetch('https://support.google.com/s/community/api/SetThreadAttribute', {
127 'headers': {
128 'content-type': 'text/plain; charset=utf-8',
129 },
130 'body': JSON.stringify({
131 1: thread.forum,
132 2: thread.thread,
133 3: (action == 'lock' ? 1 : 2),
134 }),
135 'method': 'POST',
136 'mode': 'cors',
137 'credentials': 'include',
138 })
139 .then(res => {
140 if (res.status == 200 || res.status == 400) {
141 return res.json().then(data => ({
142 status: res.status,
143 body: data,
144 }));
145 } else {
146 throw new Error('Status code ' + res.status + ' was not expected.');
147 }
148 })
149 .then(res => {
150 if (res.status == 400) {
151 throw new Error(
152 res.body[4] ||
153 ('Response status: 400. Error code: ' + res.body[2]));
154 }
155 })
156 .then(_ => {
157 addLogEntry(true, action, url, thread.thread);
158 })
159 .catch(err => {
160 console.error(
161 'An error occurred while locking thread ' + url + ': ' + err);
162 addLogEntry(false, action, url, thread.thread, err);
163 })
164 .then(_ => {
165 progress.value = parseInt(progress.value) + 1;
166 if (progress.value == progress.getAttribute('max'))
167 enableEndButtons();
168 });
169 });
170}
171
172window.addEventListener('message', e => {
173 if (e.source === window && e.data && e.data.prefix === 'TWPT-batchlock' &&
174 e.data.action) {
175 switch (e.data.action) {
176 case 'lock':
177 case 'unlock':
178 console.info('Performing action ' + e.data.action);
179 lockThreads(e.data.action);
180 break;
181
182 default:
183 console.error(
184 'Action \'' + e.data.action +
185 '\' unknown to TWPT-batchlock receiver.');
186 }
187 }
188});