commit
0a5d7267d5
728
package-lock.json
generated
728
package-lock.json
generated
File diff suppressed because it is too large
Load diff
12
package.json
12
package.json
|
@ -24,11 +24,11 @@
|
|||
"masto": "~5.11.3",
|
||||
"mem": "~9.0.2",
|
||||
"p-retry": "~5.1.2",
|
||||
"p-throttle": "~5.0.0",
|
||||
"preact": "~10.13.2",
|
||||
"p-throttle": "~5.1.0",
|
||||
"preact": "~10.15.0",
|
||||
"react-hotkeys-hook": "~4.4.0",
|
||||
"react-intersection-observer": "~9.4.3",
|
||||
"react-quick-pinch-zoom": "~4.6.0",
|
||||
"react-quick-pinch-zoom": "~4.9.0",
|
||||
"react-router-dom": "6.6.2",
|
||||
"string-length": "~5.0.1",
|
||||
"swiped-events": "~1.1.7",
|
||||
|
@ -44,12 +44,12 @@
|
|||
"@trivago/prettier-plugin-sort-imports": "~4.1.1",
|
||||
"postcss": "~8.4.23",
|
||||
"postcss-dark-theme-class": "~0.7.3",
|
||||
"postcss-preset-env": "~8.3.2",
|
||||
"postcss-preset-env": "~8.4.1",
|
||||
"twitter-text": "~3.1.0",
|
||||
"vite": "~4.3.5",
|
||||
"vite": "~4.3.8",
|
||||
"vite-plugin-generate-file": "~0.0.4",
|
||||
"vite-plugin-html-config": "~1.0.11",
|
||||
"vite-plugin-pwa": "~0.14.7",
|
||||
"vite-plugin-pwa": "~0.15.0",
|
||||
"vite-plugin-remove-console": "~2.1.1",
|
||||
"workbox-cacheable-response": "~6.5.4",
|
||||
"workbox-expiration": "~6.5.4",
|
||||
|
|
|
@ -59,8 +59,9 @@ registerRoute(iconsRoute);
|
|||
// - /api/v1/custom_emojis
|
||||
// - /api/v1/preferences
|
||||
// - /api/v1/lists/:id
|
||||
// - /api/v1/announcements
|
||||
const apiExtendedRoute = new RegExpRoute(
|
||||
/^https?:\/\/[^\/]+\/api\/v\d+\/(instance|custom_emojis|preferences|lists\/\d+)$/,
|
||||
/^https?:\/\/[^\/]+\/api\/v\d+\/(instance|custom_emojis|preferences|lists\/\d+|announcements)$/,
|
||||
new StaleWhileRevalidate({
|
||||
cacheName: 'api-extended',
|
||||
plugins: [
|
||||
|
|
|
@ -262,10 +262,10 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
|
|||
inset 0 3px var(--comment-line-color);
|
||||
}
|
||||
.timeline.contextual .replies[data-comments-level='4'] {
|
||||
overflow: auto;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.timeline.contextual .replies[data-comments-level='4']:has(.replies) {
|
||||
overflow: auto;
|
||||
overflow-x: auto;
|
||||
mask-image: linear-gradient(to left, transparent, black 32px);
|
||||
}
|
||||
.timeline.contextual
|
||||
|
|
|
@ -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 });
|
||||
|
|
3
src/assets/phanpy-bg.svg
Normal file
3
src/assets/phanpy-bg.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 90 85">
|
||||
<path fill="#fff" fill-opacity="0.4" fill-rule="evenodd" d="M88 64c6.4-29.6-2.4-55.2-31.7-62.7C31.9-4.9 8.1 11.6.8 42c-3.3 13.8 3.3 30.3 24.6 35.6l19.3 4.8c14.5 4 24.3 2.3 30.7-1.1a25 25 0 0 0 12.7-17.4Zm-8-1.8c5.4-24.7-1.1-46.7-25.7-53C34 4 14.8 18.6 8.8 44c-2.5 10.2 2.9 22 18.6 25.8l19.4 4.8c23.4 6.4 31.4-3.4 33.3-12.3ZM35.8 28.4c-3-1.4-14.5 6.4-15.3 17.8-.4 4.8 9 6.5 10.1-.3.7-4.8 2.6-9 4-12s2.3-5 1.2-5.5Zm19.8 15c-.5-5.9-1-10.6 1.7-11 4-.7 10.4 15.3 8.2 25.4-.6 3-9.6 1.6-9.4-4 .2-3.5-.2-7.1-.5-10.4Z" clip-rule="evenodd"/>
|
||||
</svg>
|
After Width: | Height: | Size: 614 B |
|
@ -493,7 +493,7 @@ function RelatedActions({ info, instance, authenticated }) {
|
|||
>
|
||||
<div class="shazam-container-inner">
|
||||
<p>
|
||||
Also followed by{' '}
|
||||
Followed by{' '}
|
||||
<span class="ib">
|
||||
{familiarFollowers.map((follower) => (
|
||||
<a
|
||||
|
|
|
@ -388,6 +388,7 @@
|
|||
background-color: var(--bg-faded-color);
|
||||
border-radius: 8px;
|
||||
margin: 8px 0 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#compose-container .poll-choices {
|
||||
|
|
|
@ -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',
|
||||
|
|
43
src/components/nav-menu.css
Normal file
43
src/components/nav-menu.css
Normal file
|
@ -0,0 +1,43 @@
|
|||
@media (min-width: 23em) {
|
||||
.nav-menu {
|
||||
display: flex;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
.nav-menu section {
|
||||
padding: 8px 0;
|
||||
width: 50%;
|
||||
}
|
||||
@keyframes phanpying {
|
||||
0% {
|
||||
background-position: 0 0, 0 0, 3em 85%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0 0, 0 0, 2em 90%;
|
||||
}
|
||||
}
|
||||
.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 left, var(--bg-blur-color), transparent),
|
||||
url(../assets/phanpy-bg.svg);
|
||||
background-repeat: no-repeat;
|
||||
/* background-size: auto, auto, 200%; */
|
||||
background-position: 0 0, 0 0, 2em 90%;
|
||||
background-blend-mode: normal, normal, overlay;
|
||||
box-shadow: inset 0 0 1px var(--bg-color);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
animation: phanpying 0.2s ease-in-out both;
|
||||
}
|
||||
.nav-menu section:last-child > .szh-menu__divider:first-child {
|
||||
display: none;
|
||||
}
|
||||
.nav-menu .szh-menu__item span {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
|
@ -1,11 +1,7 @@
|
|||
import {
|
||||
ControlledMenu,
|
||||
MenuDivider,
|
||||
MenuItem,
|
||||
useClick,
|
||||
useMenuState,
|
||||
} from '@szhsin/react-menu';
|
||||
import { useRef } from 'preact/hooks';
|
||||
import './nav-menu.css';
|
||||
|
||||
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 +43,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 +54,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 +75,18 @@ function NavMenu(props) {
|
|||
<Icon icon="menu" size={moreThanOneAccount ? 's' : 'l'} />
|
||||
</button>
|
||||
<ControlledMenu
|
||||
{...menuState}
|
||||
menuClassName="nav-menu"
|
||||
state={menuState}
|
||||
anchorRef={buttonRef}
|
||||
onClose={() => {
|
||||
toggleMenu(false);
|
||||
setMenuState(undefined);
|
||||
}}
|
||||
containerProps={{
|
||||
style: {
|
||||
zIndex: 10,
|
||||
},
|
||||
onClick: () => {
|
||||
toggleMenu(false);
|
||||
setMenuState(undefined);
|
||||
},
|
||||
}}
|
||||
portal={{
|
||||
|
@ -102,109 +100,113 @@ function NavMenu(props) {
|
|||
boundingBoxPadding="8 8 8 8"
|
||||
unmountOnClose
|
||||
>
|
||||
{!!snapStates.appVersion?.commitHash &&
|
||||
__COMMIT_HASH__ !== snapStates.appVersion.commitHash && (
|
||||
<section>
|
||||
{!!snapStates.appVersion?.commitHash &&
|
||||
__COMMIT_HASH__ !== snapStates.appVersion.commitHash && (
|
||||
<>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
const yes = confirm('Reload page now to update?');
|
||||
if (yes) {
|
||||
(async () => {
|
||||
try {
|
||||
location.reload();
|
||||
} catch (e) {}
|
||||
})();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon icon="sparkles" size="l" />{' '}
|
||||
<span>New update available…</span>
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
</>
|
||||
)}
|
||||
<MenuLink to="/">
|
||||
<Icon icon="home" size="l" /> <span>Home</span>
|
||||
</MenuLink>
|
||||
{authenticated && (
|
||||
<>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
const yes = confirm('Reload page now to update?');
|
||||
if (yes) {
|
||||
(async () => {
|
||||
try {
|
||||
location.reload();
|
||||
} catch (e) {}
|
||||
})();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon icon="sparkles" size="l" />{' '}
|
||||
<span>New update available…</span>
|
||||
</MenuItem>
|
||||
{showFollowing && (
|
||||
<MenuLink to="/following">
|
||||
<Icon icon="following" size="l" /> <span>Following</span>
|
||||
</MenuLink>
|
||||
)}
|
||||
<MenuLink to="/mentions">
|
||||
<Icon icon="at" size="l" /> <span>Mentions</span>
|
||||
</MenuLink>
|
||||
<MenuLink to="/notifications">
|
||||
<Icon icon="notification" size="l" /> <span>Notifications</span>
|
||||
{snapStates.notificationsShowNew && (
|
||||
<sup title="New" style={{ opacity: 0.5 }}>
|
||||
{' '}
|
||||
•
|
||||
</sup>
|
||||
)}
|
||||
</MenuLink>
|
||||
<MenuDivider />
|
||||
<MenuLink to="/l">
|
||||
<Icon icon="list" size="l" /> <span>Lists</span>
|
||||
</MenuLink>
|
||||
<MenuLink to="/ft">
|
||||
<Icon icon="hashtag" size="l" /> <span>Followed Hashtags</span>
|
||||
</MenuLink>
|
||||
<MenuLink to="/b">
|
||||
<Icon icon="bookmark" size="l" /> <span>Bookmarks</span>
|
||||
</MenuLink>
|
||||
<MenuLink to="/f">
|
||||
<Icon icon="heart" size="l" /> <span>Favourites</span>
|
||||
</MenuLink>
|
||||
</>
|
||||
)}
|
||||
<MenuLink to="/">
|
||||
<Icon icon="home" size="l" /> <span>Home</span>
|
||||
</MenuLink>
|
||||
{authenticated && (
|
||||
<>
|
||||
{showFollowing && (
|
||||
<MenuLink to="/following">
|
||||
<Icon icon="following" size="l" /> <span>Following</span>
|
||||
</MenuLink>
|
||||
)}
|
||||
<MenuLink to="/mentions">
|
||||
<Icon icon="at" size="l" /> <span>Mentions</span>
|
||||
</MenuLink>
|
||||
<MenuLink to="/notifications">
|
||||
<Icon icon="notification" size="l" /> <span>Notifications</span>
|
||||
{snapStates.notificationsShowNew && (
|
||||
<sup title="New" style={{ opacity: 0.5 }}>
|
||||
{' '}
|
||||
•
|
||||
</sup>
|
||||
<MenuDivider />
|
||||
<MenuLink to={`/search`}>
|
||||
<Icon icon="search" size="l" /> <span>Search</span>
|
||||
</MenuLink>
|
||||
<MenuLink to={`/${instance}/p/l`}>
|
||||
<Icon icon="group" size="l" /> <span>Local</span>
|
||||
</MenuLink>
|
||||
<MenuLink to={`/${instance}/p`}>
|
||||
<Icon icon="earth" size="l" /> <span>Federated</span>
|
||||
</MenuLink>
|
||||
<MenuLink to={`/${instance}/trending`}>
|
||||
<Icon icon="chart" size="l" /> <span>Trending</span>
|
||||
</MenuLink>
|
||||
</section>
|
||||
<section>
|
||||
{authenticated && (
|
||||
<>
|
||||
<MenuDivider />
|
||||
{currentAccount?.info?.id && (
|
||||
<MenuLink to={`/${instance}/a/${currentAccount.info.id}`}>
|
||||
<Icon icon="user" size="l" /> <span>Profile</span>
|
||||
</MenuLink>
|
||||
)}
|
||||
</MenuLink>
|
||||
<MenuDivider />
|
||||
<MenuLink to="/l">
|
||||
<Icon icon="list" size="l" /> <span>Lists</span>
|
||||
</MenuLink>
|
||||
<MenuLink to="/ft">
|
||||
<Icon icon="hashtag" size="l" /> <span>Followed Hashtags</span>
|
||||
</MenuLink>
|
||||
<MenuLink to="/b">
|
||||
<Icon icon="bookmark" size="l" /> <span>Bookmarks</span>
|
||||
</MenuLink>
|
||||
<MenuLink to="/f">
|
||||
<Icon icon="heart" size="l" /> <span>Favourites</span>
|
||||
</MenuLink>
|
||||
</>
|
||||
)}
|
||||
<MenuDivider />
|
||||
<MenuLink to={`/search`}>
|
||||
<Icon icon="search" size="l" /> <span>Search</span>
|
||||
</MenuLink>
|
||||
<MenuLink to={`/${instance}/p/l`}>
|
||||
<Icon icon="group" size="l" /> <span>Local</span>
|
||||
</MenuLink>
|
||||
<MenuLink to={`/${instance}/p`}>
|
||||
<Icon icon="earth" size="l" /> <span>Federated</span>
|
||||
</MenuLink>
|
||||
<MenuLink to={`/${instance}/trending`}>
|
||||
<Icon icon="chart" size="l" /> <span>Trending</span>
|
||||
</MenuLink>
|
||||
{authenticated && (
|
||||
<>
|
||||
<MenuDivider />
|
||||
{currentAccount?.info?.id && (
|
||||
<MenuLink to={`/${instance}/a/${currentAccount.info.id}`}>
|
||||
<Icon icon="user" size="l" /> <span>Profile</span>
|
||||
</MenuLink>
|
||||
)}
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
states.showAccounts = true;
|
||||
}}
|
||||
>
|
||||
<Icon icon="group" size="l" /> <span>Accounts…</span>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
states.showShortcutsSettings = true;
|
||||
}}
|
||||
>
|
||||
<Icon icon="shortcut" size="l" />{' '}
|
||||
<span>Shortcuts Settings…</span>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
states.showSettings = true;
|
||||
}}
|
||||
>
|
||||
<Icon icon="gear" size="l" /> <span>Settings…</span>
|
||||
</MenuItem>
|
||||
</>
|
||||
)}
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
states.showAccounts = true;
|
||||
}}
|
||||
>
|
||||
<Icon icon="group" size="l" /> <span>Accounts…</span>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
states.showShortcutsSettings = true;
|
||||
}}
|
||||
>
|
||||
<Icon icon="shortcut" size="l" />{' '}
|
||||
<span>Shortcuts Settings…</span>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
states.showSettings = true;
|
||||
}}
|
||||
>
|
||||
<Icon icon="gear" size="l" /> <span>Settings…</span>
|
||||
</MenuItem>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
</ControlledMenu>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
|
@ -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;
|
||||
|
@ -408,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();
|
||||
|
@ -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] = [];
|
||||
}
|
||||
|
@ -1102,7 +1104,7 @@ function Status({
|
|||
<>
|
||||
<Icon
|
||||
icon={visibilityIconsMap[visibility]}
|
||||
alt={visibility}
|
||||
alt={visibilityText[visibility]}
|
||||
/>{' '}
|
||||
<a href={url} target="_blank">
|
||||
<time
|
||||
|
@ -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];
|
||||
|
|
|
@ -103,14 +103,11 @@ function Notifications() {
|
|||
setUIState('loading');
|
||||
(async () => {
|
||||
try {
|
||||
const fetchNotificationsPromise = fetchNotifications(firstLoad);
|
||||
const fetchFollowRequestsPromise = fetchFollowRequests();
|
||||
const fetchAnnouncementsPromise = fetchAnnouncements();
|
||||
const { done } = await fetchNotifications(firstLoad);
|
||||
setShowMore(!done);
|
||||
|
||||
if (firstLoad) {
|
||||
const requests = await fetchFollowRequestsPromise;
|
||||
setFollowRequests(requests);
|
||||
const announcements = await fetchAnnouncementsPromise;
|
||||
announcements.sort((a, b) => {
|
||||
// Sort by updatedAt first, then createdAt
|
||||
|
@ -119,8 +116,13 @@ function Notifications() {
|
|||
return bDate - aDate;
|
||||
});
|
||||
setAnnouncements(announcements);
|
||||
const requests = await fetchFollowRequestsPromise;
|
||||
setFollowRequests(requests);
|
||||
}
|
||||
|
||||
const { done } = await fetchNotificationsPromise;
|
||||
setShowMore(!done);
|
||||
|
||||
setUIState('default');
|
||||
} catch (e) {
|
||||
setUIState('error');
|
||||
|
|
|
@ -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,15 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
|
|||
saveStatus(status, instance, {
|
||||
skipThreading: true,
|
||||
});
|
||||
|
||||
if (
|
||||
status.inReplyToId &&
|
||||
!descendants.find((s) => s.id === status.inReplyToId) &&
|
||||
status.inReplyToId !== heroStatus.id
|
||||
) {
|
||||
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 +306,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) => ({
|
||||
|
@ -591,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 (
|
||||
<div
|
||||
|
|
|
@ -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,
|
||||
|
|
12
src/utils/locale-match.jsx
Normal file
12
src/utils/locale-match.jsx
Normal file
|
@ -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;
|
|
@ -110,6 +110,7 @@ export function hideAllModals() {
|
|||
}
|
||||
|
||||
export function statusKey(id, instance) {
|
||||
if (!id) return;
|
||||
return instance ? `${instance}/${id}` : id;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue