Thread viewer for Bluesky

extracted account menu stuff to new class

+1
index.html
··· 133 133 <script src="utils.js"></script> 134 134 <script src="rich_text_lite.js"></script> 135 135 <script src="models.js"></script> 136 + <script src="menu.js"></script> 136 137 <script src="thread_page.js"></script> 137 138 <script src="posting_stats_page.js"></script> 138 139 <script src="embed_component.js"></script>
+133
menu.js
··· 1 + class Menu { 2 + constructor() { 3 + this.menuElement = $id('account_menu'); 4 + this.icon = $id('account'); 5 + 6 + this.setupEvents(); 7 + } 8 + 9 + setupEvents() { 10 + let html = $(document.body.parentNode); 11 + 12 + html.addEventListener('click', (e) => { 13 + this.menuElement.style.visibility = 'hidden'; 14 + }); 15 + 16 + this.icon.addEventListener('click', (e) => { 17 + e.stopPropagation(); 18 + this.toggleAccountMenu(); 19 + }); 20 + 21 + this.menuElement.addEventListener('click', (e) => { 22 + e.stopPropagation(); 23 + }); 24 + 25 + $(this.menuElement.querySelector('a[data-action=biohazard]')).addEventListener('click', (e) => { 26 + e.preventDefault(); 27 + 28 + let hazards = document.querySelectorAll('p.hidden-replies, .content > .post.blocked, .blocked > .load-post'); 29 + 30 + if (window.biohazardEnabled === false) { 31 + window.biohazardEnabled = true; 32 + localStorage.setItem('biohazard', 'true'); 33 + this.toggleMenuButtonCheck('biohazard', true); 34 + Array.from(hazards).forEach(p => { $(p).style.display = 'block' }); 35 + } else { 36 + window.biohazardEnabled = false; 37 + localStorage.setItem('biohazard', 'false'); 38 + this.toggleMenuButtonCheck('biohazard', false); 39 + Array.from(hazards).forEach(p => { $(p).style.display = 'none' }); 40 + } 41 + }); 42 + 43 + $(this.menuElement.querySelector('a[data-action=incognito]')).addEventListener('click', (e) => { 44 + e.preventDefault(); 45 + 46 + if (window.isIncognito) { 47 + localStorage.removeItem('incognito'); 48 + } else { 49 + localStorage.setItem('incognito', '1'); 50 + } 51 + 52 + location.reload(); 53 + }); 54 + 55 + $(this.menuElement.querySelector('a[data-action=login]')).addEventListener('click', (e) => { 56 + e.preventDefault(); 57 + 58 + toggleDialog(loginDialog); 59 + this.menuElement.style.visibility = 'hidden'; 60 + }); 61 + 62 + $(this.menuElement.querySelector('a[data-action=logout]')).addEventListener('click', (e) => { 63 + e.preventDefault(); 64 + logOut(); 65 + }); 66 + } 67 + 68 + toggleAccountMenu() { 69 + this.menuElement.style.visibility = (this.menuElement.style.visibility == 'visible') ? 'hidden' : 'visible'; 70 + } 71 + 72 + /** @param {string} buttonName */ 73 + 74 + showMenuButton(buttonName) { 75 + let button = $(this.menuElement.querySelector(`a[data-action=${buttonName}]`)); 76 + let item = $(button.parentNode); 77 + item.style.display = 'list-item'; 78 + } 79 + 80 + /** @param {string} buttonName */ 81 + 82 + hideMenuButton(buttonName) { 83 + let button = $(this.menuElement.querySelector(`a[data-action=${buttonName}]`)); 84 + let item = $(button.parentNode); 85 + item.style.display = 'none'; 86 + } 87 + 88 + /** @param {string} buttonName, @param {boolean} state */ 89 + 90 + toggleMenuButtonCheck(buttonName, state) { 91 + let button = $(this.menuElement.querySelector(`a[data-action=${buttonName}]`)); 92 + let check = $(button.querySelector('.check')); 93 + check.style.display = (state) ? 'inline' : 'none'; 94 + } 95 + 96 + /** @param {boolean | 'incognito'} loggedIn, @param {string | undefined | null} [avatar] */ 97 + 98 + showLoggedInStatus(loggedIn, avatar) { 99 + if (loggedIn === true && avatar) { 100 + let button = $(this.icon.querySelector('i')); 101 + 102 + let img = $tag('img.avatar', { src: avatar }); 103 + img.style.display = 'none'; 104 + img.addEventListener('load', () => { 105 + button.remove(); 106 + img.style.display = 'inline'; 107 + }); 108 + img.addEventListener('error', () => { 109 + this.showLoggedInStatus(true, null); 110 + }) 111 + 112 + this.icon.append(img); 113 + } else if (loggedIn === false) { 114 + this.icon.innerHTML = `<i class="fa-regular fa-user-circle fa-xl"></i>`; 115 + } else if (loggedIn === 'incognito') { 116 + this.icon.innerHTML = `<i class="fa-solid fa-user-secret fa-lg"></i>`; 117 + } else { 118 + this.icon.innerHTML = `<i class="fa-solid fa-user-circle fa-xl"></i>`; 119 + } 120 + } 121 + 122 + /** @returns {Promise<void>} */ 123 + 124 + async loadCurrentUserAvatar() { 125 + try { 126 + let url = await api.loadCurrentUserAvatar(); 127 + this.showLoggedInStatus(true, url); 128 + } catch (error) { 129 + console.log(error); 130 + this.showLoggedInStatus(true, null); 131 + } 132 + } 133 + }
+14 -135
skythread.js
··· 1 1 function init() { 2 - let html = $(document.body.parentNode); 3 - 4 2 window.dateLocale = localStorage.getItem('locale') || undefined; 5 3 window.isIncognito = !!localStorage.getItem('incognito'); 6 4 window.biohazardEnabled = JSON.parse(localStorage.getItem('biohazard') ?? 'null'); 7 5 8 6 window.loginDialog = $(document.querySelector('#login')); 9 - window.accountMenu = $(document.querySelector('#account_menu')); 10 7 11 8 window.avatarPreloader = buildAvatarPreloader(); 12 9 10 + window.accountMenu = new Menu(); 13 11 window.threadPage = new ThreadPage(); 14 12 window.postingStatsPage = new PostingStatsPage(); 15 - 16 - html.addEventListener('click', (e) => { 17 - $id('account_menu').style.visibility = 'hidden'; 18 - }); 19 13 20 14 $(document.querySelector('#search form')).addEventListener('submit', (e) => { 21 15 e.preventDefault(); ··· 67 61 68 62 window.biohazardEnabled = false; 69 63 localStorage.setItem('biohazard', 'false'); 70 - toggleMenuButton('biohazard', false); 64 + accountMenu.toggleMenuButtonCheck('biohazard', false); 71 65 72 66 for (let p of document.querySelectorAll('p.hidden-replies, .content > .post.blocked, .blocked > .load-post')) { 73 67 $(p).style.display = 'none'; ··· 78 72 hideDialog(target.closest('.dialog')); 79 73 }); 80 74 81 - $(document.querySelector('#account')).addEventListener('click', (e) => { 82 - toggleAccountMenu(); 83 - e.stopPropagation(); 84 - }); 85 - 86 - accountMenu.addEventListener('click', (e) => { 87 - e.stopPropagation(); 88 - }); 89 - 90 - $(accountMenu.querySelector('a[data-action=biohazard]')).addEventListener('click', (e) => { 91 - e.preventDefault(); 92 - 93 - let hazards = document.querySelectorAll('p.hidden-replies, .content > .post.blocked, .blocked > .load-post'); 94 - 95 - if (window.biohazardEnabled === false) { 96 - window.biohazardEnabled = true; 97 - localStorage.setItem('biohazard', 'true'); 98 - toggleMenuButton('biohazard', true); 99 - Array.from(hazards).forEach(p => { $(p).style.display = 'block' }); 100 - } else { 101 - window.biohazardEnabled = false; 102 - localStorage.setItem('biohazard', 'false'); 103 - toggleMenuButton('biohazard', false); 104 - Array.from(hazards).forEach(p => { $(p).style.display = 'none' }); 105 - } 106 - }); 107 - 108 - $(accountMenu.querySelector('a[data-action=incognito]')).addEventListener('click', (e) => { 109 - e.preventDefault(); 110 - 111 - if (isIncognito) { 112 - localStorage.removeItem('incognito'); 113 - } else { 114 - localStorage.setItem('incognito', '1'); 115 - } 116 - 117 - location.reload(); 118 - }); 119 - 120 - $(accountMenu.querySelector('a[data-action=login]')).addEventListener('click', (e) => { 121 - e.preventDefault(); 122 - toggleDialog(loginDialog); 123 - $id('account_menu').style.visibility = 'hidden'; 124 - }); 125 - 126 - $(accountMenu.querySelector('a[data-action=logout]')).addEventListener('click', (e) => { 127 - e.preventDefault(); 128 - logOut(); 129 - }); 130 - 131 75 window.appView = new BlueskyAPI('api.bsky.app', false); 132 76 window.blueAPI = new BlueskyAPI('blue.mackuba.eu', false); 133 77 window.accountAPI = new BlueskyAPI(undefined, true); 134 78 135 79 if (accountAPI.isLoggedIn) { 136 80 accountAPI.host = accountAPI.user.pdsEndpoint; 137 - hideMenuButton('login'); 81 + accountMenu.hideMenuButton('login'); 138 82 139 83 if (!isIncognito) { 140 84 window.api = accountAPI; 141 - showLoggedInStatus(true, api.user.avatar); 85 + accountMenu.showLoggedInStatus(true, api.user.avatar); 142 86 } else { 143 87 window.api = appView; 144 - showLoggedInStatus('incognito'); 145 - toggleMenuButton('incognito', true); 88 + accountMenu.showLoggedInStatus('incognito'); 89 + accountMenu.toggleMenuButtonCheck('incognito', true); 146 90 } 147 91 } else { 148 92 window.api = appView; 149 - hideMenuButton('logout'); 150 - hideMenuButton('incognito'); 93 + accountMenu.hideMenuButton('logout'); 94 + accountMenu.hideMenuButton('incognito'); 151 95 } 152 96 153 - toggleMenuButton('biohazard', window.biohazardEnabled !== false); 97 + accountMenu.toggleMenuButtonCheck('biohazard', window.biohazardEnabled !== false); 154 98 155 99 parseQueryParams(); 156 100 } ··· 248 192 $id('login').classList.toggle('expanded'); 249 193 } 250 194 251 - function toggleAccountMenu() { 252 - let menu = $id('account_menu'); 253 - menu.style.visibility = (menu.style.visibility == 'visible') ? 'hidden' : 'visible'; 254 - } 255 - 256 - /** @param {string} buttonName */ 257 - 258 - function showMenuButton(buttonName) { 259 - let button = $(accountMenu.querySelector(`a[data-action=${buttonName}]`)); 260 - let item = $(button.parentNode); 261 - item.style.display = 'list-item'; 262 - } 263 - 264 - /** @param {string} buttonName */ 265 - 266 - function hideMenuButton(buttonName) { 267 - let button = $(accountMenu.querySelector(`a[data-action=${buttonName}]`)); 268 - let item = $(button.parentNode); 269 - item.style.display = 'none'; 270 - } 271 - 272 - /** @param {string} buttonName, @param {boolean} state */ 273 - 274 - function toggleMenuButton(buttonName, state) { 275 - let button = $(accountMenu.querySelector(`a[data-action=${buttonName}]`)); 276 - let check = $(button.querySelector('.check')); 277 - check.style.display = (state) ? 'inline' : 'none'; 278 - } 279 - 280 - /** @param {boolean | 'incognito'} loggedIn, @param {string | undefined | null} [avatar] */ 281 - 282 - function showLoggedInStatus(loggedIn, avatar) { 283 - let account = $id('account'); 284 - 285 - if (loggedIn === true && avatar) { 286 - let button = $(account.querySelector('i')); 287 - 288 - let img = $tag('img.avatar', { src: avatar }); 289 - img.style.display = 'none'; 290 - img.addEventListener('load', () => { 291 - button.remove(); 292 - img.style.display = 'inline'; 293 - }); 294 - img.addEventListener('error', () => { 295 - showLoggedInStatus(true, null); 296 - }) 297 - 298 - account.append(img); 299 - } else if (loggedIn === false) { 300 - $id('account').innerHTML = `<i class="fa-regular fa-user-circle fa-xl"></i>`; 301 - } else if (loggedIn === 'incognito') { 302 - $id('account').innerHTML = `<i class="fa-solid fa-user-secret fa-lg"></i>`; 303 - } else { 304 - account.innerHTML = `<i class="fa-solid fa-user-circle fa-xl"></i>`; 305 - } 306 - } 307 - 308 195 function submitLogin() { 309 196 let handle = $id('login_handle', HTMLInputElement); 310 197 let password = $id('login_password', HTMLInputElement); ··· 327 214 submit.style.display = 'inline'; 328 215 cloudy.style.display = 'none'; 329 216 330 - loadCurrentUserAvatar(); 331 - showMenuButton('logout'); 332 - showMenuButton('incognito'); 333 - hideMenuButton('login'); 217 + accountMenu.loadCurrentUserAvatar(); 218 + 219 + accountMenu.showMenuButton('logout'); 220 + accountMenu.showMenuButton('incognito'); 221 + accountMenu.hideMenuButton('login'); 334 222 335 223 let params = new URLSearchParams(location.search); 336 224 let page = params.get('page'); ··· 371 259 let pds = new BlueskyAPI(pdsEndpoint, true); 372 260 await pds.logIn(identifier, password); 373 261 return pds; 374 - } 375 - 376 - function loadCurrentUserAvatar() { 377 - api.loadCurrentUserAvatar().then((url) => { 378 - showLoggedInStatus(true, url); 379 - }).catch((error) => { 380 - console.log(error); 381 - showLoggedInStatus(true, null); 382 - }); 383 262 } 384 263 385 264 function logOut() {
+1 -1
types.d.ts
··· 12 12 declare var isIncognito: boolean; 13 13 declare var biohazardEnabled: boolean; 14 14 declare var loginDialog: HTMLElement; 15 - declare var accountMenu: HTMLElement; 15 + declare var accountMenu: Menu; 16 16 declare var avatarPreloader: IntersectionObserver; 17 17 declare var threadPage: ThreadPage; 18 18 declare var postingStatsPage: PostingStatsPage;