mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at tmp-rm-bitdrift 239 lines 7.7 kB view raw
1import React, {useMemo} from 'react' 2import {msg} from '@lingui/macro' 3import {useLingui} from '@lingui/react' 4 5import { 6 ProgressGuideToast, 7 ProgressGuideToastRef, 8} from '#/components/ProgressGuide/Toast' 9import { 10 usePreferencesQuery, 11 useSetActiveProgressGuideMutation, 12} from '../queries/preferences' 13 14export enum ProgressGuideAction { 15 Like = 'like', 16 Follow = 'follow', 17} 18 19type ProgressGuideName = 'like-10-and-follow-7' | 'follow-10' 20 21/** 22 * Progress Guides that extend this interface must specify their name in the `guide` field, so it can be used as a discriminated union 23 */ 24interface BaseProgressGuide { 25 guide: ProgressGuideName 26 isComplete: boolean 27 [key: string]: any 28} 29 30export interface Like10AndFollow7ProgressGuide extends BaseProgressGuide { 31 guide: 'like-10-and-follow-7' 32 numLikes: number 33 numFollows: number 34} 35 36export interface Follow10ProgressGuide extends BaseProgressGuide { 37 guide: 'follow-10' 38 numFollows: number 39} 40 41export type ProgressGuide = 42 | Like10AndFollow7ProgressGuide 43 | Follow10ProgressGuide 44 | undefined 45 46const ProgressGuideContext = React.createContext<ProgressGuide>(undefined) 47 48const ProgressGuideControlContext = React.createContext<{ 49 startProgressGuide(guide: ProgressGuideName): void 50 endProgressGuide(): void 51 captureAction(action: ProgressGuideAction, count?: number): void 52}>({ 53 startProgressGuide: (_guide: ProgressGuideName) => {}, 54 endProgressGuide: () => {}, 55 captureAction: (_action: ProgressGuideAction, _count = 1) => {}, 56}) 57 58export function useProgressGuide(guide: ProgressGuideName) { 59 const ctx = React.useContext(ProgressGuideContext) 60 if (ctx?.guide === guide) { 61 return ctx 62 } 63 return undefined 64} 65 66export function useProgressGuideControls() { 67 return React.useContext(ProgressGuideControlContext) 68} 69 70export function Provider({children}: React.PropsWithChildren<{}>) { 71 const {_} = useLingui() 72 const {data: preferences} = usePreferencesQuery() 73 const {mutateAsync, variables, isPending} = 74 useSetActiveProgressGuideMutation() 75 76 const activeProgressGuide = useMemo(() => { 77 const rawProgressGuide = ( 78 isPending ? variables : preferences?.bskyAppState?.activeProgressGuide 79 ) as ProgressGuide 80 81 if (!rawProgressGuide) return undefined 82 83 // ensure the unspecced attributes have the correct types 84 // clone then mutate 85 const {...maybeWronglyTypedProgressGuide} = rawProgressGuide 86 if (maybeWronglyTypedProgressGuide?.guide === 'like-10-and-follow-7') { 87 maybeWronglyTypedProgressGuide.numLikes = 88 Number(maybeWronglyTypedProgressGuide.numLikes) || 0 89 maybeWronglyTypedProgressGuide.numFollows = 90 Number(maybeWronglyTypedProgressGuide.numFollows) || 0 91 } else if (maybeWronglyTypedProgressGuide?.guide === 'follow-10') { 92 maybeWronglyTypedProgressGuide.numFollows = 93 Number(maybeWronglyTypedProgressGuide.numFollows) || 0 94 } 95 96 return maybeWronglyTypedProgressGuide 97 }, [isPending, variables, preferences]) 98 99 const [localGuideState, setLocalGuideState] = 100 React.useState<ProgressGuide>(undefined) 101 102 if (activeProgressGuide && !localGuideState) { 103 // hydrate from the server if needed 104 setLocalGuideState(activeProgressGuide) 105 } 106 107 const firstLikeToastRef = React.useRef<ProgressGuideToastRef | null>(null) 108 const fifthLikeToastRef = React.useRef<ProgressGuideToastRef | null>(null) 109 const tenthLikeToastRef = React.useRef<ProgressGuideToastRef | null>(null) 110 111 const fifthFollowToastRef = React.useRef<ProgressGuideToastRef | null>(null) 112 const tenthFollowToastRef = React.useRef<ProgressGuideToastRef | null>(null) 113 114 const controls = React.useMemo(() => { 115 return { 116 startProgressGuide(guide: ProgressGuideName) { 117 if (guide === 'like-10-and-follow-7') { 118 const guideObj = { 119 guide: 'like-10-and-follow-7', 120 numLikes: 0, 121 numFollows: 0, 122 isComplete: false, 123 } satisfies ProgressGuide 124 setLocalGuideState(guideObj) 125 mutateAsync(guideObj) 126 } else if (guide === 'follow-10') { 127 const guideObj = { 128 guide: 'follow-10', 129 numFollows: 0, 130 isComplete: false, 131 } satisfies ProgressGuide 132 setLocalGuideState(guideObj) 133 mutateAsync(guideObj) 134 } 135 }, 136 137 endProgressGuide() { 138 setLocalGuideState(undefined) 139 mutateAsync(undefined) 140 }, 141 142 captureAction(action: ProgressGuideAction, count = 1) { 143 let guide = activeProgressGuide 144 if (!guide || guide?.isComplete) { 145 return 146 } 147 if (guide?.guide === 'like-10-and-follow-7') { 148 if (action === ProgressGuideAction.Like) { 149 guide = { 150 ...guide, 151 numLikes: (Number(guide.numLikes) || 0) + count, 152 } 153 if (guide.numLikes === 1) { 154 firstLikeToastRef.current?.open() 155 } 156 if (guide.numLikes === 5) { 157 fifthLikeToastRef.current?.open() 158 } 159 if (guide.numLikes === 10) { 160 tenthLikeToastRef.current?.open() 161 } 162 } 163 if (action === ProgressGuideAction.Follow) { 164 guide = { 165 ...guide, 166 numFollows: (Number(guide.numFollows) || 0) + count, 167 } 168 } 169 if (Number(guide.numLikes) >= 10 && Number(guide.numFollows) >= 7) { 170 guide = { 171 ...guide, 172 isComplete: true, 173 } 174 } 175 } else if (guide?.guide === 'follow-10') { 176 if (action === ProgressGuideAction.Follow) { 177 guide = { 178 ...guide, 179 numFollows: (Number(guide.numFollows) || 0) + count, 180 } 181 182 if (guide.numFollows === 5) { 183 fifthFollowToastRef.current?.open() 184 } 185 if (guide.numFollows === 10) { 186 tenthFollowToastRef.current?.open() 187 } 188 } 189 if (Number(guide.numFollows) >= 10) { 190 guide = { 191 ...guide, 192 isComplete: true, 193 } 194 } 195 } 196 197 setLocalGuideState(guide) 198 mutateAsync(guide?.isComplete ? undefined : guide) 199 }, 200 } 201 }, [activeProgressGuide, mutateAsync, setLocalGuideState]) 202 203 return ( 204 <ProgressGuideContext.Provider value={localGuideState}> 205 <ProgressGuideControlContext.Provider value={controls}> 206 {children} 207 {localGuideState?.guide === 'like-10-and-follow-7' && ( 208 <> 209 <ProgressGuideToast 210 ref={firstLikeToastRef} 211 title={_(msg`Your first like!`)} 212 subtitle={_(msg`Like 10 posts to train the Discover feed`)} 213 /> 214 <ProgressGuideToast 215 ref={fifthLikeToastRef} 216 title={_(msg`Half way there!`)} 217 subtitle={_(msg`Like 10 posts to train the Discover feed`)} 218 /> 219 <ProgressGuideToast 220 ref={tenthLikeToastRef} 221 title={_(msg`Task complete - 10 likes!`)} 222 subtitle={_(msg`The Discover feed now knows what you like`)} 223 /> 224 <ProgressGuideToast 225 ref={fifthFollowToastRef} 226 title={_(msg`Half way there!`)} 227 subtitle={_(msg`Follow 10 accounts`)} 228 /> 229 <ProgressGuideToast 230 ref={tenthFollowToastRef} 231 title={_(msg`Task complete - 10 follows!`)} 232 subtitle={_(msg`You've found some people to follow`)} 233 /> 234 </> 235 )} 236 </ProgressGuideControlContext.Provider> 237 </ProgressGuideContext.Provider> 238 ) 239}