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

Configure Feed

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

at a876aae44ea07494ebea9727350aa060b81f317b 163 lines 4.8 kB view raw
1import {useEffect} from 'react' 2import {type Agent, AppBskyActorDefs, asPredicate} from '@atproto/api' 3import {useMutation, useQueryClient} from '@tanstack/react-query' 4 5import { 6 preferencesQueryKey, 7 usePreferencesQuery, 8} from '#/state/queries/preferences' 9import {useAgent} from '#/state/session' 10import {pdsAgent} from '#/state/session/agent' 11import {useAnalytics} from '#/analytics' 12import * as env from '#/env' 13import {IS_WEB} from '#/env' 14import { 15 type LiveEventFeed, 16 type LiveEventFeedMetricContext, 17} from '#/features/liveEvents/types' 18 19export type LiveEventPreferencesAction = Parameters< 20 Agent['updateLiveEventPreferences'] 21>[0] & { 22 /** 23 * Flag that is internal to this hook, do not set when updating prefs 24 */ 25 __canUndo?: boolean 26} 27 28export function useLiveEventPreferences() { 29 const query = usePreferencesQuery() 30 useWebOnlyDebugLiveEventPreferences() 31 return { 32 ...query, 33 data: query.data?.liveEventPreferences || { 34 hideAllFeeds: false, 35 hiddenFeedIds: [], 36 }, 37 } 38} 39 40function useWebOnlyDebugLiveEventPreferences() { 41 const queryClient = useQueryClient() 42 const agent = useAgent() 43 44 useEffect(() => { 45 if (env.IS_DEV && IS_WEB && typeof window !== 'undefined') { 46 // @ts-ignore 47 window.__updateLiveEventPreferences = async ( 48 action: LiveEventPreferencesAction, 49 ) => { 50 await pdsAgent(agent).updateLiveEventPreferences(action) 51 // triggers a refetch 52 await queryClient.invalidateQueries({ 53 queryKey: preferencesQueryKey, 54 }) 55 } 56 } 57 }, [agent, queryClient]) 58} 59 60export function useUpdateLiveEventPreferences(props: { 61 feed?: LiveEventFeed 62 metricContext: LiveEventFeedMetricContext 63 onUpdateSuccess?: (props: { 64 undoAction: LiveEventPreferencesAction | null 65 }) => void 66}) { 67 const ax = useAnalytics() 68 const queryClient = useQueryClient() 69 const agent = useAgent() 70 71 return useMutation< 72 AppBskyActorDefs.LiveEventPreferences, 73 Error, 74 LiveEventPreferencesAction, 75 {undoAction: LiveEventPreferencesAction | null} 76 >({ 77 onSettled(data, error, variables) { 78 /* 79 * `onSettled` runs after the mutation completes, success or no. The idea 80 * here is that we want to invert the action that was just passed in, and 81 * provide it as an `undoAction` to the `onUpdateSuccess` callback. 82 * 83 * If the operation was not a success, we don't provide the `undoAction`. 84 * 85 * Upon the first call of the mutation, the `__canUndo` flag is undefined, 86 * so we allow the undo. However, when we create the `undoAction`, we 87 * set its `__canUndo` flag to false, so that if the user were to call 88 * the undo action, we would not provide another undo for that. 89 */ 90 const canUndo = variables.__canUndo === undefined ? true : false 91 let undoAction: LiveEventPreferencesAction | null = null 92 93 switch (variables.type) { 94 case 'hideFeed': 95 undoAction = {type: 'unhideFeed', id: variables.id, __canUndo: false} 96 break 97 case 'unhideFeed': 98 undoAction = {type: 'hideFeed', id: variables.id, __canUndo: false} 99 break 100 case 'toggleHideAllFeeds': 101 undoAction = {type: 'toggleHideAllFeeds', __canUndo: false} 102 break 103 } 104 105 if (data && !error) { 106 props?.onUpdateSuccess?.({ 107 undoAction: canUndo ? undoAction : null, 108 }) 109 } 110 }, 111 mutationFn: async action => { 112 const updated = await pdsAgent(agent).updateLiveEventPreferences(action) 113 const prefs = updated.find(p => 114 asPredicate(AppBskyActorDefs.validateLiveEventPreferences)(p), 115 ) 116 117 switch (action.type) { 118 case 'hideFeed': 119 case 'unhideFeed': { 120 if (!props.feed) { 121 ax.logger.error( 122 `useUpdateLiveEventPreferences: feed is missing, but required for hiding/unhiding`, 123 { 124 action, 125 }, 126 ) 127 break 128 } 129 130 ax.metric( 131 action.type === 'hideFeed' 132 ? 'liveEvents:feedBanner:hide' 133 : 'liveEvents:feedBanner:unhide', 134 { 135 feed: props.feed.url, 136 context: props.metricContext, 137 }, 138 ) 139 break 140 } 141 case 'toggleHideAllFeeds': { 142 if (prefs!.hideAllFeeds) { 143 ax.metric('liveEvents:hideAllFeedBanners', { 144 context: props.metricContext, 145 }) 146 } else { 147 ax.metric('liveEvents:unhideAllFeedBanners', { 148 context: props.metricContext, 149 }) 150 } 151 break 152 } 153 } 154 155 // triggers a refetch 156 queryClient.invalidateQueries({ 157 queryKey: preferencesQueryKey, 158 }) 159 160 return prefs! 161 }, 162 }) 163}