Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

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

at c540dae4e7db67031ee5f67feb076927999e364d 152 lines 3.9 kB view raw
1import {useCallback, useMemo, useRef, useState} from 'react' 2import {type AppBskyUnspeccedGetPostThreadV2} from '@atproto/api' 3import debounce from 'lodash.debounce' 4 5import {OnceKey, useCallOnce} from '#/lib/hooks/useCallOnce' 6import {logger} from '#/logger' 7import { 8 usePreferencesQuery, 9 useSetThreadViewPreferencesMutation, 10} from '#/state/queries/preferences' 11import {type ThreadViewPreferences} from '#/state/queries/preferences/types' 12import {type Literal} from '#/types/utils' 13 14export type ThreadSortOption = Literal< 15 AppBskyUnspeccedGetPostThreadV2.QueryParams['sort'], 16 string 17> 18export type ThreadViewOption = 'linear' | 'tree' 19export type ThreadPreferences = { 20 isLoaded: boolean 21 isSaving: boolean 22 sort: ThreadSortOption 23 setSort: (sort: string) => void 24 view: ThreadViewOption 25 setView: (view: ThreadViewOption) => void 26} 27 28export function useThreadPreferences({ 29 save, 30}: {save?: boolean} = {}): ThreadPreferences { 31 const {data: preferences} = usePreferencesQuery() 32 const serverPrefs = preferences?.threadViewPrefs 33 const once = useCallOnce(OnceKey.PreferencesThread) 34 35 /* 36 * Create local state representations of server state 37 */ 38 const [sort, setSort] = useState(normalizeSort(serverPrefs?.sort || 'top')) 39 const [view, setView] = useState( 40 normalizeView({ 41 treeViewEnabled: !!serverPrefs?.lab_treeViewEnabled, 42 }), 43 ) 44 45 /** 46 * If we get a server update, update local state 47 */ 48 const [prevServerPrefs, setPrevServerPrefs] = useState(serverPrefs) 49 const isLoaded = !!prevServerPrefs 50 if (serverPrefs && prevServerPrefs !== serverPrefs) { 51 setPrevServerPrefs(serverPrefs) 52 53 /* 54 * Update 55 */ 56 setSort(normalizeSort(serverPrefs.sort)) 57 setView( 58 normalizeView({ 59 treeViewEnabled: !!serverPrefs.lab_treeViewEnabled, 60 }), 61 ) 62 63 once(() => { 64 logger.metric('thread:preferences:load', { 65 sort: serverPrefs.sort, 66 view: serverPrefs.lab_treeViewEnabled ? 'tree' : 'linear', 67 }) 68 }) 69 } 70 71 const userUpdatedPrefs = useRef(false) 72 const [isSaving, setIsSaving] = useState(false) 73 const {mutateAsync} = useSetThreadViewPreferencesMutation() 74 const savePrefs = useMemo(() => { 75 return debounce(async (prefs: ThreadViewPreferences) => { 76 try { 77 setIsSaving(true) 78 await mutateAsync(prefs) 79 logger.metric('thread:preferences:update', { 80 sort: prefs.sort, 81 view: prefs.lab_treeViewEnabled ? 'tree' : 'linear', 82 }) 83 } catch (e) { 84 logger.error('useThreadPreferences failed to save', { 85 safeMessage: e, 86 }) 87 } finally { 88 setIsSaving(false) 89 } 90 }, 4e3) 91 }, [mutateAsync]) 92 93 if (save && userUpdatedPrefs.current) { 94 savePrefs({ 95 sort, 96 lab_treeViewEnabled: view === 'tree', 97 }) 98 userUpdatedPrefs.current = false 99 } 100 101 const setSortWrapped = useCallback( 102 (next: string) => { 103 userUpdatedPrefs.current = true 104 setSort(normalizeSort(next)) 105 }, 106 [setSort], 107 ) 108 const setViewWrapped = useCallback( 109 (next: ThreadViewOption) => { 110 userUpdatedPrefs.current = true 111 setView(next) 112 }, 113 [setView], 114 ) 115 116 return useMemo( 117 () => ({ 118 isLoaded, 119 isSaving, 120 sort, 121 setSort: setSortWrapped, 122 view, 123 setView: setViewWrapped, 124 }), 125 [isLoaded, isSaving, sort, setSortWrapped, view, setViewWrapped], 126 ) 127} 128 129/** 130 * Migrates user thread preferences from the old sort values to V2 131 */ 132export function normalizeSort(sort: string): ThreadSortOption { 133 switch (sort) { 134 case 'oldest': 135 return 'oldest' 136 case 'newest': 137 return 'newest' 138 default: 139 return 'top' 140 } 141} 142 143/** 144 * Transforms existing treeViewEnabled preference into a ThreadViewOption 145 */ 146export function normalizeView({ 147 treeViewEnabled, 148}: { 149 treeViewEnabled: boolean 150}): ThreadViewOption { 151 return treeViewEnabled ? 'tree' : 'linear' 152}