From cc5b87730a26db894d46ce0a3187bed7bd238f0d Mon Sep 17 00:00:00 2001 From: "xan.lol" Date: Sun, 31 May 2026 16:19:37 -0700 Subject: [PATCH] feat/fix: restart required prompt + clear deleted quote posts fixes the 'Use handles in profile links instead of DIDs' toggle causing a crash! just didn't work on native also make it clear when a lost quote wasn't detached, but deleted/unavailable --- src/components/Post/Embed/PostPlaceholder.tsx | 18 ++++++++- .../RunesSettings/InfrastructureSettings.tsx | 26 +++++++++---- .../Settings/RunesSettings/MenusSettings.tsx | 12 +++++- src/state/preferences/custom-appview-did.tsx | 8 ---- src/state/preferences/no-app-labelers.tsx | 8 ---- .../preferences/restart-required-prompt.tsx | 38 +++++++++++++++++++ src/state/preferences/use-handle-in-links.tsx | 8 ---- 7 files changed, 84 insertions(+), 34 deletions(-) create mode 100644 src/state/preferences/restart-required-prompt.tsx diff --git a/src/components/Post/Embed/PostPlaceholder.tsx b/src/components/Post/Embed/PostPlaceholder.tsx index 9d3226644..2c728ea2f 100644 --- a/src/components/Post/Embed/PostPlaceholder.tsx +++ b/src/components/Post/Embed/PostPlaceholder.tsx @@ -1,4 +1,5 @@ import {StyleSheet, View} from 'react-native' +import React from 'react' import {usePalette} from '#/lib/hooks/usePalette' import {InfoCircleIcon} from '#/lib/icons' @@ -6,6 +7,15 @@ import {Text} from '#/view/com/util/text/Text' import {atoms as a, useTheme} from '#/alf' import {Loader} from '#/components/Loader' +function extractTextFromChildren(children: React.ReactNode): string { + if (children == null) return '' + if (typeof children === 'string') return children + if (typeof children === 'number') return String(children) + if (Array.isArray(children)) return children.map(extractTextFromChildren).join(' ') + if (React.isValidElement(children)) return extractTextFromChildren((children as any).props?.children) + return '' +} + export function PostPlaceholder({ children, directFetchEnabled, @@ -15,10 +25,16 @@ export function PostPlaceholder({ }) { const t = useTheme() const pal = usePalette('default') + + const text = extractTextFromChildren(children).trim().toLowerCase() + const isDeleted = /\bdeleted\b/.test(text) + + const showLoader = Boolean(directFetchEnabled) && !isDeleted + return ( - {directFetchEnabled ? ( + {showLoader ? ( ) : ( diff --git a/src/screens/Settings/RunesSettings/InfrastructureSettings.tsx b/src/screens/Settings/RunesSettings/InfrastructureSettings.tsx index 678b05454..209284dbf 100644 --- a/src/screens/Settings/RunesSettings/InfrastructureSettings.tsx +++ b/src/screens/Settings/RunesSettings/InfrastructureSettings.tsx @@ -26,6 +26,7 @@ import { usePlcDirectory, useSetPlcDirectory, } from '#/state/preferences/plc-directory' +import {RestartRequiredPrompt} from '#/state/preferences/restart-required-prompt' import { useLibreTranslateInstance, useSetLibreTranslateInstance, @@ -69,6 +70,7 @@ export function RunesInfrastructureSettingsScreen() { const noAppLabelers = useNoAppLabelers() const setNoAppLabelers = useSetNoAppLabelers() + const restartPromptControl = Dialog.useDialogControl() return ( @@ -224,7 +226,10 @@ export function RunesInfrastructureSettingsScreen() { name="no_app_labelers" label={l`Do not declare any app labelers`} value={noAppLabelers} - onChange={value => setNoAppLabelers(value)}> + onChange={value => { + setNoAppLabelers(value) + restartPromptControl.open() + }}> @@ -234,15 +239,10 @@ export function RunesInfrastructureSettingsScreen() { - - - Toggling this will reload the app. - - - Some App Views will default to using an app labeler if you have no + Some AppViews will default to using an app labeler if you have no labelers, so consider subscribing to at least one labeler if you have issues. @@ -259,12 +259,18 @@ export function RunesInfrastructureSettingsScreen() { - + { + restartPromptControl.open() + }} + /> + ) } @@ -335,8 +341,10 @@ function ConstellationInstanceDialog({ function CustomAppViewDidDialog({ control, + onRestartRequired, }: { control: Dialog.DialogControlProps + onRestartRequired: () => void }) { const pal = usePalette('default') const {t: l} = useLingui() @@ -353,12 +361,14 @@ function CustomAppViewDidDialog({ if (did.length === 0) { control.close(() => { setCustomAppViewDid(undefined) + onRestartRequired() }) return } if (!bskyAppViewService?.serviceEndpoint) return control.close(() => { setCustomAppViewDid(did) + onRestartRequired() }) } diff --git a/src/screens/Settings/RunesSettings/MenusSettings.tsx b/src/screens/Settings/RunesSettings/MenusSettings.tsx index ed16933c8..bc65a25cf 100644 --- a/src/screens/Settings/RunesSettings/MenusSettings.tsx +++ b/src/screens/Settings/RunesSettings/MenusSettings.tsx @@ -16,6 +16,8 @@ import { useHandleInLinks, useSetHandleInLinks, } from '#/state/preferences/use-handle-in-links' +import {RestartRequiredPrompt} from '#/state/preferences/restart-required-prompt' +import * as Dialog from '#/components/Dialog' import * as SettingsList from '#/screens/Settings/components/SettingsList' import * as Toggle from '#/components/forms/Toggle' import {ArrowShareRight_Stroke2_Corner2_Rounded as ArrowShareRightIcon} from '#/components/icons/ArrowShareRight' @@ -28,6 +30,7 @@ export function RunesMenusSettingsScreen() { const handleInLinks = useHandleInLinks() const setHandleInLinks = useSetHandleInLinks() + const restartPromptControl = Dialog.useDialogControl() const showExternalShareButtons = useShowExternalShareButtons() const setShowExternalShareButtons = useSetShowExternalShareButtons() @@ -45,7 +48,10 @@ export function RunesMenusSettingsScreen() { name="use_handle_in_links" label={l`Use handles in profile links instead of DIDs (requires restart)`} value={handleInLinks ?? false} - onChange={value => setHandleInLinks(value)}> + onChange={value => { + setHandleInLinks(value) + restartPromptControl.open() + }}> @@ -97,6 +103,10 @@ export function RunesMenusSettingsScreen() { )} + + ) } diff --git a/src/state/preferences/custom-appview-did.tsx b/src/state/preferences/custom-appview-did.tsx index 5da1ee85d..c4ee0ba4f 100644 --- a/src/state/preferences/custom-appview-did.tsx +++ b/src/state/preferences/custom-appview-did.tsx @@ -1,8 +1,6 @@ import {useCallback} from 'react' -import {reloadAppAsync} from 'expo' import {isDid} from '@atproto/api' -import {IS_WEB} from '#/env' import {device, useStorage} from '#/storage' export function useCustomAppViewDid() { @@ -20,12 +18,6 @@ export function useSetCustomAppViewDid() { return useCallback( (customAppViewDid: string | undefined) => { setCustomAppViewDid(customAppViewDid) - - if (IS_WEB) { - window.location.reload() - } else { - void reloadAppAsync() - } }, [setCustomAppViewDid], ) diff --git a/src/state/preferences/no-app-labelers.tsx b/src/state/preferences/no-app-labelers.tsx index 94ad4f552..2cddf1955 100644 --- a/src/state/preferences/no-app-labelers.tsx +++ b/src/state/preferences/no-app-labelers.tsx @@ -6,10 +6,8 @@ import { useEffect, useState, } from 'react' -import {reloadAppAsync} from 'expo' import * as persisted from '#/state/persisted' -import {IS_WEB} from '#/env' type StateContext = persisted.Schema['noAppLabelers'] type SetContext = (v: persisted.Schema['noAppLabelers']) => void @@ -28,12 +26,6 @@ export function Provider({children}: PropsWithChildren<{}>) { (noAppLabelers: persisted.Schema['noAppLabelers']) => { setState(noAppLabelers) persisted.write('noAppLabelers', noAppLabelers) - - if (IS_WEB) { - window.location.reload() - } else { - void reloadAppAsync() - } }, [setState], ) diff --git a/src/state/preferences/restart-required-prompt.tsx b/src/state/preferences/restart-required-prompt.tsx new file mode 100644 index 000000000..44212a596 --- /dev/null +++ b/src/state/preferences/restart-required-prompt.tsx @@ -0,0 +1,38 @@ +import {useCallback} from 'react' + +import {useLingui} from '@lingui/react/macro' +import {reloadAppAsync} from 'expo' + +import {IS_WEB} from '#/env' +import * as Prompt from '#/components/Prompt' + +export function RestartRequiredPrompt({ + control, + onConfirm, +}: { + control: Prompt.PromptControlProps + onConfirm?: () => void +}) { + const {t: l} = useLingui() + + const handleConfirm = useCallback(() => { + onConfirm?.() + + if (IS_WEB) { + window.location.reload() + } else { + void reloadAppAsync() + } + }, [onConfirm]) + + return ( + + ) +} \ No newline at end of file diff --git a/src/state/preferences/use-handle-in-links.tsx b/src/state/preferences/use-handle-in-links.tsx index df1d4dd28..42cb8d2cd 100644 --- a/src/state/preferences/use-handle-in-links.tsx +++ b/src/state/preferences/use-handle-in-links.tsx @@ -6,10 +6,8 @@ import { useEffect, useState, } from 'react' -import {reloadAppAsync} from 'expo' import * as persisted from '#/state/persisted' -import {IS_WEB} from '#/env' type StateContext = persisted.Schema['useHandleInLinks'] type SetContext = (v: persisted.Schema['useHandleInLinks']) => void @@ -57,12 +55,6 @@ export function useSetHandleInLinks() { return useCallback( (useHandleInLinks: persisted.Schema['useHandleInLinks']) => { set(useHandleInLinks) - - if (IS_WEB) { - window.location.reload() - } else { - void reloadAppAsync() - } }, [set], ) -- 2.51.2