Thread viewer for Bluesky

Compare changes

Choose any two refs to compare.

+10
index.html
··· 49 50 <li><a href="#" data-action="login">Log in</a></li> 51 <li><a href="#" data-action="logout">Log out</a></li> 52 </ul> 53 </div> 54 ··· 195 196 <div class="lycan-import"> 197 <form> 198 <p> 199 In order to search within your likes and bookmarks, the posts you've liked or saved need to be imported into a database. 200 This is a one-time process, but it can take several minutes or more, depending on the age of your account. ··· 209 </form> 210 211 <div class="import-progress"> 212 <p class="import-status"></p> 213 <p><progress></progress> <output></output></p> 214 </div>
··· 49 50 <li><a href="#" data-action="login">Log in</a></li> 51 <li><a href="#" data-action="logout">Log out</a></li> 52 + 53 + <li class="link"><a href="?">Home</a></li> 54 + <li class="link"><a href="?page=posting_stats">Posting stats</a></li> 55 + <li class="link"><a href="?page=like_stats">Like stats</a></li> 56 + <li class="link"><a href="?page=search">Timeline search</a></li> 57 + <li class="link"><a href="?page=search&mode=likes">Archive search</a></li> 58 </ul> 59 </div> 60 ··· 201 202 <div class="lycan-import"> 203 <form> 204 + <h4>Data not imported yet</h4> 205 + 206 <p> 207 In order to search within your likes and bookmarks, the posts you've liked or saved need to be imported into a database. 208 This is a one-time process, but it can take several minutes or more, depending on the age of your account. ··· 217 </form> 218 219 <div class="import-progress"> 220 + <h4>Import in progress</h4> 221 + 222 <p class="import-status"></p> 223 <p><progress></progress> <output></output></p> 224 </div>
+8 -1
menu.js
··· 11 12 html.addEventListener('click', (e) => { 13 this.menuElement.style.visibility = 'hidden'; 14 }); 15 16 this.icon.addEventListener('click', (e) => { 17 e.stopPropagation(); ··· 66 } 67 68 toggleAccountMenu() { 69 - this.menuElement.style.visibility = (this.menuElement.style.visibility == 'visible') ? 'hidden' : 'visible'; 70 } 71 72 /** @param {string} buttonName */
··· 11 12 html.addEventListener('click', (e) => { 13 this.menuElement.style.visibility = 'hidden'; 14 + this.icon.classList.remove('active'); 15 }); 16 + 17 + let homeLink = $(this.menuElement.querySelector('a[href="?"]'), HTMLLinkElement); 18 + homeLink.href = location.origin + location.pathname; 19 20 this.icon.addEventListener('click', (e) => { 21 e.stopPropagation(); ··· 70 } 71 72 toggleAccountMenu() { 73 + let isVisible = (this.menuElement.style.visibility == 'visible'); 74 + 75 + this.menuElement.style.visibility = isVisible ? 'hidden' : 'visible'; 76 + this.icon.classList.toggle('active', !isVisible); 77 } 78 79 /** @param {string} buttonName */
+14 -6
private_search_page.js
··· 42 43 let params = new URLSearchParams(location.search); 44 this.mode = params.get('mode'); 45 - this.lycanMode = params.get('lycan'); 46 47 - if (this.lycanMode == 'local') { 48 this.localLycan = new BlueskyAPI('http://localhost:3000', false); 49 } 50 } 51 ··· 136 return await this.localLycan.getRequest('blue.feeds.lycan.getImportStatus', { user: accountAPI.user.did }); 137 } else { 138 return await accountAPI.getRequest('blue.feeds.lycan.getImportStatus', null, { 139 - headers: { 'atproto-proxy': 'did:web:lycan.feeds.blue#lycan' } 140 }); 141 } 142 } ··· 157 this.lycanImportSection.style.display = 'block'; 158 this.lycanImportForm.style.display = 'block'; 159 this.importProgress.style.display = 'none'; 160 161 this.stopImportTimer(); 162 } else if (info.status == 'in_progress' || info.status == 'scheduled' || info.status == 'requested') { 163 this.lycanImportSection.style.display = 'block'; 164 this.lycanImportForm.style.display = 'none'; 165 this.importProgress.style.display = 'block'; 166 167 this.showImportProgress(info); 168 this.startImportTimer(); 169 } else if (info.status == 'finished') { 170 this.lycanImportForm.style.display = 'none'; 171 this.importProgress.style.display = 'block'; 172 173 this.showImportProgress({ status: 'finished', progress: 1.0 }); 174 this.stopImportTimer(); ··· 192 this.importStatusLabel.innerText = `Import complete โœ“`; 193 } else if (info.position) { 194 let date = new Date(info.position).toLocaleString(window.dateLocale, { day: 'numeric', month: 'short', year: 'numeric' }); 195 - this.importStatusLabel.innerText = `Imported data until: ${date}`; 196 } else if (info.status == 'requested') { 197 this.importStatusLabel.innerText = 'Requesting importโ€ฆ'; 198 } else { ··· 206 this.lycanImportSection.style.display = 'block'; 207 this.lycanImportForm.style.display = 'none'; 208 this.importProgress.style.display = 'block'; 209 210 this.importStatusLabel.innerText = message; 211 this.stopImportTimer(); ··· 240 }); 241 } else { 242 await accountAPI.postRequest('blue.feeds.lycan.startImport', null, { 243 - headers: { 'atproto-proxy': 'did:web:lycan.feeds.blue#lycan' } 244 }); 245 } 246 ··· 354 if (cursor) params.cursor = cursor; 355 356 response = await accountAPI.getRequest('blue.feeds.lycan.searchPosts', params, { 357 - headers: { 'atproto-proxy': 'did:web:lycan.feeds.blue#lycan' } 358 }); 359 } 360
··· 42 43 let params = new URLSearchParams(location.search); 44 this.mode = params.get('mode'); 45 + let lycan = params.get('lycan'); 46 47 + if (lycan == 'local') { 48 this.localLycan = new BlueskyAPI('http://localhost:3000', false); 49 + } else if (lycan) { 50 + this.lycanAddress = `did:web:${lycan}#lycan`; 51 + } else { 52 + this.lycanAddress = 'did:web:lycan.feeds.blue#lycan'; 53 } 54 } 55 ··· 140 return await this.localLycan.getRequest('blue.feeds.lycan.getImportStatus', { user: accountAPI.user.did }); 141 } else { 142 return await accountAPI.getRequest('blue.feeds.lycan.getImportStatus', null, { 143 + headers: { 'atproto-proxy': this.lycanAddress } 144 }); 145 } 146 } ··· 161 this.lycanImportSection.style.display = 'block'; 162 this.lycanImportForm.style.display = 'block'; 163 this.importProgress.style.display = 'none'; 164 + this.searchField.disabled = true; 165 166 this.stopImportTimer(); 167 } else if (info.status == 'in_progress' || info.status == 'scheduled' || info.status == 'requested') { 168 this.lycanImportSection.style.display = 'block'; 169 this.lycanImportForm.style.display = 'none'; 170 this.importProgress.style.display = 'block'; 171 + this.searchField.disabled = true; 172 173 this.showImportProgress(info); 174 this.startImportTimer(); 175 } else if (info.status == 'finished') { 176 this.lycanImportForm.style.display = 'none'; 177 this.importProgress.style.display = 'block'; 178 + this.searchField.disabled = false; 179 180 this.showImportProgress({ status: 'finished', progress: 1.0 }); 181 this.stopImportTimer(); ··· 199 this.importStatusLabel.innerText = `Import complete โœ“`; 200 } else if (info.position) { 201 let date = new Date(info.position).toLocaleString(window.dateLocale, { day: 'numeric', month: 'short', year: 'numeric' }); 202 + this.importStatusLabel.innerText = `Downloaded data until: ${date}`; 203 } else if (info.status == 'requested') { 204 this.importStatusLabel.innerText = 'Requesting importโ€ฆ'; 205 } else { ··· 213 this.lycanImportSection.style.display = 'block'; 214 this.lycanImportForm.style.display = 'none'; 215 this.importProgress.style.display = 'block'; 216 + this.searchField.disabled = true; 217 218 this.importStatusLabel.innerText = message; 219 this.stopImportTimer(); ··· 248 }); 249 } else { 250 await accountAPI.postRequest('blue.feeds.lycan.startImport', null, { 251 + headers: { 'atproto-proxy': this.lycanAddress } 252 }); 253 } 254 ··· 362 if (cursor) params.cursor = cursor; 363 364 response = await accountAPI.getRequest('blue.feeds.lycan.searchPosts', params, { 365 + headers: { 'atproto-proxy': this.lycanAddress } 366 }); 367 } 368
+24 -4
style.css
··· 127 padding: 6px 11px; 128 } 129 130 - #account_menu li a { 131 display: inline-block; 132 color: #333; 133 font-size: 11pt; ··· 138 background-color: hsla(210, 100%, 4%, 0.12); 139 } 140 141 - #account_menu li a:hover { 142 background-color: hsla(210, 100%, 4%, 0.2); 143 text-decoration: none; 144 } 145 146 #account_menu li .check { ··· 1142 background-color: transparent; 1143 } 1144 1145 #account_menu { 1146 background: hsl(210, 33.33%, 94.0%); 1147 border-color: #ccc; 1148 } 1149 1150 - #account_menu li a { 1151 color: #333; 1152 border-color: #bbb; 1153 background-color: hsla(210, 100%, 4%, 0.12); 1154 } 1155 1156 - #account_menu li a:hover { 1157 background-color: hsla(210, 100%, 4%, 0.2); 1158 } 1159
··· 127 padding: 6px 11px; 128 } 129 130 + #account_menu li a[data-action] { 131 display: inline-block; 132 color: #333; 133 font-size: 11pt; ··· 138 background-color: hsla(210, 100%, 4%, 0.12); 139 } 140 141 + #account_menu li a[data-action]:hover { 142 background-color: hsla(210, 100%, 4%, 0.2); 143 text-decoration: none; 144 + } 145 + 146 + #account_menu li:not(.link) + li.link { 147 + margin-top: 16px; 148 + padding-top: 10px; 149 + border-top: 1px solid #ccc; 150 + } 151 + 152 + #account_menu li.link { 153 + margin-top: 8px; 154 + margin-left: 2px; 155 + } 156 + 157 + #account_menu li.link a { 158 + font-size: 11pt; 159 + color: #333; 160 } 161 162 #account_menu li .check { ··· 1158 background-color: transparent; 1159 } 1160 1161 + #account.active { 1162 + color: #333; 1163 + } 1164 + 1165 #account_menu { 1166 background: hsl(210, 33.33%, 94.0%); 1167 border-color: #ccc; 1168 } 1169 1170 + #account_menu li a[data-action] { 1171 color: #333; 1172 border-color: #bbb; 1173 background-color: hsla(210, 100%, 4%, 0.12); 1174 } 1175 1176 + #account_menu li a[data-action]:hover { 1177 background-color: hsla(210, 100%, 4%, 0.2); 1178 } 1179