From 104665cafe7c38afed81a9f9f3f93d4e53995452 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Thu, 18 May 2023 21:43:58 +0800 Subject: [PATCH 01/23] Don't need "Also" --- src/components/account-info.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/account-info.jsx b/src/components/account-info.jsx index a1e30168..751c062d 100644 --- a/src/components/account-info.jsx +++ b/src/components/account-info.jsx @@ -493,7 +493,7 @@ function RelatedActions({ info, instance, authenticated }) { >

- Also followed by{' '} + Followed by{' '} {familiarFollowers.map((follower) => ( Date: Thu, 18 May 2023 23:42:49 +0800 Subject: [PATCH 02/23] Add check for "missing" statuses in context --- src/pages/status.jsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/pages/status.jsx b/src/pages/status.jsx index e09affea..18d5a1c0 100644 --- a/src/pages/status.jsx +++ b/src/pages/status.jsx @@ -248,10 +248,17 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) { totalDescendants.current = descendants?.length || 0; + const missingStatuses = new Set(); ancestors.forEach((status) => { saveStatus(status, instance, { skipThreading: true, }); + if ( + status.inReplyToId && + !ancestors.find((s) => s.id === status.inReplyToId) + ) { + missingStatuses.add(status.inReplyToId); + } }); const ancestorsIsThread = ancestors.every( (s) => s.account.id === heroStatus.account.id, @@ -261,6 +268,14 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) { saveStatus(status, instance, { skipThreading: true, }); + + if ( + status.inReplyToId && + !descendants.find((s) => s.id === status.inReplyToId) + ) { + missingStatuses.add(status.inReplyToId); + } + if (status.inReplyToAccountId === status.account.id) { // If replying to self, it's part of the thread, level 1 nestedDescendants.push(status); @@ -290,6 +305,9 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) { }); console.log({ ancestors, descendants, nestedDescendants }); + if (missingStatuses.size) { + console.error('Missing statuses', [...missingStatuses]); + } function expandReplies(_replies) { return _replies?.map((_r) => ({ From f368071fcb670dcc63234aef22f865ca92f3ff0a Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Fri, 19 May 2023 09:03:20 +0800 Subject: [PATCH 03/23] ul, ol need a bit more space --- src/components/status.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/status.css b/src/components/status.css index 755f3c13..0e417b1d 100644 --- a/src/components/status.css +++ b/src/components/status.css @@ -518,7 +518,7 @@ .status .content > div > :is(ul, ol) { margin-block: min(0.75em, 12px); margin-inline: 0; - padding-inline-start: 1em; + padding-inline-start: 1.5em; } .status .content .invisible { display: none; From 0df21af374c607c775b6b6ee3f05d1a0ecbe4855 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Sat, 20 May 2023 01:06:16 +0800 Subject: [PATCH 04/23] Handle cases when statusID is not used --- src/components/status.jsx | 5 ++++- src/utils/states.js | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/status.jsx b/src/components/status.jsx index c09277ac..30cba751 100644 --- a/src/components/status.jsx +++ b/src/components/status.jsx @@ -105,10 +105,11 @@ function Status({ const { instance: currentInstance } = api(); const sameInstance = instance === currentInstance; - const sKey = statusKey(statusID, instance); + let sKey = statusKey(statusID, instance); const snapStates = useSnapshot(states); if (!status) { status = snapStates.statuses[sKey] || snapStates.statuses[statusID]; + sKey = statusKey(status?.id, instance); } if (!status) { return null; @@ -977,6 +978,7 @@ function Status({ (result) => { if (!result) return; a.removeAttribute('target'); + if (!sKey) return; if (!Array.isArray(states.statusQuotes[sKey])) { states.statusQuotes[sKey] = []; } @@ -1954,6 +1956,7 @@ function FilteredStatus({ status, filterInfo, instance, containerProps = {} }) { } const QuoteStatuses = memo(({ id, instance, level = 0 }) => { + if (!id || !instance) return; const snapStates = useSnapshot(states); const sKey = statusKey(id, instance); const quotes = snapStates.statusQuotes[sKey]; diff --git a/src/utils/states.js b/src/utils/states.js index da3771f1..fb76cc41 100644 --- a/src/utils/states.js +++ b/src/utils/states.js @@ -110,6 +110,7 @@ export function hideAllModals() { } export function statusKey(id, instance) { + if (!id) return; return instance ? `${instance}/${id}` : id; } From 3acaac2d471637a5e973f8dde5650a4c47ca09c4 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Sat, 20 May 2023 01:19:25 +0800 Subject: [PATCH 05/23] Check missing status with hero status too --- src/pages/status.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/status.jsx b/src/pages/status.jsx index 18d5a1c0..a681b55e 100644 --- a/src/pages/status.jsx +++ b/src/pages/status.jsx @@ -271,7 +271,8 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) { if ( status.inReplyToId && - !descendants.find((s) => s.id === status.inReplyToId) + !descendants.find((s) => s.id === status.inReplyToId) && + status.inReplyToId !== heroStatus.id ) { missingStatuses.add(status.inReplyToId); } From bcd91851d28a8e91be24adfcac6e73af53159383 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Sat, 20 May 2023 10:08:41 +0800 Subject: [PATCH 06/23] Must decode the code! --- src/app.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app.jsx b/src/app.jsx index 63357bff..0d303bbb 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -91,7 +91,9 @@ function App() { useEffect(() => { const instanceURL = store.local.get('instanceURL'); - const code = (window.location.search.match(/code=([^&]+)/) || [])[1]; + const code = decodeURIComponent( + (window.location.search.match(/code=([^&]+)/) || [, ''])[1], + ); if (code) { console.log({ code }); From 3524eb84c3e47f510c311584b88529444d8e84e8 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Sat, 20 May 2023 10:22:05 +0800 Subject: [PATCH 07/23] Make poll form take full width --- src/components/compose.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/compose.css b/src/components/compose.css index 600f7b07..2c1af00e 100644 --- a/src/components/compose.css +++ b/src/components/compose.css @@ -388,6 +388,7 @@ background-color: var(--bg-faded-color); border-radius: 8px; margin: 8px 0 0; + display: block; } #compose-container .poll-choices { From 04b3fd9545b97913029220840529a52eb7d4904d Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Sat, 20 May 2023 13:02:47 +0800 Subject: [PATCH 08/23] Fix media click handler not "refreshing" --- src/pages/status.jsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/pages/status.jsx b/src/pages/status.jsx index a681b55e..2ea035c9 100644 --- a/src/pages/status.jsx +++ b/src/pages/status.jsx @@ -610,14 +610,17 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) { const initialPageState = useRef(showMedia ? 'media+status' : 'status'); - const handleMediaClick = useCallback((e, i, media, status) => { - e.preventDefault(); - e.stopPropagation(); - setSearchParams({ - media: i + 1, - mediaStatusID: status.id, - }); - }, []); + const handleMediaClick = useCallback( + (e, i, media, status) => { + e.preventDefault(); + e.stopPropagation(); + setSearchParams({ + media: i + 1, + mediaStatusID: status.id, + }); + }, + [id], + ); return (

Date: Sat, 20 May 2023 14:14:35 +0800 Subject: [PATCH 09/23] Try/catch match because it throws when there's invalid language code --- src/components/compose.jsx | 4 ++-- src/components/status.jsx | 6 +++--- src/utils/get-translate-target-language.jsx | 5 ++--- src/utils/locale-match.jsx | 12 ++++++++++++ 4 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 src/utils/locale-match.jsx diff --git a/src/components/compose.jsx b/src/components/compose.jsx index 5d224337..abe4621b 100644 --- a/src/components/compose.jsx +++ b/src/components/compose.jsx @@ -1,6 +1,5 @@ import './compose.css'; -import { match } from '@formatjs/intl-localematcher'; import '@github/text-expander-element'; import equal from 'fast-deep-equal'; import { forwardRef } from 'preact/compat'; @@ -16,6 +15,7 @@ import urlRegex from '../data/url-regex'; import { api } from '../utils/api'; import db from '../utils/db'; import emojifyText from '../utils/emojify-text'; +import localeMatch from '../utils/locale-match'; import openCompose from '../utils/open-compose'; import states, { saveStatus } from '../utils/states'; import store from '../utils/store'; @@ -85,7 +85,7 @@ const observer = new IntersectionObserver((entries) => { }); observer.observe(menu); -const DEFAULT_LANG = match( +const DEFAULT_LANG = localeMatch( [new Intl.DateTimeFormat().resolvedOptions().locale, ...navigator.languages], supportedLanguages.map((l) => l[0]), 'en', diff --git a/src/components/status.jsx b/src/components/status.jsx index 30cba751..65391e5b 100644 --- a/src/components/status.jsx +++ b/src/components/status.jsx @@ -1,6 +1,5 @@ import './status.css'; -import { match } from '@formatjs/intl-localematcher'; import '@justinribeiro/lite-youtube'; import { ControlledMenu, @@ -33,6 +32,7 @@ import getHTMLText from '../utils/getHTMLText'; import handleContentLinks from '../utils/handle-content-links'; import htmlContentLength from '../utils/html-content-length'; import isMastodonLinkMaybe from '../utils/isMastodonLinkMaybe'; +import localeMatch from '../utils/locale-match'; import niceDateTime from '../utils/nice-date-time'; import shortenNumber from '../utils/shorten-number'; import showToast from '../utils/show-toast'; @@ -409,9 +409,9 @@ function Status({ const differentLanguage = language && language !== targetLanguage && - !match([language], [targetLanguage]) && + !localeMatch([language], [targetLanguage]) && !contentTranslationHideLanguages.find( - (l) => language === l || match([language], [l]), + (l) => language === l || localeMatch([language], [l]), ); const menuInstanceRef = useRef(); diff --git a/src/utils/get-translate-target-language.jsx b/src/utils/get-translate-target-language.jsx index ce837079..7a598643 100644 --- a/src/utils/get-translate-target-language.jsx +++ b/src/utils/get-translate-target-language.jsx @@ -1,7 +1,6 @@ -import { match } from '@formatjs/intl-localematcher'; - import translationTargetLanguages from '../data/lingva-target-languages'; +import localeMatch from './locale-match'; import states from './states'; function getTranslateTargetLanguage(fromSettings = false) { @@ -11,7 +10,7 @@ function getTranslateTargetLanguage(fromSettings = false) { return contentTranslationTargetLanguage; } } - return match( + return localeMatch( [ new Intl.DateTimeFormat().resolvedOptions().locale, ...navigator.languages, diff --git a/src/utils/locale-match.jsx b/src/utils/locale-match.jsx new file mode 100644 index 00000000..f67d1fad --- /dev/null +++ b/src/utils/locale-match.jsx @@ -0,0 +1,12 @@ +import { match } from '@formatjs/intl-localematcher'; + +function localeMatch(...args) { + // Wrap in try/catch because localeMatcher throws on invalid locales + try { + return match(...args); + } catch (e) { + return false; + } +} + +export default localeMatch; From a52bd7ca5df4f9ff26c99b492f617d366ab8e920 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Sat, 20 May 2023 17:08:20 +0800 Subject: [PATCH 10/23] Attempt to fix lost focus when closing nav menu --- src/components/nav-menu.jsx | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/components/nav-menu.jsx b/src/components/nav-menu.jsx index 402331d9..7d6fbb8a 100644 --- a/src/components/nav-menu.jsx +++ b/src/components/nav-menu.jsx @@ -1,11 +1,5 @@ -import { - ControlledMenu, - MenuDivider, - MenuItem, - useClick, - useMenuState, -} from '@szhsin/react-menu'; -import { useRef } from 'preact/hooks'; +import { ControlledMenu, MenuDivider, MenuItem } from '@szhsin/react-menu'; +import { useRef, useState } from 'preact/hooks'; import { useLongPress } from 'use-long-press'; import { useSnapshot } from 'valtio'; @@ -47,8 +41,7 @@ function NavMenu(props) { ); const buttonRef = useRef(); - const [menuState, toggleMenu] = useMenuState(); - const anchorProps = useClick(menuState.state, toggleMenu); + const [menuState, setMenuState] = useState(undefined); return ( <> @@ -59,7 +52,9 @@ function NavMenu(props) { moreThanOneAccount ? 'with-avatar' : '' } ${open ? 'active' : ''}`} style={{ position: 'relative' }} - {...anchorProps} + onClick={() => { + setMenuState((state) => (!state ? 'open' : undefined)); + }} onContextMenu={(e) => { e.preventDefault(); states.showAccounts = true; @@ -78,17 +73,17 @@ function NavMenu(props) { { - toggleMenu(false); + setMenuState(undefined); }} containerProps={{ style: { zIndex: 10, }, onClick: () => { - toggleMenu(false); + setMenuState(undefined); }, }} portal={{ From 4b45375b4c17d0776c4e3a1dc7a3aa5fc7f1c4ec Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Sat, 20 May 2023 17:46:58 +0800 Subject: [PATCH 11/23] Transform nav menu into 2 columns --- src/components/nav-menu.css | 26 +++++ src/components/nav-menu.jsx | 203 +++++++++++++++++++----------------- 2 files changed, 131 insertions(+), 98 deletions(-) create mode 100644 src/components/nav-menu.css diff --git a/src/components/nav-menu.css b/src/components/nav-menu.css new file mode 100644 index 00000000..a3bb8830 --- /dev/null +++ b/src/components/nav-menu.css @@ -0,0 +1,26 @@ +@media (min-width: 23em) { + .nav-menu { + display: flex; + width: auto; + padding: 0; + /* 1px background image */ + background-image: linear-gradient( + to right, + var(--divider-color), + var(--divider-color) + ); + background-size: 1px 1px; + background-repeat: repeat-y; + background-position: center; + } + .nav-menu section { + width: 50%; + padding: 8px 0; + } + .nav-menu section:last-child > .szh-menu__divider:first-child { + display: none; + } + .nav-menu .szh-menu__item span { + white-space: normal; + } +} diff --git a/src/components/nav-menu.jsx b/src/components/nav-menu.jsx index 7d6fbb8a..45793528 100644 --- a/src/components/nav-menu.jsx +++ b/src/components/nav-menu.jsx @@ -1,3 +1,5 @@ +import './nav-menu.css'; + import { ControlledMenu, MenuDivider, MenuItem } from '@szhsin/react-menu'; import { useRef, useState } from 'preact/hooks'; import { useLongPress } from 'use-long-press'; @@ -73,6 +75,7 @@ function NavMenu(props) { { @@ -97,109 +100,113 @@ function NavMenu(props) { boundingBoxPadding="8 8 8 8" unmountOnClose > - {!!snapStates.appVersion?.commitHash && - __COMMIT_HASH__ !== snapStates.appVersion.commitHash && ( +
+ {!!snapStates.appVersion?.commitHash && + __COMMIT_HASH__ !== snapStates.appVersion.commitHash && ( + <> + { + const yes = confirm('Reload page now to update?'); + if (yes) { + (async () => { + try { + location.reload(); + } catch (e) {} + })(); + } + }} + > + {' '} + New update available… + + + + )} + + Home + + {authenticated && ( <> - { - const yes = confirm('Reload page now to update?'); - if (yes) { - (async () => { - try { - location.reload(); - } catch (e) {} - })(); - } - }} - > - {' '} - New update available… - + {showFollowing && ( + + Following + + )} + + Mentions + + + Notifications + {snapStates.notificationsShowNew && ( + + {' '} + • + + )} + + + Lists + + + Followed Hashtags + + + Bookmarks + + + Favourites + )} - - Home - - {authenticated && ( - <> - {showFollowing && ( - - Following - - )} - - Mentions - - - Notifications - {snapStates.notificationsShowNew && ( - - {' '} - • - + + + Search + + + Local + + + Federated + + + Trending + +
+
+ {authenticated && ( + <> + + {currentAccount?.info?.id && ( + + Profile + )} - - - - Lists - - - Followed Hashtags - - - Bookmarks - - - Favourites - - - )} - - - Search - - - Local - - - Federated - - - Trending - - {authenticated && ( - <> - - {currentAccount?.info?.id && ( - - Profile - - )} - { - states.showAccounts = true; - }} - > - Accounts… - - { - states.showShortcutsSettings = true; - }} - > - {' '} - Shortcuts Settings… - - { - states.showSettings = true; - }} - > - Settings… - - - )} + { + states.showAccounts = true; + }} + > + Accounts… + + { + states.showShortcutsSettings = true; + }} + > + {' '} + Shortcuts Settings… + + { + states.showSettings = true; + }} + > + Settings… + + + )} +
); From 4b48bab2bbbca08bddc8caf798e7a9bccaf37b94 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Sat, 20 May 2023 19:35:58 +0800 Subject: [PATCH 12/23] Make nav menu look a bit nicer --- src/components/nav-menu.css | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/nav-menu.css b/src/components/nav-menu.css index a3bb8830..320e96e4 100644 --- a/src/components/nav-menu.css +++ b/src/components/nav-menu.css @@ -3,24 +3,24 @@ display: flex; width: auto; padding: 0; - /* 1px background image */ - background-image: linear-gradient( - to right, - var(--divider-color), - var(--divider-color) - ); - background-size: 1px 1px; - background-repeat: repeat-y; - background-position: center; } .nav-menu section { - width: 50%; padding: 8px 0; } + .nav-menu section:last-child { + background-color: var(--bg-faded-color); + background-image: linear-gradient( + to right, + var(--divider-color) 1px, + transparent 1px + ), + linear-gradient(to bottom, var(--bg-blur-color), transparent); + box-shadow: inset 0 0 1px var(--bg-color); + } .nav-menu section:last-child > .szh-menu__divider:first-child { display: none; } - .nav-menu .szh-menu__item span { + .nav-menu section:last-child .szh-menu__item span { white-space: normal; } } From 9a10bc0fec51d05b69cdd75566622bfea2eaa123 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Sat, 20 May 2023 19:43:32 +0800 Subject: [PATCH 13/23] Use proper alt --- src/components/status.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/status.jsx b/src/components/status.jsx index 65391e5b..b776299f 100644 --- a/src/components/status.jsx +++ b/src/components/status.jsx @@ -1104,7 +1104,7 @@ function Status({ <> {' '}