mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React 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'
20
21interface BaseProgressGuide {
22 guide: string
23 isComplete: boolean
24 [key: string]: any
25}
26
27interface Like10AndFollow7ProgressGuide extends BaseProgressGuide {
28 numLikes: number
29 numFollows: number
30}
31
32type ProgressGuide = Like10AndFollow7ProgressGuide | undefined
33
34const ProgressGuideContext = React.createContext<ProgressGuide>(undefined)
35
36const ProgressGuideControlContext = React.createContext<{
37 startProgressGuide(guide: ProgressGuideName): void
38 endProgressGuide(): void
39 captureAction(action: ProgressGuideAction, count?: number): void
40}>({
41 startProgressGuide: (_guide: ProgressGuideName) => {},
42 endProgressGuide: () => {},
43 captureAction: (_action: ProgressGuideAction, _count = 1) => {},
44})
45
46export function useProgressGuide(guide: ProgressGuideName) {
47 const ctx = React.useContext(ProgressGuideContext)
48 if (ctx?.guide === guide) {
49 return ctx
50 }
51 return undefined
52}
53
54export function useProgressGuideControls() {
55 return React.useContext(ProgressGuideControlContext)
56}
57
58export function Provider({children}: React.PropsWithChildren<{}>) {
59 const {_} = useLingui()
60 const {data: preferences} = usePreferencesQuery()
61 const {mutateAsync, variables, isPending} =
62 useSetActiveProgressGuideMutation()
63
64 const activeProgressGuide = (
65 isPending ? variables : preferences?.bskyAppState?.activeProgressGuide
66 ) as ProgressGuide
67
68 // ensure the unspecced attributes have the correct types
69 if (activeProgressGuide?.guide === 'like-10-and-follow-7') {
70 activeProgressGuide.numLikes = Number(activeProgressGuide.numLikes) || 0
71 activeProgressGuide.numFollows = Number(activeProgressGuide.numFollows) || 0
72 }
73
74 const [localGuideState, setLocalGuideState] =
75 React.useState<ProgressGuide>(undefined)
76
77 if (activeProgressGuide && !localGuideState) {
78 // hydrate from the server if needed
79 setLocalGuideState(activeProgressGuide)
80 }
81
82 const firstLikeToastRef = React.useRef<ProgressGuideToastRef | null>(null)
83 const fifthLikeToastRef = React.useRef<ProgressGuideToastRef | null>(null)
84 const tenthLikeToastRef = React.useRef<ProgressGuideToastRef | null>(null)
85 const guideCompleteToastRef = React.useRef<ProgressGuideToastRef | null>(null)
86
87 const controls = React.useMemo(() => {
88 return {
89 startProgressGuide(guide: ProgressGuideName) {
90 if (guide === 'like-10-and-follow-7') {
91 const guideObj = {
92 guide: 'like-10-and-follow-7',
93 numLikes: 0,
94 numFollows: 0,
95 isComplete: false,
96 }
97 setLocalGuideState(guideObj)
98 mutateAsync(guideObj)
99 }
100 },
101
102 endProgressGuide() {
103 setLocalGuideState(undefined)
104 mutateAsync(undefined)
105 },
106
107 captureAction(action: ProgressGuideAction, count = 1) {
108 let guide = activeProgressGuide
109 if (!guide || guide?.isComplete) {
110 return
111 }
112 if (guide?.guide === 'like-10-and-follow-7') {
113 if (action === ProgressGuideAction.Like) {
114 guide = {
115 ...guide,
116 numLikes: (Number(guide.numLikes) || 0) + count,
117 }
118 if (guide.numLikes === 1) {
119 firstLikeToastRef.current?.open()
120 }
121 if (guide.numLikes === 5) {
122 fifthLikeToastRef.current?.open()
123 }
124 if (guide.numLikes === 10) {
125 tenthLikeToastRef.current?.open()
126 }
127 }
128 if (action === ProgressGuideAction.Follow) {
129 guide = {
130 ...guide,
131 numFollows: (Number(guide.numFollows) || 0) + count,
132 }
133 }
134 if (Number(guide.numLikes) >= 10 && Number(guide.numFollows) >= 7) {
135 guide = {
136 ...guide,
137 isComplete: true,
138 }
139 }
140 }
141
142 setLocalGuideState(guide)
143 mutateAsync(guide?.isComplete ? undefined : guide)
144 },
145 }
146 }, [activeProgressGuide, mutateAsync, setLocalGuideState])
147
148 return (
149 <ProgressGuideContext.Provider value={localGuideState}>
150 <ProgressGuideControlContext.Provider value={controls}>
151 {children}
152 {localGuideState?.guide === 'like-10-and-follow-7' && (
153 <>
154 <ProgressGuideToast
155 ref={firstLikeToastRef}
156 title={_(msg`Your first like!`)}
157 subtitle={_(msg`Like 10 posts to train the Discover feed`)}
158 />
159 <ProgressGuideToast
160 ref={fifthLikeToastRef}
161 title={_(msg`Half way there!`)}
162 subtitle={_(msg`Like 10 posts to train the Discover feed`)}
163 />
164 <ProgressGuideToast
165 ref={tenthLikeToastRef}
166 title={_(msg`Task complete - 10 likes!`)}
167 subtitle={_(msg`The Discover feed now knows what you like`)}
168 />
169 <ProgressGuideToast
170 ref={guideCompleteToastRef}
171 title={_(msg`Algorithm training complete!`)}
172 subtitle={_(msg`The Discover feed now knows what you like`)}
173 />
174 </>
175 )}
176 </ProgressGuideControlContext.Provider>
177 </ProgressGuideContext.Provider>
178 )
179}