Simplify useRichText hook to use synchronous facet detection
Replace async detectFacets with synchronous detectFacetsWithoutResolution, eliminating the need for loading states, agent dependency, and complex effect-based resolution. This simplifies the hook from ~30 lines to ~6 lines and removes the duplicate local implementation from Profile.tsx.
This commit is contained in:
@@ -430,7 +430,7 @@ function Inner({
|
|||||||
() => moderateProfile(profile, moderationOpts),
|
() => moderateProfile(profile, moderationOpts),
|
||||||
[profile, moderationOpts],
|
[profile, moderationOpts],
|
||||||
)
|
)
|
||||||
const [descriptionRT] = useRichText(profile.description ?? '')
|
const descriptionRT = useRichText(profile.description ?? '')
|
||||||
const profileShadow = useProfileShadow(profile)
|
const profileShadow = useProfileShadow(profile)
|
||||||
const {follow, unfollow} = useFollowMethods({
|
const {follow, unfollow} = useFollowMethods({
|
||||||
profile: profileShadow,
|
profile: profileShadow,
|
||||||
|
|||||||
@@ -1,34 +1,10 @@
|
|||||||
import React from 'react'
|
import {useMemo} from 'react'
|
||||||
import {RichText as RichTextAPI} from '@atproto/api'
|
import {RichText as RichTextAPI} from '@atproto/api'
|
||||||
|
|
||||||
import {useAgent} from '#/state/session'
|
export function useRichText(text: string): RichTextAPI {
|
||||||
|
return useMemo(() => {
|
||||||
export function useRichText(text: string): [RichTextAPI, boolean] {
|
const rt = new RichTextAPI({text})
|
||||||
const [prevText, setPrevText] = React.useState(text)
|
rt.detectFacetsWithoutResolution()
|
||||||
const [rawRT, setRawRT] = React.useState(() => new RichTextAPI({text}))
|
return rt
|
||||||
const [resolvedRT, setResolvedRT] = React.useState<RichTextAPI | null>(null)
|
}, [text])
|
||||||
const agent = useAgent()
|
|
||||||
if (text !== prevText) {
|
|
||||||
setPrevText(text)
|
|
||||||
setRawRT(new RichTextAPI({text}))
|
|
||||||
setResolvedRT(null)
|
|
||||||
// This will queue an immediate re-render
|
|
||||||
}
|
|
||||||
React.useEffect(() => {
|
|
||||||
let ignore = false
|
|
||||||
async function resolveRTFacets() {
|
|
||||||
// new each time
|
|
||||||
const resolvedRT = new RichTextAPI({text})
|
|
||||||
await resolvedRT.detectFacets(agent)
|
|
||||||
if (!ignore) {
|
|
||||||
setResolvedRT(resolvedRT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolveRTFacets()
|
|
||||||
return () => {
|
|
||||||
ignore = true
|
|
||||||
}
|
|
||||||
}, [text, agent])
|
|
||||||
const isResolving = resolvedRT === null
|
|
||||||
return [resolvedRT ?? rawRT, isResolving]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -394,7 +394,7 @@ function DialogInner({
|
|||||||
const playHaptic = useHaptics()
|
const playHaptic = useHaptics()
|
||||||
const control = Dialog.useDialogContext()
|
const control = Dialog.useDialogContext()
|
||||||
const reportDialogControl = useReportDialogControl()
|
const reportDialogControl = useReportDialogControl()
|
||||||
const [rt] = useRichText(info.description.text)
|
const rt = useRichText(info.description.text)
|
||||||
const {mutateAsync: likeFeed, isPending: isLikePending} = useLikeMutation()
|
const {mutateAsync: likeFeed, isPending: isLikePending} = useLikeMutation()
|
||||||
const {mutateAsync: unlikeFeed, isPending: isUnlikePending} =
|
const {mutateAsync: unlikeFeed, isPending: isUnlikePending} =
|
||||||
useUnlikeMutation()
|
useUnlikeMutation()
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ function LandingScreenLoaded({
|
|||||||
const setActiveStarterPack = useSetActiveStarterPack()
|
const setActiveStarterPack = useSetActiveStarterPack()
|
||||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||||
const androidDialogControl = useDialogControl()
|
const androidDialogControl = useDialogControl()
|
||||||
const [descriptionRt] = useRichText(record.description || '')
|
const descriptionRt = useRichText(record.description || '')
|
||||||
|
|
||||||
const [appClipOverlayVisible, setAppClipOverlayVisible] =
|
const [appClipOverlayVisible, setAppClipOverlayVisible] =
|
||||||
React.useState(false)
|
React.useState(false)
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
type AppBskyActorDefs,
|
type AppBskyActorDefs,
|
||||||
moderateProfile,
|
moderateProfile,
|
||||||
type ModerationOpts,
|
type ModerationOpts,
|
||||||
RichText as RichTextAPI,
|
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
@@ -32,7 +31,7 @@ import {useLabelerInfoQuery} from '#/state/queries/labeler'
|
|||||||
import {resetProfilePostsQueries} from '#/state/queries/post-feed'
|
import {resetProfilePostsQueries} from '#/state/queries/post-feed'
|
||||||
import {useProfileQuery} from '#/state/queries/profile'
|
import {useProfileQuery} from '#/state/queries/profile'
|
||||||
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
||||||
import {useAgent, useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {useSetMinimalShellMode} from '#/state/shell'
|
import {useSetMinimalShellMode} from '#/state/shell'
|
||||||
import {ProfileFeedgens} from '#/view/com/feeds/ProfileFeedgens'
|
import {ProfileFeedgens} from '#/view/com/feeds/ProfileFeedgens'
|
||||||
import {ProfileLists} from '#/view/com/lists/ProfileLists'
|
import {ProfileLists} from '#/view/com/lists/ProfileLists'
|
||||||
@@ -44,6 +43,7 @@ import {ProfileHeader, ProfileHeaderLoading} from '#/screens/Profile/Header'
|
|||||||
import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed'
|
import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed'
|
||||||
import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels'
|
import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels'
|
||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
|
import {useRichText} from '#/components/hooks/useRichText'
|
||||||
import {Circle_And_Square_Stroke1_Corner0_Rounded_Filled as CircleAndSquareIcon} from '#/components/icons/CircleAndSquare'
|
import {Circle_And_Square_Stroke1_Corner0_Rounded_Filled as CircleAndSquareIcon} from '#/components/icons/CircleAndSquare'
|
||||||
import {Heart2_Stroke1_Corner0_Rounded as HeartIcon} from '#/components/icons/Heart2'
|
import {Heart2_Stroke1_Corner0_Rounded as HeartIcon} from '#/components/icons/Heart2'
|
||||||
import {Image_Stroke1_Corner0_Rounded as ImageIcon} from '#/components/icons/Image'
|
import {Image_Stroke1_Corner0_Rounded as ImageIcon} from '#/components/icons/Image'
|
||||||
@@ -205,8 +205,8 @@ function ProfileScreenLoaded({
|
|||||||
|
|
||||||
const description = profile.description ?? ''
|
const description = profile.description ?? ''
|
||||||
const hasDescription = description !== ''
|
const hasDescription = description !== ''
|
||||||
const [descriptionRT, isResolvingDescriptionRT] = useRichText(description)
|
const descriptionRT = useRichText(description)
|
||||||
const showPlaceholder = isPlaceholderProfile || isResolvingDescriptionRT
|
const showPlaceholder = isPlaceholderProfile
|
||||||
const moderation = useMemo(
|
const moderation = useMemo(
|
||||||
() => moderateProfile(profile, moderationOpts),
|
() => moderateProfile(profile, moderationOpts),
|
||||||
[profile, moderationOpts],
|
[profile, moderationOpts],
|
||||||
@@ -599,36 +599,6 @@ function ProfileScreenLoaded({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function useRichText(text: string): [RichTextAPI, boolean] {
|
|
||||||
const agent = useAgent()
|
|
||||||
const [prevText, setPrevText] = useState(text)
|
|
||||||
const [rawRT, setRawRT] = useState(() => new RichTextAPI({text}))
|
|
||||||
const [resolvedRT, setResolvedRT] = useState<RichTextAPI | null>(null)
|
|
||||||
if (text !== prevText) {
|
|
||||||
setPrevText(text)
|
|
||||||
setRawRT(new RichTextAPI({text}))
|
|
||||||
setResolvedRT(null)
|
|
||||||
// This will queue an immediate re-render
|
|
||||||
}
|
|
||||||
useEffect(() => {
|
|
||||||
let ignore = false
|
|
||||||
async function resolveRTFacets() {
|
|
||||||
// new each time
|
|
||||||
const resolvedRT = new RichTextAPI({text})
|
|
||||||
await resolvedRT.detectFacets(agent)
|
|
||||||
if (!ignore) {
|
|
||||||
setResolvedRT(resolvedRT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void resolveRTFacets()
|
|
||||||
return () => {
|
|
||||||
ignore = true
|
|
||||||
}
|
|
||||||
}, [text, agent])
|
|
||||||
const isResolving = resolvedRT === null
|
|
||||||
return [resolvedRT ?? rawRT, isResolving]
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
|||||||
Reference in New Issue
Block a user