|
1 | 1 | 'use client'; |
2 | 2 |
|
3 | 3 | import { useCallback, useEffect, useRef, useState, type ReactElement } from 'react'; |
| 4 | +import { useRouter, useSearchParams } from 'next/navigation'; |
4 | 5 | import { validateGitHubUsername } from '@/lib/validations'; |
5 | 6 | import Link from 'next/link'; |
6 | 7 | import { motion } from 'framer-motion'; |
@@ -56,6 +57,39 @@ export default function CustomizePage(): ReactElement { |
56 | 57 | const hasUsername = trimmedUsername.length > 0; |
57 | 58 | const isRandomTheme = theme === 'random'; |
58 | 59 |
|
| 60 | + const router = useRouter(); |
| 61 | + const searchParams = useSearchParams(); |
| 62 | + |
| 63 | + // On mount: initialize state from URL search params |
| 64 | + /* eslint-disable react-hooks/set-state-in-effect */ |
| 65 | + useEffect(() => { |
| 66 | + const u = searchParams.get('user') ?? ''; |
| 67 | + const t = searchParams.get('theme') ?? 'dark'; |
| 68 | + setUsername(u); |
| 69 | + setTheme(t); |
| 70 | + setBgHex(searchParams.get('bg') ?? ''); |
| 71 | + setAccentHex(searchParams.get('accent') ?? ''); |
| 72 | + setTextHex(searchParams.get('text') ?? ''); |
| 73 | + setScale((searchParams.get('scale') as Scale) ?? 'linear'); |
| 74 | + setSpeed(searchParams.get('speed') ?? '8s'); |
| 75 | + setFont((searchParams.get('font') as Font) ?? 'Inter'); |
| 76 | + setYear(searchParams.get('year') ?? ''); |
| 77 | + setRadius(Number(searchParams.get('radius') ?? 8)); |
| 78 | + setSize((searchParams.get('size') as BadgeSize) ?? 'medium'); |
| 79 | + setHideTitle(searchParams.get('hide_title') === 'true'); |
| 80 | + setHideBackground(searchParams.get('hide_background') === 'true'); |
| 81 | + setHideStats(searchParams.get('hide_stats') === 'true'); |
| 82 | + setViewMode((searchParams.get('view') as ViewMode) ?? 'default'); |
| 83 | + setDeltaFormat((searchParams.get('delta_format') as DeltaFormat) ?? 'percent'); |
| 84 | + setBadgeWidth(searchParams.get('width') ? Number(searchParams.get('width')) : ''); |
| 85 | + setBadgeHeight(searchParams.get('height') ? Number(searchParams.get('height')) : ''); |
| 86 | + setGrace(Number(searchParams.get('grace') ?? 1)); |
| 87 | + setLanguage((searchParams.get('lang') as Language) ?? 'en'); |
| 88 | + setTimezone((searchParams.get('tz') as Timezone) ?? 'UTC'); |
| 89 | + // eslint-disable-next-line react-hooks/exhaustive-deps |
| 90 | + }, []); |
| 91 | + /* eslint-enable react-hooks/set-state-in-effect */ |
| 92 | + |
59 | 93 | useEffect(() => { |
60 | 94 | return () => { |
61 | 95 | if (copyResetTimeoutRef.current !== null) { |
@@ -114,6 +148,12 @@ export default function CustomizePage(): ReactElement { |
114 | 148 | }); |
115 | 149 | const previewSrc = `/api/streak?${queryString}`; |
116 | 150 |
|
| 151 | + // On change sync state to URL |
| 152 | + useEffect(() => { |
| 153 | + if (!queryString) return; |
| 154 | + router.replace(`/customize?${queryString}`, { scroll: false }); |
| 155 | + }, [queryString, router]); |
| 156 | + |
117 | 157 | useEffect(() => { |
118 | 158 | // eslint-disable-next-line react-hooks/set-state-in-effect |
119 | 159 | setErrorMessage(null); |
|
0 commit comments