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