From 1dd64c4e19c779a9181aeb7128ff0c4e893828ee Mon Sep 17 00:00:00 2001 From: xmflsct Date: Mon, 6 Feb 2023 14:00:40 +0100 Subject: [PATCH 01/17] Fix link matching crashes --- package.json | 2 +- src/components/openLink.ts | 10 +--------- src/utils/helpers/urlMatcher.ts | 17 +++++++++++------ src/utils/queryHooks/account.ts | 2 +- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index d602f225..3264ce49 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tooot", - "version": "4.8.7", + "version": "4.8.8", "description": "tooot for Mastodon", "author": "xmflsct ", "license": "GPL-3.0-or-later", diff --git a/src/components/openLink.ts b/src/components/openLink.ts index e5790c28..cf4e5f2f 100644 --- a/src/components/openLink.ts +++ b/src/components/openLink.ts @@ -46,17 +46,9 @@ const openLink = async (url: string, navigation?: any) => { // If an account can be found if (match?.account) { - if (!match.account._remote && match.account.id) { - handleNavigation('Tab-Shared-Account', { account: match.account.id }) - return - } - let response: Mastodon.Account | undefined = undefined - const queryKey: QueryKeyAccount = [ - 'Account', - { id: match.account.id, url: url, _remote: match.account._remote } - ] + const queryKey: QueryKeyAccount = ['Account', { url: url, _remote: match.account._remote }] const cache = queryClient.getQueryData(queryKey) if (cache) { diff --git a/src/utils/helpers/urlMatcher.ts b/src/utils/helpers/urlMatcher.ts index 9f70780a..eb3b91c5 100644 --- a/src/utils/helpers/urlMatcher.ts +++ b/src/utils/helpers/urlMatcher.ts @@ -9,7 +9,7 @@ export const urlMatcher = ( ): | { domain: string - account?: Partial> + account?: Partial> status?: Partial> } | undefined => { @@ -24,13 +24,18 @@ export const urlMatcher = ( const _remote = parsed.hostname !== getAccountStorage.string('auth.domain') let statusId: string | undefined - let accountId: string | undefined let accountAcct: string | undefined const segments = parsed.pathname.split('/') const last = segments[segments.length - 1] const length = segments.length // there is a starting slash + const testAndAssignStatusId = (id: string) => { + if (!!parseInt(id)) { + statusId = id + } + } + switch (last?.startsWith('@')) { case true: if (length === 2 || (length === 3 && segments[length - 2] === 'web')) { @@ -45,14 +50,14 @@ export const urlMatcher = ( if (nextToLast === 'statuses') { if (length === 4 && segments[length - 3] === 'web') { // https://social.xmflsct.com/web/statuses/105590085754428765 <- old - statusId = last + testAndAssignStatusId(last) } else if ( length === 5 && segments[length - 2] === 'statuses' && segments[length - 4] === 'users' ) { // https://social.xmflsct.com/users/tooot/statuses/105590085754428765 <- default Mastodon - statusId = last + testAndAssignStatusId(last) // accountAcct = `@${segments[length - 3]}@${domain}` } } else if ( @@ -61,7 +66,7 @@ export const urlMatcher = ( ) { // https://social.xmflsct.com/web/@tooot/105590085754428765 <- pretty Mastodon v3.5 and below // https://social.xmflsct.com/@tooot/105590085754428765 <- pretty Mastodon v4.0 and above - statusId = last + testAndAssignStatusId(last) // accountAcct = `${nextToLast}@${domain}` } } @@ -70,7 +75,7 @@ export const urlMatcher = ( return { domain, - ...((accountId || accountAcct) && { account: { id: accountId, acct: accountAcct, _remote } }), + ...(accountAcct && { account: { acct: accountAcct, _remote } }), ...(statusId && { status: { id: statusId, _remote } }) } } diff --git a/src/utils/queryHooks/account.ts b/src/utils/queryHooks/account.ts index 61c2bfca..852336b3 100644 --- a/src/utils/queryHooks/account.ts +++ b/src/utils/queryHooks/account.ts @@ -25,7 +25,7 @@ const accountQueryFunction = async ({ queryKey }: QueryFunctionContext Date: Mon, 6 Feb 2023 18:58:55 +0100 Subject: [PATCH 02/17] Fix some toots not interactable --- src/screens/Tabs/Shared/Toot.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/screens/Tabs/Shared/Toot.tsx b/src/screens/Tabs/Shared/Toot.tsx index fcd1384f..75f49ca6 100644 --- a/src/screens/Tabs/Shared/Toot.tsx +++ b/src/screens/Tabs/Shared/Toot.tsx @@ -248,7 +248,11 @@ const TabSharedToot: React.FC> = ({ body: data.map(remote => { const localMatch = old?.pages[0].body.find(local => local.uri === remote.uri) if (localMatch) { - return { ...localMatch, _level: remote._level } + return { + ...localMatch, + _level: remote._level, + key: `${localMatch.id}_remote` + } } else { return appendRemote.status(remote) } @@ -275,6 +279,7 @@ const TabSharedToot: React.FC> = ({ ref={flRef} windowSize={5} data={query.data?.pages?.[0].body} + extraData={query.dataUpdatedAt} renderItem={({ item, index }) => { const prev = query.data?.pages[0].body[index - 1]?._level || 0 const curr = item._level || 0 From be2c223142ab30ebc56c24a508a5ef8395ac0931 Mon Sep 17 00:00:00 2001 From: xmflsct Date: Mon, 6 Feb 2023 19:07:22 +0100 Subject: [PATCH 03/17] Mark id on load With the introduction of throttle, it is crucial to log on load otherwise there won't be much match after that --- src/components/Timeline/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Timeline/index.tsx b/src/components/Timeline/index.tsx index b896e57f..96b3b926 100644 --- a/src/components/Timeline/index.tsx +++ b/src/components/Timeline/index.tsx @@ -160,7 +160,7 @@ const Timeline: React.FC = ({ viewabilityConfig: { minimumViewTime: 300, itemVisiblePercentThreshold: 80, - waitForInteraction: true + waitForInteraction: false }, onViewableItemsChanged: ({ viewableItems }) => { const marker = readMarker ? getAccountStorage.string(readMarker) : undefined From f98b8946dc759f7c3445b63e0981bc63052fa45c Mon Sep 17 00:00:00 2001 From: xmflsct Date: Tue, 7 Feb 2023 13:56:50 +0100 Subject: [PATCH 04/17] Refine marker --- src/components/Timeline/index.tsx | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/components/Timeline/index.tsx b/src/components/Timeline/index.tsx index 96b3b926..48da5cfe 100644 --- a/src/components/Timeline/index.tsx +++ b/src/components/Timeline/index.tsx @@ -138,10 +138,16 @@ const Timeline: React.FC = ({ const latestMarker = useRef() const updateMarkers = useCallback( - throttle( - () => readMarker && setAccountStorage([{ key: readMarker, value: latestMarker.current }]), - 1000 * 15 - ), + throttle(() => { + if (readMarker) { + const currentMarker = getAccountStorage.string(readMarker) || '0' + if ((latestMarker.current || '0') > currentMarker) { + setAccountStorage([{ key: readMarker, value: latestMarker.current }]) + } else { + // setAccountStorage([{ key: readMarker, value: '105250709762254246' }]) + } + } + }, 1000 * 15), [] ) readMarker && @@ -159,24 +165,14 @@ const Timeline: React.FC = ({ { viewabilityConfig: { minimumViewTime: 300, - itemVisiblePercentThreshold: 80, + itemVisiblePercentThreshold: 10, waitForInteraction: false }, onViewableItemsChanged: ({ viewableItems }) => { - const marker = readMarker ? getAccountStorage.string(readMarker) : undefined - const firstItemId = viewableItems.filter(item => item.isViewable)[0]?.item.id - if ( - !isFetchingPrev.value && - !isRefetching && - firstItemId && - firstItemId > (marker || '0') - ) { + if (!isFetchingPrev.value && !isRefetching && firstItemId) { latestMarker.current = firstItemId updateMarkers() - } else { - // latestMarker.current = '105250709762254246' - // updateMarkers() } } } From fd114ed0f02a472cb0520f5be088360534d1590f Mon Sep 17 00:00:00 2001 From: xmflsct Date: Tue, 7 Feb 2023 15:06:04 +0100 Subject: [PATCH 05/17] Check for Wildebeest's account ID format --- src/components/Timeline/Default.tsx | 9 ++++----- src/components/Timeline/Notifications.tsx | 9 ++++----- src/components/Timeline/Shared/Actions.tsx | 8 ++++---- src/components/Timeline/Shared/Context.tsx | 2 +- src/components/Timeline/Shared/Poll.tsx | 4 ++-- src/components/contextMenu/account.ts | 12 ++++++------ src/components/contextMenu/status.ts | 19 ++++++++++--------- .../Shared/Account/Information/Actions.tsx | 7 +++---- src/utils/helpers/isMyAccount.ts | 9 +++++++++ 9 files changed, 43 insertions(+), 36 deletions(-) create mode 100644 src/utils/helpers/isMyAccount.ts diff --git a/src/components/Timeline/Default.tsx b/src/components/Timeline/Default.tsx index 2ab6ae9f..b21a2e34 100644 --- a/src/components/Timeline/Default.tsx +++ b/src/components/Timeline/Default.tsx @@ -12,11 +12,11 @@ import TimelinePoll from '@components/Timeline/Shared/Poll' import { useNavigation } from '@react-navigation/native' import { StackNavigationProp } from '@react-navigation/stack' import { featureCheck } from '@utils/helpers/featureCheck' +import { checkIsMyAccount } from '@utils/helpers/isMyAccount' import removeHTML from '@utils/helpers/removeHTML' import { TabLocalStackParamList } from '@utils/navigation/navigators' import { usePreferencesQuery } from '@utils/queryHooks/preferences' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' -import { useAccountStorage } from '@utils/storage/actions' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React, { Fragment, useRef, useState } from 'react' @@ -63,10 +63,9 @@ const TimelineDefault: React.FC = ({ const { colors } = useTheme() const navigation = useNavigation>() - const [accountId] = useAccountStorage.string('auth.account.id') const { data: preferences } = usePreferencesQuery() - const ownAccount = status.account?.id === accountId + const isMyAccount = checkIsMyAccount(status.account.id) const [spoilerExpanded, setSpoilerExpanded] = useState( preferences?.['reading:expand:spoilers'] || false ) @@ -136,7 +135,7 @@ const TimelineDefault: React.FC = ({ const mStatus = menuStatus({ status, queryKey }) const mInstance = menuInstance({ status, queryKey }) - if (!ownAccount) { + if (!isMyAccount) { let filterResults: FilteredProps['filterResults'] = [] const [filterRevealed, setFilterRevealed] = useState(false) const hasFilterServerSide = featureCheck('filter_server_side') @@ -166,7 +165,7 @@ const TimelineDefault: React.FC = ({ value={{ queryKey, status, - ownAccount, + isMyAccount, spoilerHidden, rawContent, detectedLanguage, diff --git a/src/components/Timeline/Notifications.tsx b/src/components/Timeline/Notifications.tsx index 8f2ad75f..2c7f4892 100644 --- a/src/components/Timeline/Notifications.tsx +++ b/src/components/Timeline/Notifications.tsx @@ -12,10 +12,10 @@ import TimelinePoll from '@components/Timeline/Shared/Poll' import { useNavigation } from '@react-navigation/native' import { StackNavigationProp } from '@react-navigation/stack' import { featureCheck } from '@utils/helpers/featureCheck' +import { checkIsMyAccount } from '@utils/helpers/isMyAccount' import { TabLocalStackParamList } from '@utils/navigation/navigators' import { usePreferencesQuery } from '@utils/queryHooks/preferences' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' -import { useAccountStorage } from '@utils/storage/actions' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React, { Fragment, useState } from 'react' @@ -32,7 +32,6 @@ export interface Props { } const TimelineNotifications: React.FC = ({ notification, queryKey }) => { - const [accountId] = useAccountStorage.string('auth.account.id') const { data: preferences } = usePreferencesQuery() const status = notification.status?.reblog ? notification.status.reblog : notification.status @@ -42,7 +41,7 @@ const TimelineNotifications: React.FC = ({ notification, queryKey }) => { : notification.status ? notification.status.account : notification.account - const ownAccount = notification.account?.id === accountId + const isMyAccount = checkIsMyAccount(notification.account?.id) const [spoilerExpanded, setSpoilerExpanded] = useState( preferences?.['reading:expand:spoilers'] || false ) @@ -109,7 +108,7 @@ const TimelineNotifications: React.FC = ({ notification, queryKey }) => { const mStatus = menuStatus({ status: notification.status, queryKey }) const mInstance = menuInstance({ status: notification.status, queryKey }) - if (!ownAccount) { + if (!isMyAccount) { let filterResults: FilteredProps['filterResults'] = [] const [filterRevealed, setFilterRevealed] = useState(false) const hasFilterServerSide = featureCheck('filter_server_side') @@ -140,7 +139,7 @@ const TimelineNotifications: React.FC = ({ notification, queryKey }) => { value={{ queryKey, status, - ownAccount, + isMyAccount, spoilerHidden }} > diff --git a/src/components/Timeline/Shared/Actions.tsx b/src/components/Timeline/Shared/Actions.tsx index ec3dfdb7..3178e62a 100644 --- a/src/components/Timeline/Shared/Actions.tsx +++ b/src/components/Timeline/Shared/Actions.tsx @@ -22,7 +22,7 @@ import { Pressable, StyleSheet, View } from 'react-native' import StatusContext from './Context' const TimelineActions: React.FC = () => { - const { queryKey, status, ownAccount, highlighted, disableDetails } = useContext(StatusContext) + const { queryKey, status, isMyAccount, highlighted, disableDetails } = useContext(StatusContext) if (!queryKey || !status || disableDetails) return null const navigationState = useNavState() @@ -182,7 +182,7 @@ const TimelineActions: React.FC = () => { const childrenReblog = () => { const color = (state: boolean) => (state ? colors.green : colors.secondary) const disabled = - status.visibility === 'direct' || (status.visibility === 'private' && !ownAccount) + status.visibility === 'direct' || (status.visibility === 'private' && !isMyAccount) return ( <> { fontStyle='S' style={{ color: - status.visibility === 'private' && !ownAccount + status.visibility === 'private' && !isMyAccount ? colors.disabled : color(status.reblogged), marginLeft: StyleConstants.Spacing.XS @@ -258,7 +258,7 @@ const TimelineActions: React.FC = () => { onPress={onPressReblog} children={childrenReblog()} disabled={ - status.visibility === 'direct' || (status.visibility === 'private' && !ownAccount) + status.visibility === 'direct' || (status.visibility === 'private' && !isMyAccount) } /> diff --git a/src/components/Timeline/Shared/Context.tsx b/src/components/Timeline/Shared/Context.tsx index 2dbea5ae..4ab73b10 100644 --- a/src/components/Timeline/Shared/Context.tsx +++ b/src/components/Timeline/Shared/Context.tsx @@ -8,7 +8,7 @@ type StatusContextType = { status?: Mastodon.Status - ownAccount?: boolean + isMyAccount?: boolean spoilerHidden?: boolean rawContent?: React.MutableRefObject // When highlighted, for translate, edit history detectedLanguage?: React.MutableRefObject diff --git a/src/components/Timeline/Shared/Poll.tsx b/src/components/Timeline/Shared/Poll.tsx index 2229e505..4e8bf87b 100644 --- a/src/components/Timeline/Shared/Poll.tsx +++ b/src/components/Timeline/Shared/Poll.tsx @@ -21,7 +21,7 @@ import { Pressable, View } from 'react-native' import StatusContext from './Context' const TimelinePoll: React.FC = () => { - const { queryKey, status, ownAccount, spoilerHidden, disableDetails, highlighted } = + const { queryKey, status, isMyAccount, spoilerHidden, disableDetails, highlighted } = useContext(StatusContext) if (!queryKey || !status || !status.poll) return null const poll = status.poll @@ -72,7 +72,7 @@ const TimelinePoll: React.FC = () => { const pollButton = () => { if (!poll.expired) { - if (!ownAccount && !poll.voted) { + if (!isMyAccount && !poll.voted) { return (