Bluesky app fork with some witchin' additions 💫

Performance improvements: structural sharing & moderation opts context (#3785)

* Fix: correctly apply structural sharing to preferences object

* Move moderation opts into a context

* Fix import

* Remove log

* Pass userdid directly

* Pass moderationPrefs directly

authored by

Paul Frazee and committed by
GitHub
31cb3e54 39807a86

+231 -120
+20 -17
src/App.native.tsx
··· 18 18 import {Provider as StatsigProvider} from '#/lib/statsig/statsig' 19 19 import {init as initPersistedState} from '#/state/persisted' 20 20 import {Provider as LabelDefsProvider} from '#/state/preferences/label-defs' 21 + import {Provider as ModerationOptsProvider} from '#/state/preferences/moderation-opts' 21 22 import {readLastActiveAccount} from '#/state/session/util' 22 23 import {useIntentHandler} from 'lib/hooks/useIntentHandler' 23 24 import {useNotificationsListener} from 'lib/notifications/notifications' ··· 79 80 <QueryProvider currentDid={currentAccount?.did}> 80 81 <PushNotificationsListener> 81 82 <StatsigProvider> 82 - <LabelDefsProvider> 83 - <LoggedOutViewProvider> 84 - <SelectedFeedProvider> 85 - <UnreadNotifsProvider> 86 - <ThemeProvider theme={theme}> 87 - {/* All components should be within this provider */} 88 - <RootSiblingParent> 89 - <GestureHandlerRootView style={s.h100pct}> 90 - <TestCtrls /> 91 - <Shell /> 92 - </GestureHandlerRootView> 93 - </RootSiblingParent> 94 - </ThemeProvider> 95 - </UnreadNotifsProvider> 96 - </SelectedFeedProvider> 97 - </LoggedOutViewProvider> 98 - </LabelDefsProvider> 83 + <ModerationOptsProvider> 84 + <LabelDefsProvider> 85 + <LoggedOutViewProvider> 86 + <SelectedFeedProvider> 87 + <UnreadNotifsProvider> 88 + <ThemeProvider theme={theme}> 89 + {/* All components should be within this provider */} 90 + <RootSiblingParent> 91 + <GestureHandlerRootView style={s.h100pct}> 92 + <TestCtrls /> 93 + <Shell /> 94 + </GestureHandlerRootView> 95 + </RootSiblingParent> 96 + </ThemeProvider> 97 + </UnreadNotifsProvider> 98 + </SelectedFeedProvider> 99 + </LoggedOutViewProvider> 100 + </LabelDefsProvider> 101 + </ModerationOptsProvider> 99 102 </StatsigProvider> 100 103 </PushNotificationsListener> 101 104 </QueryProvider>
+20 -17
src/App.web.tsx
··· 8 8 import {Provider as StatsigProvider} from '#/lib/statsig/statsig' 9 9 import {init as initPersistedState} from '#/state/persisted' 10 10 import {Provider as LabelDefsProvider} from '#/state/preferences/label-defs' 11 + import {Provider as ModerationOptsProvider} from '#/state/preferences/moderation-opts' 11 12 import {readLastActiveAccount} from '#/state/session/util' 12 13 import {useIntentHandler} from 'lib/hooks/useIntentHandler' 13 14 import {QueryProvider} from 'lib/react-query' ··· 56 57 key={currentAccount?.did}> 57 58 <QueryProvider currentDid={currentAccount?.did}> 58 59 <StatsigProvider> 59 - <LabelDefsProvider> 60 - <LoggedOutViewProvider> 61 - <SelectedFeedProvider> 62 - <UnreadNotifsProvider> 63 - <ThemeProvider theme={theme}> 64 - {/* All components should be within this provider */} 65 - <RootSiblingParent> 66 - <SafeAreaProvider> 67 - <Shell /> 68 - </SafeAreaProvider> 69 - </RootSiblingParent> 70 - <ToastContainer /> 71 - </ThemeProvider> 72 - </UnreadNotifsProvider> 73 - </SelectedFeedProvider> 74 - </LoggedOutViewProvider> 75 - </LabelDefsProvider> 60 + <ModerationOptsProvider> 61 + <LabelDefsProvider> 62 + <LoggedOutViewProvider> 63 + <SelectedFeedProvider> 64 + <UnreadNotifsProvider> 65 + <ThemeProvider theme={theme}> 66 + {/* All components should be within this provider */} 67 + <RootSiblingParent> 68 + <SafeAreaProvider> 69 + <Shell /> 70 + </SafeAreaProvider> 71 + </RootSiblingParent> 72 + <ToastContainer /> 73 + </ThemeProvider> 74 + </UnreadNotifsProvider> 75 + </SelectedFeedProvider> 76 + </LoggedOutViewProvider> 77 + </LabelDefsProvider> 78 + </ModerationOptsProvider> 76 79 </StatsigProvider> 77 80 </QueryProvider> 78 81 </React.Fragment>
+1 -1
src/components/ProfileHoverCard/index.web.tsx
··· 9 9 import {sanitizeDisplayName} from '#/lib/strings/display-names' 10 10 import {sanitizeHandle} from '#/lib/strings/handles' 11 11 import {pluralize} from '#/lib/strings/helpers' 12 - import {useModerationOpts} from '#/state/queries/preferences' 12 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 13 13 import {usePrefetchProfileQuery, useProfileQuery} from '#/state/queries/profile' 14 14 import {useSession} from '#/state/session' 15 15 import {useProfileShadow} from 'state/cache/profile-shadow'
+1 -1
src/components/dms/NewChat.tsx
··· 8 8 import {sanitizeDisplayName} from '#/lib/strings/display-names' 9 9 import {sanitizeHandle} from '#/lib/strings/handles' 10 10 import {isWeb} from '#/platform/detection' 11 - import {useModerationOpts} from '#/state/queries/preferences' 11 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 12 12 import {useActorAutocompleteQuery} from 'state/queries/actor-autocomplete' 13 13 import {FAB} from '#/view/com/util/fab/FAB' 14 14 import * as Toast from '#/view/com/util/Toast'
+87
src/lib/functions.ts
··· 9 9 const s = new Set(arr) 10 10 return [...s] 11 11 } 12 + 13 + /** 14 + * Taken from @tanstack/query-core utils.ts 15 + * Modified to support Date object comparisons 16 + * 17 + * This function returns `a` if `b` is deeply equal. 18 + * If not, it will replace any deeply equal children of `b` with those of `a`. 19 + * This can be used for structural sharing between JSON values for example. 20 + */ 21 + export function replaceEqualDeep(a: any, b: any): any { 22 + if (a === b) { 23 + return a 24 + } 25 + 26 + if (a instanceof Date && b instanceof Date) { 27 + return a.getTime() === b.getTime() ? a : b 28 + } 29 + 30 + const array = isPlainArray(a) && isPlainArray(b) 31 + 32 + if (array || (isPlainObject(a) && isPlainObject(b))) { 33 + const aItems = array ? a : Object.keys(a) 34 + const aSize = aItems.length 35 + const bItems = array ? b : Object.keys(b) 36 + const bSize = bItems.length 37 + const copy: any = array ? [] : {} 38 + 39 + let equalItems = 0 40 + 41 + for (let i = 0; i < bSize; i++) { 42 + const key = array ? i : bItems[i] 43 + if ( 44 + !array && 45 + a[key] === undefined && 46 + b[key] === undefined && 47 + aItems.includes(key) 48 + ) { 49 + copy[key] = undefined 50 + equalItems++ 51 + } else { 52 + copy[key] = replaceEqualDeep(a[key], b[key]) 53 + if (copy[key] === a[key] && a[key] !== undefined) { 54 + equalItems++ 55 + } 56 + } 57 + } 58 + 59 + return aSize === bSize && equalItems === aSize ? a : copy 60 + } 61 + 62 + return b 63 + } 64 + 65 + export function isPlainArray(value: unknown) { 66 + return Array.isArray(value) && value.length === Object.keys(value).length 67 + } 68 + 69 + // Copied from: https://github.com/jonschlinkert/is-plain-object 70 + export function isPlainObject(o: any): o is Object { 71 + if (!hasObjectPrototype(o)) { 72 + return false 73 + } 74 + 75 + // If has no constructor 76 + const ctor = o.constructor 77 + if (ctor === undefined) { 78 + return true 79 + } 80 + 81 + // If has modified prototype 82 + const prot = ctor.prototype 83 + if (!hasObjectPrototype(prot)) { 84 + return false 85 + } 86 + 87 + // If constructor does not have an Object-specific method 88 + if (!prot.hasOwnProperty('isPrototypeOf')) { 89 + return false 90 + } 91 + 92 + // Most likely a plain Object 93 + return true 94 + } 95 + 96 + function hasObjectPrototype(o: any): boolean { 97 + return Object.prototype.toString.call(o) === '[object Object]' 98 + }
+4 -4
src/screens/Onboarding/StepSuggestedAccounts/SuggestedAccountCard.tsx
··· 2 2 import {View, ViewStyle} from 'react-native' 3 3 import {AppBskyActorDefs, moderateProfile} from '@atproto/api' 4 4 5 - import {useTheme, atoms as a, flatten} from '#/alf' 6 - import {Text} from '#/components/Typography' 5 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 6 + import {UserAvatar} from '#/view/com/util/UserAvatar' 7 + import {atoms as a, flatten, useTheme} from '#/alf' 7 8 import {useItemContext} from '#/components/forms/Toggle' 8 9 import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' 9 - import {UserAvatar} from '#/view/com/util/UserAvatar' 10 - import {useModerationOpts} from '#/state/queries/preferences' 11 10 import {RichText} from '#/components/RichText' 11 + import {Text} from '#/components/Typography' 12 12 13 13 export function SuggestedAccountCard({ 14 14 profile,
+1 -1
src/screens/Onboarding/StepSuggestedAccounts/index.tsx
··· 7 7 import {useAnalytics} from '#/lib/analytics/analytics' 8 8 import {logEvent} from '#/lib/statsig/statsig' 9 9 import {capitalize} from '#/lib/strings/capitalize' 10 - import {useModerationOpts} from '#/state/queries/preferences' 10 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 11 11 import {useProfilesQuery} from '#/state/queries/profile' 12 12 import { 13 13 DescriptionText,
+61
src/state/preferences/moderation-opts.tsx
··· 1 + import React, {createContext, useContext, useMemo} from 'react' 2 + import {BSKY_LABELER_DID, ModerationOpts} from '@atproto/api' 3 + 4 + import {useHiddenPosts, useLabelDefinitions} from '#/state/preferences' 5 + import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/moderation' 6 + import {useSession} from '#/state/session' 7 + import {usePreferencesQuery} from '../queries/preferences' 8 + 9 + export const moderationOptsContext = createContext<ModerationOpts | undefined>( 10 + undefined, 11 + ) 12 + 13 + // used in the moderation state devtool 14 + export const moderationOptsOverrideContext = createContext< 15 + ModerationOpts | undefined 16 + >(undefined) 17 + 18 + export function useModerationOpts() { 19 + return useContext(moderationOptsContext) 20 + } 21 + 22 + export function Provider({children}: React.PropsWithChildren<{}>) { 23 + const override = useContext(moderationOptsOverrideContext) 24 + const {currentAccount} = useSession() 25 + const prefs = usePreferencesQuery() 26 + const {labelDefs} = useLabelDefinitions() 27 + const hiddenPosts = useHiddenPosts() // TODO move this into pds-stored prefs 28 + 29 + const userDid = currentAccount?.did 30 + const moderationPrefs = prefs.data?.moderationPrefs 31 + const value = useMemo<ModerationOpts | undefined>(() => { 32 + if (override) { 33 + return override 34 + } 35 + if (!moderationPrefs) { 36 + return undefined 37 + } 38 + return { 39 + userDid, 40 + prefs: { 41 + ...moderationPrefs, 42 + labelers: moderationPrefs.labelers.length 43 + ? moderationPrefs.labelers 44 + : [ 45 + { 46 + did: BSKY_LABELER_DID, 47 + labels: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES, 48 + }, 49 + ], 50 + hiddenPosts: hiddenPosts || [], 51 + }, 52 + labelDefs, 53 + } 54 + }, [override, userDid, labelDefs, moderationPrefs, hiddenPosts]) 55 + 56 + return ( 57 + <moderationOptsContext.Provider value={value}> 58 + {children} 59 + </moderationOptsContext.Provider> 60 + ) 61 + }
+2 -1
src/state/queries/actor-autocomplete.ts
··· 6 6 import {logger} from '#/logger' 7 7 import {STALE} from '#/state/queries' 8 8 import {useAgent} from '#/state/session' 9 - import {DEFAULT_LOGGED_OUT_PREFERENCES, useModerationOpts} from './preferences' 9 + import {useModerationOpts} from '../preferences/moderation-opts' 10 + import {DEFAULT_LOGGED_OUT_PREFERENCES} from './preferences' 10 11 11 12 const DEFAULT_MOD_OPTS = { 12 13 userDid: undefined,
+1 -1
src/state/queries/notifications/feed.ts
··· 28 28 29 29 import {useMutedThreads} from '#/state/muted-threads' 30 30 import {useAgent} from '#/state/session' 31 + import {useModerationOpts} from '../../preferences/moderation-opts' 31 32 import {STALE} from '..' 32 - import {useModerationOpts} from '../preferences' 33 33 import {embedViewRecordToPostView, getEmbeddedPost} from '../util' 34 34 import {FeedPage} from './types' 35 35 import {useUnreadNotificationsApi} from './unread'
+1 -1
src/state/queries/notifications/unread.tsx
··· 13 13 import {isNative} from '#/platform/detection' 14 14 import {useMutedThreads} from '#/state/muted-threads' 15 15 import {useAgent, useSession} from '#/state/session' 16 - import {useModerationOpts} from '../preferences' 16 + import {useModerationOpts} from '../../preferences/moderation-opts' 17 17 import {truncateAndInvalidate} from '../util' 18 18 import {RQKEY as RQKEY_NOTIFS} from './feed' 19 19 import {CachedFeedPage, FeedPage} from './types'
+2 -1
src/state/queries/post-feed.ts
··· 32 32 import {BSKY_FEED_OWNER_DIDS} from 'lib/constants' 33 33 import {KnownError} from '#/view/com/posts/FeedErrorMessage' 34 34 import {useFeedTuners} from '../preferences/feed-tuners' 35 - import {useModerationOpts, usePreferencesQuery} from './preferences' 35 + import {useModerationOpts} from '../preferences/moderation-opts' 36 + import {usePreferencesQuery} from './preferences' 36 37 import {embedViewRecordToPostView, getEmbeddedPost} from './util' 37 38 38 39 type ActorDid = string
+3 -45
src/state/queries/preferences/index.ts
··· 1 - import {createContext, useContext, useMemo} from 'react' 2 1 import { 3 2 AppBskyActorDefs, 4 - BSKY_LABELER_DID, 5 3 BskyFeedViewPreference, 6 4 LabelPreference, 7 - ModerationOpts, 8 5 } from '@atproto/api' 9 6 import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' 10 7 11 8 import {track} from '#/lib/analytics/analytics' 9 + import {replaceEqualDeep} from '#/lib/functions' 12 10 import {getAge} from '#/lib/strings/time' 13 - import {useHiddenPosts, useLabelDefinitions} from '#/state/preferences' 14 11 import {STALE} from '#/state/queries' 15 12 import { 16 13 DEFAULT_HOME_FEED_PREFS, 17 14 DEFAULT_LOGGED_OUT_PREFERENCES, 18 15 DEFAULT_THREAD_VIEW_PREFS, 19 16 } from '#/state/queries/preferences/const' 20 - import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/moderation' 21 17 import { 22 18 ThreadViewPreferences, 23 19 UsePreferencesQueryResponse, 24 20 } from '#/state/queries/preferences/types' 25 - import {useAgent, useSession} from '#/state/session' 21 + import {useAgent} from '#/state/session' 26 22 import {saveLabelers} from '#/state/session/agent-config' 27 23 28 24 export * from '#/state/queries/preferences/const' ··· 36 32 const {getAgent} = useAgent() 37 33 return useQuery({ 38 34 staleTime: STALE.SECONDS.FIFTEEN, 39 - structuralSharing: true, 35 + structuralSharing: replaceEqualDeep, 40 36 refetchOnWindowFocus: true, 41 37 queryKey: preferencesQueryKey, 42 38 queryFn: async () => { ··· 77 73 } 78 74 }, 79 75 }) 80 - } 81 - 82 - // used in the moderation state devtool 83 - export const moderationOptsOverrideContext = createContext< 84 - ModerationOpts | undefined 85 - >(undefined) 86 - 87 - export function useModerationOpts() { 88 - const override = useContext(moderationOptsOverrideContext) 89 - const {currentAccount} = useSession() 90 - const prefs = usePreferencesQuery() 91 - const {labelDefs} = useLabelDefinitions() 92 - const hiddenPosts = useHiddenPosts() // TODO move this into pds-stored prefs 93 - const opts = useMemo<ModerationOpts | undefined>(() => { 94 - if (override) { 95 - return override 96 - } 97 - if (!prefs.data) { 98 - return 99 - } 100 - return { 101 - userDid: currentAccount?.did, 102 - prefs: { 103 - ...prefs.data.moderationPrefs, 104 - labelers: prefs.data.moderationPrefs.labelers.length 105 - ? prefs.data.moderationPrefs.labelers 106 - : [ 107 - { 108 - did: BSKY_LABELER_DID, 109 - labels: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES, 110 - }, 111 - ], 112 - hiddenPosts: hiddenPosts || [], 113 - }, 114 - labelDefs, 115 - } 116 - }, [override, currentAccount, labelDefs, prefs.data, hiddenPosts]) 117 - return opts 118 76 } 119 77 120 78 export function useClearPreferencesMutation() {
+2 -4
src/state/queries/suggested-follows.ts
··· 18 18 } from '#/lib/api/feed/utils' 19 19 import {getContentLanguages} from '#/state/preferences/languages' 20 20 import {STALE} from '#/state/queries' 21 - import { 22 - useModerationOpts, 23 - usePreferencesQuery, 24 - } from '#/state/queries/preferences' 21 + import {usePreferencesQuery} from '#/state/queries/preferences' 25 22 import {useAgent, useSession} from '#/state/session' 23 + import {useModerationOpts} from '../preferences/moderation-opts' 26 24 27 25 const suggestedFollowsQueryKeyRoot = 'suggested-follows' 28 26 const suggestedFollowsQueryKey = [suggestedFollowsQueryKeyRoot]
+14 -13
src/view/com/notifications/Feed.tsx
··· 1 1 import React from 'react' 2 - import {CenteredView} from '../util/Views' 3 2 import {ActivityIndicator, StyleSheet, View} from 'react-native' 4 - import {FeedItem} from './FeedItem' 5 - import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder' 6 - import {ErrorMessage} from '../util/error/ErrorMessage' 7 - import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' 8 - import {EmptyState} from '../util/EmptyState' 9 - import {s} from 'lib/styles' 3 + import {msg} from '@lingui/macro' 4 + import {useLingui} from '@lingui/react' 5 + 6 + import {usePalette} from '#/lib/hooks/usePalette' 7 + import {cleanError} from '#/lib/strings/errors' 8 + import {logger} from '#/logger' 9 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 10 10 import {useNotificationFeedQuery} from '#/state/queries/notifications/feed' 11 11 import {useUnreadNotificationsApi} from '#/state/queries/notifications/unread' 12 - import {logger} from '#/logger' 13 - import {cleanError} from '#/lib/strings/errors' 14 - import {useModerationOpts} from '#/state/queries/preferences' 12 + import {s} from 'lib/styles' 13 + import {EmptyState} from '../util/EmptyState' 14 + import {ErrorMessage} from '../util/error/ErrorMessage' 15 15 import {List, ListRef} from '../util/List' 16 - import {useLingui} from '@lingui/react' 17 - import {msg} from '@lingui/macro' 18 - import {usePalette} from '#/lib/hooks/usePalette' 16 + import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder' 17 + import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' 18 + import {CenteredView} from '../util/Views' 19 + import {FeedItem} from './FeedItem' 19 20 20 21 const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} 21 22 const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
+2 -4
src/view/com/post-thread/PostThread.tsx
··· 8 8 import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped' 9 9 import {ScrollProvider} from '#/lib/ScrollContext' 10 10 import {isAndroid, isNative, isWeb} from '#/platform/detection' 11 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 11 12 import { 12 13 sortThread, 13 14 ThreadBlocked, ··· 16 17 ThreadPost, 17 18 usePostThreadQuery, 18 19 } from '#/state/queries/post-thread' 19 - import { 20 - useModerationOpts, 21 - usePreferencesQuery, 22 - } from '#/state/queries/preferences' 20 + import {usePreferencesQuery} from '#/state/queries/preferences' 23 21 import {useSession} from '#/state/session' 24 22 import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' 25 23 import {usePalette} from 'lib/hooks/usePalette'
+1 -1
src/view/com/post-thread/PostThreadItem.tsx
··· 15 15 import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow' 16 16 import {useLanguagePrefs} from '#/state/preferences' 17 17 import {useOpenLink} from '#/state/preferences/in-app-browser' 18 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 18 19 import {ThreadPost} from '#/state/queries/post-thread' 19 - import {useModerationOpts} from '#/state/queries/preferences' 20 20 import {useComposerControls} from '#/state/shell/composer' 21 21 import {MAX_POST_LINES} from 'lib/constants' 22 22 import {usePalette} from 'lib/hooks/usePalette'
+1 -1
src/view/com/post/Post.tsx
··· 14 14 15 15 import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped' 16 16 import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow' 17 - import {useModerationOpts} from '#/state/queries/preferences' 17 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 18 18 import {useComposerControls} from '#/state/shell/composer' 19 19 import {MAX_POST_LINES} from 'lib/constants' 20 20 import {usePalette} from 'lib/hooks/usePalette'
+1 -1
src/view/com/profile/ProfileCard.tsx
··· 12 12 import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription' 13 13 import {useProfileShadow} from '#/state/cache/profile-shadow' 14 14 import {Shadow} from '#/state/cache/types' 15 - import {useModerationOpts} from '#/state/queries/preferences' 15 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 16 16 import {useSession} from '#/state/session' 17 17 import {usePalette} from 'lib/hooks/usePalette' 18 18 import {getModerationCauseKey, isJustAMute} from 'lib/moderation'
+1 -1
src/view/com/profile/ProfileHeaderSuggestedFollows.tsx
··· 9 9 import {useLingui} from '@lingui/react' 10 10 11 11 import {useProfileShadow} from '#/state/cache/profile-shadow' 12 - import {useModerationOpts} from '#/state/queries/preferences' 12 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 13 13 import {useProfileFollowMutationQueue} from '#/state/queries/profile' 14 14 import {useSuggestedFollowsByActorQuery} from '#/state/queries/suggested-follows' 15 15 import {useAnalytics} from 'lib/analytics/analytics'
+1 -1
src/view/com/util/post-embeds/QuoteEmbed.tsx
··· 25 25 26 26 import {HITSLOP_20} from '#/lib/constants' 27 27 import {s} from '#/lib/styles' 28 - import {useModerationOpts} from '#/state/queries/preferences' 28 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 29 29 import {usePalette} from 'lib/hooks/usePalette' 30 30 import {InfoCircleIcon} from 'lib/icons' 31 31 import {makeProfileLink} from 'lib/routes/links'
+1 -1
src/view/screens/DebugMod.tsx
··· 20 20 import {useLingui} from '@lingui/react' 21 21 22 22 import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings' 23 + import {moderationOptsOverrideContext} from '#/state/preferences/moderation-opts' 23 24 import {FeedNotification} from '#/state/queries/notifications/types' 24 25 import { 25 26 groupNotifications, 26 27 shouldFilterNotif, 27 28 } from '#/state/queries/notifications/util' 28 - import {moderationOptsOverrideContext} from '#/state/queries/preferences' 29 29 import {useSession} from '#/state/session' 30 30 import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' 31 31 import {CenteredView, ScrollView} from '#/view/com/util/Views'
+1 -1
src/view/screens/Profile.tsx
··· 13 13 14 14 import {cleanError} from '#/lib/strings/errors' 15 15 import {useProfileShadow} from '#/state/cache/profile-shadow' 16 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 16 17 import {useLabelerInfoQuery} from '#/state/queries/labeler' 17 18 import {resetProfilePostsQueries} from '#/state/queries/post-feed' 18 - import {useModerationOpts} from '#/state/queries/preferences' 19 19 import {useProfileQuery} from '#/state/queries/profile' 20 20 import {useResolveDidQuery} from '#/state/queries/resolve-uri' 21 21 import {useAgent, useSession} from '#/state/session'
+1 -1
src/view/screens/Search/Search.tsx
··· 27 27 import {logger} from '#/logger' 28 28 import {isIOS, isNative, isWeb} from '#/platform/detection' 29 29 import {listenSoftReset} from '#/state/events' 30 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 30 31 import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete' 31 32 import {useActorSearch} from '#/state/queries/actor-search' 32 - import {useModerationOpts} from '#/state/queries/preferences' 33 33 import {useSearchPostsQuery} from '#/state/queries/search-posts' 34 34 import {useSuggestedFollowsQuery} from '#/state/queries/suggested-follows' 35 35 import {useSession} from '#/state/session'
+1 -1
src/view/shell/desktop/Search.tsx
··· 21 21 import {sanitizeDisplayName} from '#/lib/strings/display-names' 22 22 import {sanitizeHandle} from '#/lib/strings/handles' 23 23 import {s} from '#/lib/styles' 24 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 24 25 import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete' 25 - import {useModerationOpts} from '#/state/queries/preferences' 26 26 import {usePalette} from 'lib/hooks/usePalette' 27 27 import {MagnifyingGlassIcon2} from 'lib/icons' 28 28 import {NavigationProp} from 'lib/routes/types'