Bluesky app fork with some witchin' additions ๐Ÿ’ซ
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: preferences disappearing ๐Ÿ˜ญ๐Ÿ˜ญ๐Ÿ˜ญ

xan.lol 5d662082 32abb8b7

+91 -45
+91 -45
src/state/queries/preferences/index.ts
··· 1 - import {useCallback} from 'react' 1 + import {useEffect, useMemo} from 'react' 2 2 import { 3 3 type AppBskyActorDefs, 4 4 type BskyFeedViewPreference, 5 + type BskyPreferences, 5 6 type LabelPreference, 6 7 } from '@atproto/api' 7 8 import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' ··· 37 38 {persistedVersion: 1}, 38 39 ) 39 40 41 + /** 42 + * Some screens interpret missing prefs as "use defaults", which causes a 43 + * visible flicker when the preferences query briefly has no data. Retain the 44 + * last successful snapshot per account so those consumers stay stable. 45 + */ 46 + const lastKnownPreferencesByDid = new Map<string, UsePreferencesQueryResponse>() 47 + 48 + function normalizePreferences( 49 + res: BskyPreferences, 50 + ): UsePreferencesQueryResponse { 51 + return { 52 + ...res, 53 + savedFeeds: res.savedFeeds.filter(f => f.type !== 'unknown'), 54 + /** 55 + * Special preference, only used for following feed, previously 56 + * called `home` 57 + */ 58 + feedViewPrefs: { 59 + ...DEFAULT_HOME_FEED_PREFS, 60 + ...(res.feedViewPrefs.home || {}), 61 + }, 62 + threadViewPrefs: { 63 + ...DEFAULT_THREAD_VIEW_PREFS, 64 + ...(res.threadViewPrefs ?? {}), 65 + }, 66 + userAge: res.birthDate ? getAge(res.birthDate) : undefined, 67 + } 68 + } 69 + 70 + function ensureBirthDate( 71 + preferences: UsePreferencesQueryResponse, 72 + ): UsePreferencesQueryResponse { 73 + if (!preferences.birthDate || preferences.birthDate instanceof Date) { 74 + return preferences 75 + } 76 + return { 77 + ...preferences, 78 + birthDate: new Date(preferences.birthDate), 79 + } 80 + } 81 + 82 + function applyAgeAssurancePreferences( 83 + data: UsePreferencesQueryResponse, 84 + aa: ReturnType<typeof useAgeAssurance>, 85 + ) { 86 + /** 87 + * Prefs are all downstream of age assurance now. For logged-out 88 + * users, we override moderation prefs based on AA state. 89 + */ 90 + if (aa.state.access !== aa.Access.Full) { 91 + return { 92 + ...data, 93 + moderationPrefs: makeAgeRestrictedModerationPrefs(data.moderationPrefs), 94 + } 95 + } 96 + return data 97 + } 98 + 40 99 export function usePreferencesQuery() { 41 100 const agent = useAgent() 42 101 const aa = useAgeAssurance() ··· 54 113 const res = await pdsAgent(agent).getPreferences() 55 114 56 115 // save to local storage to ensure there are labels on initial requests 57 - saveLabelers( 116 + void saveLabelers( 58 117 agent.did, 59 - res.moderationPrefs.labelers.map(l => l.did), 118 + res.moderationPrefs.labelers.map((l: {did: string}) => l.did), 60 119 ) 61 120 62 - const preferences: UsePreferencesQueryResponse = { 63 - ...res, 64 - savedFeeds: res.savedFeeds.filter(f => f.type !== 'unknown'), 65 - /** 66 - * Special preference, only used for following feed, previously 67 - * called `home` 68 - */ 69 - feedViewPrefs: { 70 - ...DEFAULT_HOME_FEED_PREFS, 71 - ...(res.feedViewPrefs.home || {}), 72 - }, 73 - threadViewPrefs: { 74 - ...DEFAULT_THREAD_VIEW_PREFS, 75 - ...(res.threadViewPrefs ?? {}), 76 - }, 77 - userAge: res.birthDate ? getAge(res.birthDate) : undefined, 78 - } 79 - return preferences 121 + return normalizePreferences(res) 80 122 } 81 123 }, 82 - select: useCallback( 83 - (data: UsePreferencesQueryResponse) => { 84 - /** 85 - * Prefs are all downstream of age assurance now. For logged-out 86 - * users, we override moderation prefs based on AA state. 87 - */ 88 - if (aa.state.access !== aa.Access.Full) { 89 - data = { 90 - ...data, 91 - moderationPrefs: makeAgeRestrictedModerationPrefs( 92 - data.moderationPrefs, 93 - ), 94 - } 95 - } 96 - return data 97 - }, 98 - [aa], 99 - ), 100 124 }) 101 125 102 - if (query.data?.birthDate) { 103 - /** 104 - * The persisted query cache stores dates as strings, but our code expects a `Date`. 105 - */ 106 - query.data.birthDate = new Date(query.data.birthDate) 126 + useEffect(() => { 127 + if (agent.did && query.data) { 128 + lastKnownPreferencesByDid.set(agent.did, ensureBirthDate(query.data)) 129 + } 130 + }, [agent.did, query.data]) 131 + 132 + const stableData = useMemo(() => { 133 + const data = 134 + query.data ?? 135 + (agent.did ? lastKnownPreferencesByDid.get(agent.did) : undefined) 136 + if (!data) { 137 + return data 138 + } 139 + return applyAgeAssurancePreferences(ensureBirthDate(data), aa) 140 + }, [aa, agent.did, query.data]) 141 + 142 + if (!stableData) { 143 + return query 107 144 } 108 145 109 - return query 146 + return { 147 + ...query, 148 + data: stableData, 149 + error: null, 150 + isError: false, 151 + isLoading: false, 152 + isPending: false, 153 + isSuccess: true, 154 + status: 'success', 155 + } 110 156 } 111 157 112 158 export function useClearPreferencesMutation() {