Thread viewer for Bluesky

refactoring

Changed files
+41 -68
+41 -68
posting_stats_page.js
··· 18 this.submitButton = $(this.pageElement.querySelector('input[type="submit"]'), HTMLInputElement); 19 this.progressBar = $(this.pageElement.querySelector('input[type=submit] + progress'), HTMLProgressElement); 20 this.table = $(this.pageElement.querySelector('table.scan-result')); 21 22 this.setupEvents(); 23 ··· 26 } 27 28 setupEvents() { 29 - $(this.pageElement.querySelector('form')).addEventListener('submit', (e) => { 30 e.preventDefault(); 31 32 if (!this.scanStartTime) { ··· 38 39 this.rangeInput.addEventListener('input', (e) => { 40 let days = parseInt(this.rangeInput.value, 10); 41 - this.configurePostingStats({ days }); 42 }); 43 44 - this.pageElement.querySelectorAll('input[type="radio"]').forEach(r => { 45 r.addEventListener('click', (e) => { 46 let value = $(r, HTMLInputElement).value; 47 ··· 67 /** @returns {Promise<void>} */ 68 69 async fetchLists() { 70 - let select = $(this.pageElement.querySelector('.list-choice select')); 71 let lists = await accountAPI.loadUserLists(); 72 73 let sorted = lists.sort((a, b) => { ··· 78 }); 79 80 for (let list of lists) { 81 - let opt = $tag('option', { value: list.uri, text: list.name + ' ' }); 82 - select.append(opt); 83 - } 84 - } 85 - 86 - /** @param {{ days: number }} args */ 87 - 88 - configurePostingStats(args) { 89 - if (args.days) { 90 - let label = $(this.pageElement.querySelector('input[type=range] + label')); 91 - label.innerText = (args.days == 1) ? '1 day' : `${args.days} days`; 92 } 93 } 94 95 /** @returns {Promise<void>} */ 96 97 async scanPostingStats() { 98 - this.submitButton.value = 'Cancel'; 99 - 100 let requestedDays = this.selectedDaysRange(); 101 - 102 - this.progressBar.max = requestedDays; 103 - this.progressBar.value = 0; 104 - this.progressBar.style.display = 'inline'; 105 - 106 - this.table.style.display = 'none'; 107 108 - let tbody = $(this.table.querySelector('tbody')); 109 - tbody.innerHTML = ''; 110 - 111 - let thead = $(this.table.querySelector('thead')); 112 - thead.innerHTML = ''; 113 - 114 - let startTime = new Date().getTime(); 115 - this.scanStartTime = startTime; 116 - 117 - let scanInfo = $(this.pageElement.querySelector('.scan-info')); 118 - scanInfo.style.display = 'none'; 119 120 /** @type {FetchAllOnPageLoad} */ 121 let onPageLoad = (data) => { ··· 126 this.updateProgress(data, startTime); 127 }; 128 129 - let scanType = this.form.elements['scan_type'].value; 130 - 131 if (scanType == 'home') { 132 let items = await accountAPI.loadHomeTimeline(requestedDays, { 133 onPageLoad: onPageLoad, 134 keepLastPage: true 135 }); 136 - 137 - if (this.scanStartTime != startTime) { 138 - return; 139 - } 140 141 this.updateResultsTable(items, startTime, requestedDays); 142 } else if (scanType == 'list') { 143 - let select = $(this.pageElement.querySelector('.list-choice select'), HTMLSelectElement); 144 - let list = select.value; 145 let items = await accountAPI.loadListTimeline(list, requestedDays, { 146 onPageLoad: onPageLoad, 147 keepLastPage: true 148 }); 149 - 150 - if (this.scanStartTime != startTime) { 151 - return; 152 - } 153 154 this.updateResultsTable(items, startTime, requestedDays, { showReposts: false }); 155 } else if (scanType == 'users') { ··· 172 })); 173 174 let datasets = await Promise.all(requests); 175 - 176 - if (this.scanStartTime != startTime) { 177 - return; 178 - } 179 - 180 let items = datasets.flat(); 181 182 this.updateResultsTable(items, startTime, requestedDays, { ··· 189 keepLastPage: true 190 }); 191 192 - if (this.scanStartTime != startTime) { 193 - return; 194 - } 195 - 196 this.updateResultsTable(items, startTime, requestedDays, { showTotal: false, showPercentages: false }); 197 } 198 } ··· 209 210 this.progressBar.value = daysBack; 211 } 212 - 213 214 /** @param {string[]} dids */ 215 ··· 265 */ 266 267 updateResultsTable(items, startTime, requestedDays, options = {}) { 268 let users = {}; 269 let total = 0; 270 let allReposts = 0; ··· 284 let fetchedDays = (startTime - lastDate) / 86400 / 1000; 285 286 if (Math.ceil(fetchedDays) < requestedDays) { 287 - let scanInfo = $(this.pageElement.querySelector('.scan-info')); 288 - scanInfo.innerText = `🕓 Showing data from ${Math.round(fetchedDays)} days (the timeline only goes that far):`; 289 - scanInfo.style.display = 'block'; 290 } 291 292 daysBack = Math.min(requestedDays, fetchedDays); ··· 314 } 315 } 316 317 - let thead = $(this.table.querySelector('thead')); 318 let headRow = $tag('tr'); 319 320 if (options.showReposts !== false) { ··· 337 headRow.append($tag('th', { text: '% of all' })); 338 } 339 340 - thead.append(headRow); 341 - 342 - let tbody = $(this.table.querySelector('tbody')); 343 344 if (options.showTotal !== false) { 345 let tr = $tag('tr.total'); ··· 361 tr.append($tag('td.percent', { text: '' })); 362 } 363 364 - tbody.append(tr); 365 } 366 367 let sorted = Object.values(users).sort(this.sortUserRows); ··· 390 tr.append($tag('td.percent', { text: ((user.own + user.reposts) * 100 / total).toFixed(1) + '%' })); 391 } 392 393 - tbody.append(tr); 394 } 395 396 this.table.style.display = 'table'; 397 - this.submitButton.value = 'Start scan'; 398 - this.progressBar.style.display = 'none'; 399 - this.scanStartTime = undefined; 400 } 401 402 stopScan() {
··· 18 this.submitButton = $(this.pageElement.querySelector('input[type="submit"]'), HTMLInputElement); 19 this.progressBar = $(this.pageElement.querySelector('input[type=submit] + progress'), HTMLProgressElement); 20 this.table = $(this.pageElement.querySelector('table.scan-result')); 21 + this.tableHead = $(this.table.querySelector('thead')); 22 + this.tableBody = $(this.table.querySelector('tbody')); 23 + this.listSelect = $(this.pageElement.querySelector('.list-choice select'), HTMLSelectElement); 24 + this.scanInfo = $(this.pageElement.querySelector('.scan-info')); 25 + this.scanType = this.form.elements['scan_type']; 26 27 this.setupEvents(); 28 ··· 31 } 32 33 setupEvents() { 34 + this.form.addEventListener('submit', (e) => { 35 e.preventDefault(); 36 37 if (!this.scanStartTime) { ··· 43 44 this.rangeInput.addEventListener('input', (e) => { 45 let days = parseInt(this.rangeInput.value, 10); 46 + let label = $(this.pageElement.querySelector('input[type=range] + label')); 47 + label.innerText = (days == 1) ? '1 day' : `${days} days`; 48 }); 49 50 + this.scanType.forEach(r => { 51 r.addEventListener('click', (e) => { 52 let value = $(r, HTMLInputElement).value; 53 ··· 73 /** @returns {Promise<void>} */ 74 75 async fetchLists() { 76 let lists = await accountAPI.loadUserLists(); 77 78 let sorted = lists.sort((a, b) => { ··· 83 }); 84 85 for (let list of lists) { 86 + this.listSelect.append( 87 + $tag('option', { value: list.uri, text: list.name + ' ' }) 88 + ); 89 } 90 } 91 92 /** @returns {Promise<void>} */ 93 94 async scanPostingStats() { 95 + let startTime = new Date().getTime(); 96 let requestedDays = this.selectedDaysRange(); 97 + let scanType = this.scanType.value; 98 99 + this.startScan(startTime, requestedDays); 100 101 /** @type {FetchAllOnPageLoad} */ 102 let onPageLoad = (data) => { ··· 107 this.updateProgress(data, startTime); 108 }; 109 110 if (scanType == 'home') { 111 let items = await accountAPI.loadHomeTimeline(requestedDays, { 112 onPageLoad: onPageLoad, 113 keepLastPage: true 114 }); 115 116 this.updateResultsTable(items, startTime, requestedDays); 117 } else if (scanType == 'list') { 118 + let list = this.listSelect.value; 119 let items = await accountAPI.loadListTimeline(list, requestedDays, { 120 onPageLoad: onPageLoad, 121 keepLastPage: true 122 }); 123 124 this.updateResultsTable(items, startTime, requestedDays, { showReposts: false }); 125 } else if (scanType == 'users') { ··· 142 })); 143 144 let datasets = await Promise.all(requests); 145 let items = datasets.flat(); 146 147 this.updateResultsTable(items, startTime, requestedDays, { ··· 154 keepLastPage: true 155 }); 156 157 this.updateResultsTable(items, startTime, requestedDays, { showTotal: false, showPercentages: false }); 158 } 159 } ··· 170 171 this.progressBar.value = daysBack; 172 } 173 174 /** @param {string[]} dids */ 175 ··· 225 */ 226 227 updateResultsTable(items, startTime, requestedDays, options = {}) { 228 + if (this.scanStartTime != startTime) { 229 + return; 230 + } 231 + 232 let users = {}; 233 let total = 0; 234 let allReposts = 0; ··· 248 let fetchedDays = (startTime - lastDate) / 86400 / 1000; 249 250 if (Math.ceil(fetchedDays) < requestedDays) { 251 + this.scanInfo.innerText = `🕓 Showing data from ${Math.round(fetchedDays)} days (the timeline only goes that far):`; 252 + this.scanInfo.style.display = 'block'; 253 } 254 255 daysBack = Math.min(requestedDays, fetchedDays); ··· 277 } 278 } 279 280 let headRow = $tag('tr'); 281 282 if (options.showReposts !== false) { ··· 299 headRow.append($tag('th', { text: '% of all' })); 300 } 301 302 + this.tableHead.append(headRow); 303 304 if (options.showTotal !== false) { 305 let tr = $tag('tr.total'); ··· 321 tr.append($tag('td.percent', { text: '' })); 322 } 323 324 + this.tableBody.append(tr); 325 } 326 327 let sorted = Object.values(users).sort(this.sortUserRows); ··· 350 tr.append($tag('td.percent', { text: ((user.own + user.reposts) * 100 / total).toFixed(1) + '%' })); 351 } 352 353 + this.tableBody.append(tr); 354 } 355 356 this.table.style.display = 'table'; 357 + this.stopScan(); 358 + } 359 + 360 + startScan(startTime, requestedDays) { 361 + this.submitButton.value = 'Cancel'; 362 + 363 + this.progressBar.max = requestedDays; 364 + this.progressBar.value = 0; 365 + this.progressBar.style.display = 'inline'; 366 + 367 + this.table.style.display = 'none'; 368 + this.tableHead.innerHTML = ''; 369 + this.tableBody.innerHTML = ''; 370 + 371 + this.scanStartTime = startTime; 372 + this.scanInfo.style.display = 'none'; 373 } 374 375 stopScan() {