seems alright
This commit is contained in:
parent
ebe7bbb51d
commit
eac1bd0d13
58 changed files with 23125 additions and 153505 deletions
|
|
@ -236,6 +236,13 @@ export default function App() {
|
|||
const authCompletedRef = useRef(false);
|
||||
const [licenseSuccessStatus, setLicenseSuccessStatus] = useState<LicenseSuccessStatus>('hidden');
|
||||
|
||||
// Keep a ref to the latest refreshAuth so the mount-only startup effect always
|
||||
// calls the current implementation without re-running when the callback identity changes.
|
||||
const refreshAuthRef = useRef(refreshAuth);
|
||||
useEffect(() => {
|
||||
refreshAuthRef.current = refreshAuth;
|
||||
}, [refreshAuth]);
|
||||
|
||||
const openAuthModal = useCallback(
|
||||
(
|
||||
tab: 'login' | 'register',
|
||||
|
|
@ -284,14 +291,14 @@ export default function App() {
|
|||
async function refreshOnStartup() {
|
||||
if (!returnedFromCheckout) {
|
||||
// Always refresh auth on startup to pick up server-side subscription changes.
|
||||
refreshAuth().catch(() => {});
|
||||
refreshAuthRef.current().catch(() => {});
|
||||
return;
|
||||
}
|
||||
|
||||
setLicenseSuccessStatus('verifying');
|
||||
for (let attempt = 0; attempt < LICENSE_VERIFICATION_ATTEMPTS; attempt += 1) {
|
||||
try {
|
||||
const refreshedUser = await refreshAuth();
|
||||
const refreshedUser = await refreshAuthRef.current();
|
||||
if (cancelled) return;
|
||||
if (hasFullAccess(refreshedUser)) {
|
||||
trackEvent('Purchase');
|
||||
|
|
@ -314,7 +321,9 @@ export default function App() {
|
|||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
// Mount-only: this is a startup auth refresh / license verification handshake
|
||||
// that must fire exactly once on initial load. refreshAuth is read via ref.
|
||||
}, []);
|
||||
|
||||
const savedSearches = useSavedSearches(user?.id ?? null);
|
||||
const [showSaveModal, setShowSaveModal] = useState(false);
|
||||
|
|
@ -381,20 +390,17 @@ export default function App() {
|
|||
[inviteCode]
|
||||
);
|
||||
|
||||
const handleEditSearch = useCallback(
|
||||
(id: string, name: string, params: string) => {
|
||||
const search = params.startsWith('?') ? params : `?${params}`;
|
||||
dashboardSearchRef.current = search;
|
||||
const url = `/dashboard${search}`;
|
||||
window.history.pushState({ page: 'dashboard', hash: '' }, '', url);
|
||||
setMapUrlState(parseUrlState());
|
||||
setDashboardRouteKey(search);
|
||||
setRouteHash('');
|
||||
setActivePage('dashboard');
|
||||
setEditingSearch({ id, name });
|
||||
},
|
||||
[]
|
||||
);
|
||||
const handleEditSearch = useCallback((id: string, name: string, params: string) => {
|
||||
const search = params.startsWith('?') ? params : `?${params}`;
|
||||
dashboardSearchRef.current = search;
|
||||
const url = `/dashboard${search}`;
|
||||
window.history.pushState({ page: 'dashboard', hash: '' }, '', url);
|
||||
setMapUrlState(parseUrlState());
|
||||
setDashboardRouteKey(search);
|
||||
setRouteHash('');
|
||||
setActivePage('dashboard');
|
||||
setEditingSearch({ id, name });
|
||||
}, []);
|
||||
|
||||
const handleCancelEdit = useCallback(() => {
|
||||
setEditingSearch(null);
|
||||
|
|
@ -451,13 +457,25 @@ export default function App() {
|
|||
activePageRef.current = activePage;
|
||||
}, [activePage]);
|
||||
|
||||
// Refs for the initial history.replaceState seed below — the popstate effect runs
|
||||
// mount-only, but it needs to read the *initial* page/hash/inviteCode values once.
|
||||
const initialPageRef = useRef(activePage);
|
||||
const initialRouteHashRef = useRef(routeHash);
|
||||
const initialInviteCodeRef = useRef(inviteCode);
|
||||
|
||||
useEffect(() => {
|
||||
if (!window.history.state?.page) {
|
||||
const hash = routeHash || normalizeHash(window.location.hash);
|
||||
const initialActivePage = initialPageRef.current;
|
||||
const hash = initialRouteHashRef.current || normalizeHash(window.location.hash);
|
||||
window.history.replaceState(
|
||||
{ page: activePage, hash },
|
||||
{ page: initialActivePage, hash },
|
||||
'',
|
||||
buildPageUrl(activePage, inviteCode ?? undefined, window.location.search, hash)
|
||||
buildPageUrl(
|
||||
initialActivePage,
|
||||
initialInviteCodeRef.current ?? undefined,
|
||||
window.location.search,
|
||||
hash
|
||||
)
|
||||
);
|
||||
}
|
||||
const handlePopState = (e: PopStateEvent) => {
|
||||
|
|
@ -487,7 +505,10 @@ export default function App() {
|
|||
};
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
return () => window.removeEventListener('popstate', handlePopState);
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
// Mount-only: registers a single popstate listener for the app lifetime and
|
||||
// seeds initial history state. The handler uses only stable setters and module
|
||||
// functions; initial-render values are read via refs above.
|
||||
}, []);
|
||||
|
||||
const { fetchSearches } = savedSearches;
|
||||
useEffect(() => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue