1- import { useEffect , useMemo , useRef , useState } from "react" ;
1+ import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
22import type { JSX } from "react" ;
33import { useDispatch } from "react-redux" ;
44import { BounceLoader } from "react-spinners" ;
55import { v4 as uuidv4 } from "uuid" ;
66
77import { authenticate } from "../../api/user" ;
88import { DEFAULT_OPTION as LANGUAGES_DEFAULT_LAYOUT } from "../../components/Home/LanguagesLayoutSection" ;
9- import { DEFAULT_OPTION as STATS_DEFAULT_RANK } from "../../components/Home/StatsRankSection" ;
109import { DEFAULT_OPTION as WAKATIME_DEFAULT_LAYOUT } from "../../components/Home/WakatimeLayoutSection" ;
11- import {
12- DEMO_GIST ,
13- DEMO_REPO ,
14- DEMO_USER ,
15- DEMO_WAKATIME_USER ,
16- HOST ,
17- } from "../../constants" ;
10+ import { DEMO_USER } from "../../constants" ;
1811import { CardType } from "../../models/CardType" ;
1912import { STAGE_LABELS } from "../../models/Stage" ;
2013import type { StageIndex } from "../../models/Stage" ;
@@ -27,6 +20,8 @@ import {
2720import { login } from "../../redux/slices/user" ;
2821
2922import { buildCardUrl } from "./buildCardUrl" ;
23+ import { getDefaultCardOptions } from "./cardOptions" ;
24+ import type { CardOptions } from "./cardOptions" ;
3025import { CustomizeStage } from "./stages/Customize" ;
3126import { DisplayStage } from "./stages/Display" ;
3227import { LoginStage } from "./stages/Login/Login" ;
@@ -48,57 +43,44 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element {
4843
4944 const dispatch = useDispatch ( ) ;
5045
51- // for stage two
52- const [ selectedUserId , setSelectedUserId ] = useState ( userId ) ;
53- const [ repo , setRepo ] = useState ( DEMO_REPO ) ;
54- const [ gist , setGist ] = useState ( DEMO_GIST ) ;
55- const [ wakatimeUser , setWakatimeUser ] = useState ( DEMO_WAKATIME_USER ) ;
56-
5746 const [ selectedCard , setSelectedCard ] = useState < CardType > ( CardType . STATS ) ;
5847
48+ // for stages two and three
49+ const [ cardOptions , setCardOptions ] = useState ( ( ) =>
50+ getDefaultCardOptions ( userId ) ,
51+ ) ;
52+
53+ const setCardOption = useCallback <
54+ < K extends keyof CardOptions > ( key : K , value : CardOptions [ K ] ) => void
55+ > ( ( key , value ) => {
56+ setCardOptions ( ( prev ) => ( { ...prev , [ key ] : value } ) ) ;
57+ } , [ ] ) ;
58+
5959 // Reset the selected user to the resolved account id when it changes,
6060 // adjusting state during render rather than in an effect:
6161 // https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes
6262 const [ prevUserId , setPrevUserId ] = useState ( userId ) ;
6363 if ( userId !== prevUserId ) {
6464 setPrevUserId ( userId ) ;
65- setSelectedUserId ( userId ) ;
65+ setCardOptions ( ( prev ) => ( { ... prev , selectedUserId : userId } ) ) ;
6666 }
6767
68- // for stage three
69- const [ selectedStatsRank , setSelectedStatsRank ] =
70- useState ( STATS_DEFAULT_RANK ) ;
71- const [ selectedLanguagesLayout , setSelectedLanguagesLayout ] = useState (
72- LANGUAGES_DEFAULT_LAYOUT ,
73- ) ;
74- const [ selectedWakatimeLayout , setSelectedWakatimeLayout ] = useState (
75- WAKATIME_DEFAULT_LAYOUT ,
76- ) ;
77-
78- const [ showTitle , setShowTitle ] = useState ( true ) ;
79- const [ showOwner , setShowOwner ] = useState ( false ) ;
80- const [ descriptionLines , setDescriptionLines ] = useState <
81- number | undefined
82- > ( ) ;
83- const [ customTitle , setCustomTitle ] = useState ( "" ) ;
84- const [ langsCount , setLangsCount ] = useState < number | undefined > ( ) ;
85- const [ hideValues , setHideValues ] = useState ( false ) ;
86- const [ showAllStats , setShowAllStats ] = useState ( false ) ;
87- const [ showIcons , setShowIcons ] = useState ( false ) ;
88- const [ includeAllCommits , setIncludeAllCommits ] = useState ( true ) ;
89- const [ enableAnimations , setEnableAnimations ] = useState ( true ) ;
90- const [ usePercent , setUsePercent ] = useState ( false ) ;
91-
9268 const { isDark } = useTheme ( ) ;
9369 const [ theme , setTheme ] = useState ( isDark ? "dark" : "default" ) ;
9470
9571 const handleCardTypeChange = ( cardType : CardType ) => {
9672 if ( cardType === CardType . TOP_LANGS ) {
97- setLangsCount ( 4 ) ;
98- setSelectedWakatimeLayout ( WAKATIME_DEFAULT_LAYOUT ) ;
73+ setCardOptions ( ( prev ) => ( {
74+ ...prev ,
75+ langsCount : 4 ,
76+ selectedWakatimeLayout : WAKATIME_DEFAULT_LAYOUT ,
77+ } ) ) ;
9978 } else if ( cardType === CardType . WAKATIME ) {
100- setLangsCount ( 6 ) ;
101- setSelectedLanguagesLayout ( LANGUAGES_DEFAULT_LAYOUT ) ;
79+ setCardOptions ( ( prev ) => ( {
80+ ...prev ,
81+ langsCount : 6 ,
82+ selectedLanguagesLayout : LANGUAGES_DEFAULT_LAYOUT ,
83+ } ) ) ;
10284 }
10385
10486 if ( theme === "default" || theme === "default_repocard" ) {
@@ -119,51 +101,8 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element {
119101 // string), but memoizing keeps it safe to pass as a prop if any of them are
120102 // ever wrapped in React.memo.
121103 const cardBuilder = useMemo (
122- ( ) =>
123- buildCardUrl ( {
124- userId,
125- selectedCard,
126- selectedUserId,
127- repo,
128- gist,
129- wakatimeUser,
130- selectedStatsRank,
131- selectedLanguagesLayout,
132- selectedWakatimeLayout,
133- showTitle,
134- showOwner,
135- descriptionLines,
136- customTitle,
137- langsCount,
138- hideValues,
139- showAllStats,
140- showIcons,
141- includeAllCommits,
142- enableAnimations,
143- usePercent,
144- } ) ,
145- [
146- userId ,
147- selectedCard ,
148- selectedUserId ,
149- repo ,
150- gist ,
151- wakatimeUser ,
152- selectedStatsRank ,
153- selectedLanguagesLayout ,
154- selectedWakatimeLayout ,
155- showTitle ,
156- showOwner ,
157- descriptionLines ,
158- customTitle ,
159- langsCount ,
160- hideValues ,
161- showAllStats ,
162- showIcons ,
163- includeAllCommits ,
164- enableAnimations ,
165- usePercent ,
166- ] ,
104+ ( ) => buildCardUrl ( userId , selectedCard , cardOptions ) ,
105+ [ userId , selectedCard , cardOptions ] ,
167106 ) ;
168107
169108 // Preview builder for the customize stage, dark-themed to match the surroundings.
@@ -187,10 +126,10 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element {
187126 const cardDescriptor = useCardDescriptor ( {
188127 selectedCard,
189128 themeBuilder,
190- repo,
129+ repo : cardOptions . repo ,
191130 userId,
192- wakatimeUser,
193- gist,
131+ wakatimeUser : cardOptions . wakatimeUser ,
132+ gist : cardOptions . gist ,
194133 } ) ;
195134
196135 const contentSectionRef = useRef < HTMLDivElement | null > ( null ) ;
@@ -208,31 +147,31 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element {
208147 } , [ stage ] ) ;
209148
210149 useEffect ( ( ) => {
211- async function redirectCode ( ) {
212- // After requesting GitHub access, GitHub redirects back to your app with a code parameter
213- const url = window . location . href ;
150+ // After requesting GitHub access, GitHub redirects back to the app with a
151+ // code parameter appended to the redirect URI (which carries mode=private|public).
152+ const url = new URL ( window . location . href ) ;
153+ const code = url . searchParams . get ( "code" ) ;
154+ if ( code === null ) {
155+ return ;
156+ }
214157
215- // If GitHub API returns the code parameter
216- if ( url . includes ( "code=" ) ) {
217- const tempPrivateAccess = url . includes ( "private" ) ;
218- const newUrl = url . split ( "code=" , 2 ) as [ string , string ] ;
219- const redirect = `${ url . split ( HOST ) [ 0 ] as string } ${ HOST } /frontend` ;
220- window . history . pushState ( { } , "" , redirect ) ;
221- setIsLoading ( true ) ;
222- const userKey = uuidv4 ( ) ;
223- const newUserId = await authenticate (
224- newUrl [ 1 ] ,
225- tempPrivateAccess ,
226- userKey ,
227- ) ;
158+ const tempPrivateAccess = url . searchParams . get ( "mode" ) === "private" ;
159+ window . history . pushState ( { } , "" , `${ url . origin } /frontend` ) ;
228160
161+ async function exchangeCode ( code : string ) {
162+ setIsLoading ( true ) ;
163+ try {
164+ const userKey = uuidv4 ( ) ;
165+ const newUserId = await authenticate ( code , tempPrivateAccess , userKey ) ;
229166 dispatch ( login ( { userId : newUserId , userKey } ) ) ;
230-
167+ } catch ( error ) {
168+ console . error ( error ) ;
169+ } finally {
231170 setIsLoading ( false ) ;
232171 }
233172 }
234173
235- void redirectCode ( ) ;
174+ void exchangeCode ( code ) ;
236175 } , [ dispatch ] ) ;
237176
238177 if ( isLoading ) {
@@ -283,13 +222,7 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element {
283222 </ p >
284223 </ div >
285224 ) : (
286- [
287- "" ,
288- "You will be able to customize your card in future steps." ,
289- "" ,
290- "" ,
291- "Display the finished card on GitHub, Twitter/X, LinkedIn, or anywhere else!" ,
292- ] [ stage ]
225+ STAGE_LABELS [ stage ] . description
293226 ) }
294227 </ div >
295228 </ div >
@@ -309,42 +242,8 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element {
309242 { stage === 2 && (
310243 < CustomizeStage
311244 selectedCard = { selectedCard }
312- selectedStatsRank = { selectedStatsRank }
313- setSelectedStatsRank = { setSelectedStatsRank }
314- selectedLanguagesLayout = { selectedLanguagesLayout }
315- setSelectedLanguagesLayout = { setSelectedLanguagesLayout }
316- selectedWakatimeLayout = { selectedWakatimeLayout }
317- setSelectedWakatimeLayout = { setSelectedWakatimeLayout }
318- showTitle = { showTitle }
319- setShowTitle = { setShowTitle }
320- showOwner = { showOwner }
321- setShowOwner = { setShowOwner }
322- descriptionLines = { descriptionLines }
323- setDescriptionLines = { setDescriptionLines }
324- customTitle = { customTitle }
325- setCustomTitle = { setCustomTitle }
326- langsCount = { langsCount }
327- setLangsCount = { setLangsCount }
328- hideValues = { hideValues }
329- setHideValues = { setHideValues }
330- showAllStats = { showAllStats }
331- setShowAllStats = { setShowAllStats }
332- showIcons = { showIcons }
333- setShowIcons = { setShowIcons }
334- includeAllCommits = { includeAllCommits }
335- setIncludeAllCommits = { setIncludeAllCommits }
336- enableAnimations = { enableAnimations }
337- setEnableAnimations = { setEnableAnimations }
338- selectedUserId = { selectedUserId }
339- setSelectedUserId = { setSelectedUserId }
340- repo = { repo }
341- setRepo = { setRepo }
342- gist = { gist }
343- setGist = { setGist }
344- wakatimeUser = { wakatimeUser }
345- setWakatimeUser = { setWakatimeUser }
346- usePercent = { usePercent }
347- setUsePercent = { setUsePercent }
245+ options = { cardOptions }
246+ onOptionChange = { setCardOption }
348247 card = { customizeCardBuilder }
349248 setStage = { setStage }
350249 />
0 commit comments