Bluesky app fork with some witchin' additions 💫

toggle for discover fallback in following

commit 65a731390f5d3ceae900e1b60c296c0f6453079c
Author: Aviva Ruben <aviva@rubenfamily.com>
Date: Mon Apr 14 16:15:59 2025 -0500

allow turning off discover fallback

Aviva Ruben 42672d1e 2123877a

+96 -35
+12 -6
src/screens/Settings/DeerSettings.tsx
··· 27 27 useNoAppLabelers, 28 28 useSetNoAppLabelers, 29 29 } from '#/state/preferences/no-app-labelers' 30 + import { 31 + useNoDiscoverFallback, 32 + useSetNoDiscoverFallback, 33 + } from '#/state/preferences/no-discover-fallback' 30 34 import {TextInput} from '#/view/com/modals/util' 31 35 import * as SettingsList from '#/screens/Settings/components/SettingsList' 32 36 import {atoms as a} from '#/alf' ··· 124 128 125 129 const noAppLabelers = useNoAppLabelers() 126 130 const setNoAppLabelers = useSetNoAppLabelers() 131 + 132 + const noDiscoverFallback = useNoDiscoverFallback() 133 + const setNoDiscoverFallback = useSetNoDiscoverFallback() 127 134 128 135 const location = useGeolocation() 129 136 const setLocationControl = Dialog.useDialogControl() ··· 277 284 <Trans>Tweaks</Trans> 278 285 </SettingsList.ItemText> 279 286 <Toggle.Item 280 - name="under construction" 281 - label={_(msg`🚧 under construction...`)} 282 - value={false} 283 - onChange={() => {}} 284 - disabled={true} 287 + name="no_discover_fallback" 288 + label={_(msg`Do not fall back to discover feed`)} 289 + value={noDiscoverFallback} 290 + onChange={value => setNoDiscoverFallback(value)} 285 291 style={[a.w_full]}> 286 292 <Toggle.LabelText style={[a.flex_1]}> 287 - <Trans>🚧 under construction...</Trans> 293 + <Trans>Do not fall back to discover feed</Trans> 288 294 </Toggle.LabelText> 289 295 <Toggle.Platform /> 290 296 </Toggle.Item>
+2
src/state/persisted/schema.ts
··· 129 129 constellationEnabled: z.boolean().optional(), 130 130 directFetchRecords: z.boolean().optional(), 131 131 noAppLabelers: z.boolean().optional(), 132 + noDiscoverFallback: z.boolean().optional(), 132 133 133 134 /** @deprecated */ 134 135 mutedThreads: z.array(z.string()), ··· 187 188 constellationEnabled: false, 188 189 directFetchRecords: false, 189 190 noAppLabelers: false, 191 + noDiscoverFallback: false, 190 192 } 191 193 192 194 export function tryParse(rawData: string): Schema | undefined {
+22 -19
src/state/preferences/index.tsx
··· 13 13 import {Provider as LanguagesProvider} from './languages' 14 14 import {Provider as LargeAltBadgeProvider} from './large-alt-badge' 15 15 import {Provider as NoAppLabelersProvider} from './no-app-labelers' 16 + import {Provider as NoDiscoverProvider} from './no-discover-fallback' 16 17 import {Provider as SubtitlesProvider} from './subtitles' 17 18 import {Provider as TrendingSettingsProvider} from './trending' 18 19 import {Provider as UsedStarterPacksProvider} from './used-starter-packs' ··· 41 42 <NoAppLabelersProvider> 42 43 <DirectFetchRecordsProvider> 43 44 <ConstellationProvider> 44 - <LargeAltBadgeProvider> 45 - <ExternalEmbedsProvider> 46 - <HiddenPostsProvider> 47 - <InAppBrowserProvider> 48 - <DisableHapticsProvider> 49 - <AutoplayProvider> 50 - <UsedStarterPacksProvider> 51 - <SubtitlesProvider> 52 - <TrendingSettingsProvider> 53 - <KawaiiProvider>{children}</KawaiiProvider> 54 - </TrendingSettingsProvider> 55 - </SubtitlesProvider> 56 - </UsedStarterPacksProvider> 57 - </AutoplayProvider> 58 - </DisableHapticsProvider> 59 - </InAppBrowserProvider> 60 - </HiddenPostsProvider> 61 - </ExternalEmbedsProvider> 62 - </LargeAltBadgeProvider> 45 + <NoDiscoverProvider> 46 + <LargeAltBadgeProvider> 47 + <ExternalEmbedsProvider> 48 + <HiddenPostsProvider> 49 + <InAppBrowserProvider> 50 + <DisableHapticsProvider> 51 + <AutoplayProvider> 52 + <UsedStarterPacksProvider> 53 + <SubtitlesProvider> 54 + <TrendingSettingsProvider> 55 + <KawaiiProvider>{children}</KawaiiProvider> 56 + </TrendingSettingsProvider> 57 + </SubtitlesProvider> 58 + </UsedStarterPacksProvider> 59 + </AutoplayProvider> 60 + </DisableHapticsProvider> 61 + </InAppBrowserProvider> 62 + </HiddenPostsProvider> 63 + </ExternalEmbedsProvider> 64 + </LargeAltBadgeProvider> 65 + </NoDiscoverProvider> 63 66 </ConstellationProvider> 64 67 </DirectFetchRecordsProvider> 65 68 </NoAppLabelersProvider>
+47
src/state/preferences/no-discover-fallback.tsx
··· 1 + import React from 'react' 2 + 3 + import * as persisted from '#/state/persisted' 4 + 5 + type StateContext = persisted.Schema['noDiscoverFallback'] 6 + type SetContext = (v: persisted.Schema['noDiscoverFallback']) => void 7 + 8 + const stateContext = React.createContext<StateContext>( 9 + persisted.defaults.noDiscoverFallback, 10 + ) 11 + const setContext = React.createContext<SetContext>( 12 + (_: persisted.Schema['noDiscoverFallback']) => {}, 13 + ) 14 + 15 + export function Provider({children}: React.PropsWithChildren<{}>) { 16 + const [state, setState] = React.useState(persisted.get('noDiscoverFallback')) 17 + 18 + const setStateWrapped = React.useCallback( 19 + (noDiscoverFallback: persisted.Schema['noDiscoverFallback']) => { 20 + setState(noDiscoverFallback) 21 + persisted.write('noDiscoverFallback', noDiscoverFallback) 22 + }, 23 + [setState], 24 + ) 25 + 26 + React.useEffect(() => { 27 + return persisted.onUpdate('noDiscoverFallback', nextNoDiscoverFallback => { 28 + setState(nextNoDiscoverFallback) 29 + }) 30 + }, [setStateWrapped]) 31 + 32 + return ( 33 + <stateContext.Provider value={state}> 34 + <setContext.Provider value={setStateWrapped}> 35 + {children} 36 + </setContext.Provider> 37 + </stateContext.Provider> 38 + ) 39 + } 40 + 41 + export function useNoDiscoverFallback() { 42 + return React.useContext(stateContext) 43 + } 44 + 45 + export function useSetNoDiscoverFallback() { 46 + return React.useContext(setContext) 47 + }
+13 -10
src/state/queries/post-feed.ts
··· 1 1 import React, {useCallback, useEffect, useRef} from 'react' 2 2 import {AppState} from 'react-native' 3 3 import { 4 - AppBskyActorDefs, 4 + type AppBskyActorDefs, 5 5 AppBskyFeedDefs, 6 - AppBskyFeedPost, 6 + type AppBskyFeedPost, 7 7 AtUri, 8 - BskyAgent, 8 + type BskyAgent, 9 9 moderatePost, 10 - ModerationDecision, 10 + type ModerationDecision, 11 11 } from '@atproto/api' 12 12 import { 13 - InfiniteData, 14 - QueryClient, 15 - QueryKey, 13 + type InfiniteData, 14 + type QueryClient, 15 + type QueryKey, 16 16 useInfiniteQuery, 17 17 } from '@tanstack/react-query' 18 18 ··· 23 23 import {LikesFeedAPI} from '#/lib/api/feed/likes' 24 24 import {ListFeedAPI} from '#/lib/api/feed/list' 25 25 import {MergeFeedAPI} from '#/lib/api/feed/merge' 26 - import {FeedAPI, ReasonFeedSource} from '#/lib/api/feed/types' 26 + import {type FeedAPI, type ReasonFeedSource} from '#/lib/api/feed/types' 27 27 import {aggregateUserInterests} from '#/lib/api/feed/utils' 28 - import {FeedTuner, FeedTunerFn} from '#/lib/api/feed-manip' 28 + import {FeedTuner, type FeedTunerFn} from '#/lib/api/feed-manip' 29 29 import {DISCOVER_FEED_URI} from '#/lib/constants' 30 30 import {BSKY_FEED_OWNER_DIDS} from '#/lib/constants' 31 31 import {logger} from '#/logger' ··· 36 36 import {KnownError} from '#/view/com/posts/PostFeedErrorMessage' 37 37 import {useFeedTuners} from '../preferences/feed-tuners' 38 38 import {useModerationOpts} from '../preferences/moderation-opts' 39 + import {useNoDiscoverFallback} from '../preferences/no-discover-fallback' 39 40 import {usePreferencesQuery} from './preferences' 40 41 import { 41 42 didOrHandleUriMatches, ··· 135 136 preferences?.savedFeeds?.findIndex( 136 137 f => f.pinned && f.value === 'following', 137 138 ) ?? -1 138 - const enableFollowingToDiscoverFallback = followingPinnedIndex === 0 139 + const noDiscoverFallback = useNoDiscoverFallback() 140 + const enableFollowingToDiscoverFallback = 141 + followingPinnedIndex === 0 && !noDiscoverFallback 139 142 const agent = useAgent() 140 143 const lastRun = useRef<{ 141 144 data: InfiniteData<FeedPageUnselected>