orchestrate privacy modal in onboarding flow
This commit is contained in:
parent
22f132bbc8
commit
93ae9f3082
3 changed files with 33 additions and 29 deletions
|
|
@ -6,7 +6,6 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|||
import { ThemeProvider } from '@/contexts/ThemeContext';
|
||||
import { AuthRateLimitProvider } from '@/contexts/AuthRateLimitContext';
|
||||
import { RuntimeConfigProvider } from '@/contexts/RuntimeConfigContext';
|
||||
import { PrivacyModal } from '@/components/PrivacyModal';
|
||||
import { AuthLoader } from '@/components/auth/AuthLoader';
|
||||
|
||||
interface ProvidersProps {
|
||||
|
|
@ -38,10 +37,7 @@ export function Providers({ children, authEnabled, authBaseUrl, allowAnonymousAu
|
|||
>
|
||||
<ThemeProvider>
|
||||
<AuthLoader>
|
||||
<>
|
||||
{children}
|
||||
{authEnabled && <PrivacyModal />}
|
||||
</>
|
||||
{children}
|
||||
</AuthLoader>
|
||||
</ThemeProvider>
|
||||
</AuthRateLimitProvider>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { Fragment, useState, useEffect, useCallback } from 'react';
|
||||
import { Fragment, useState, useEffect } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogPanel,
|
||||
|
|
@ -9,10 +9,12 @@ import {
|
|||
TransitionChild,
|
||||
Button,
|
||||
} from '@headlessui/react';
|
||||
import { updateAppConfig, getAppConfig } from '@/lib/client/dexie';
|
||||
import { updateAppConfig } from '@/lib/client/dexie';
|
||||
|
||||
interface PrivacyModalProps {
|
||||
isOpen: boolean;
|
||||
onAccept?: () => void;
|
||||
onDismiss?: () => void;
|
||||
}
|
||||
|
||||
function PrivacyModalBody({ origin }: { origin: string }) {
|
||||
|
|
@ -38,32 +40,23 @@ function PrivacyModalBody({ origin }: { origin: string }) {
|
|||
);
|
||||
}
|
||||
|
||||
export function PrivacyModal({ onAccept }: PrivacyModalProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
export function PrivacyModal({ isOpen, onAccept, onDismiss }: PrivacyModalProps) {
|
||||
const [origin, setOrigin] = useState('');
|
||||
const [agreed, setAgreed] = useState(false);
|
||||
|
||||
const checkPrivacyAccepted = useCallback(async () => {
|
||||
const config = await getAppConfig();
|
||||
if (!config?.privacyAccepted) {
|
||||
setIsOpen(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
checkPrivacyAccepted().catch((err) => {
|
||||
console.error('Privacy acceptance check failed:', err);
|
||||
});
|
||||
}, [checkPrivacyAccepted]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
setOrigin(window.location.origin);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setAgreed(false);
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
const handleAccept = async () => {
|
||||
await updateAppConfig({ privacyAccepted: true });
|
||||
setIsOpen(false);
|
||||
if (typeof window !== 'undefined') {
|
||||
window.dispatchEvent(new Event('openreader:privacyAccepted'));
|
||||
}
|
||||
|
|
@ -72,7 +65,7 @@ export function PrivacyModal({ onAccept }: PrivacyModalProps) {
|
|||
|
||||
return (
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-[60]" onClose={() => { }}>
|
||||
<Dialog as="div" className="relative z-[80]" onClose={onDismiss ?? (() => { })}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
|
||||
import ClaimDataModal, { type ClaimableCounts } from '@/components/auth/ClaimDataModal';
|
||||
import { DexieMigrationModal } from '@/components/documents/DexieMigrationModal';
|
||||
import { PrivacyModal } from '@/components/PrivacyModal';
|
||||
import { useAuthConfig } from '@/contexts/AuthRateLimitContext';
|
||||
import { useAuthSession } from '@/hooks/useAuthSession';
|
||||
import { useRuntimeConfig } from '@/contexts/RuntimeConfigContext';
|
||||
|
|
@ -120,7 +121,7 @@ export function OnboardingFlowProvider({ children }: { children: ReactNode }) {
|
|||
const userId = user?.id ?? null;
|
||||
const isAnonymous = Boolean(user?.isAnonymous);
|
||||
|
||||
const [activeBlockingModal, setActiveBlockingModal] = useState<'claim' | 'migration' | null>(null);
|
||||
const [activeBlockingModal, setActiveBlockingModal] = useState<'privacy' | 'claim' | 'migration' | null>(null);
|
||||
const [claimableCounts, setClaimableCounts] = useState<ClaimableCounts>(EMPTY_CLAIM_COUNTS);
|
||||
const [migrationCounts, setMigrationCounts] = useState<{ localCount: number; missingCount: number }>({
|
||||
localCount: 0,
|
||||
|
|
@ -144,12 +145,14 @@ export function OnboardingFlowProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
runningAdvanceRef.current = true;
|
||||
try {
|
||||
if (activeBlockingModal) {
|
||||
return;
|
||||
}
|
||||
|
||||
const local = await readLocalOnboardingSnapshot();
|
||||
if (authEnabled && !local.privacyAccepted) {
|
||||
setActiveBlockingModal('privacy');
|
||||
return;
|
||||
}
|
||||
if (activeBlockingModal === 'privacy') {
|
||||
setActiveBlockingModal(null);
|
||||
} else if (activeBlockingModal) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -237,6 +240,11 @@ export function OnboardingFlowProvider({ children }: { children: ReactNode }) {
|
|||
void advanceFlow();
|
||||
}, [advanceFlow]);
|
||||
|
||||
const handlePrivacyAccepted = useCallback(() => {
|
||||
setActiveBlockingModal(null);
|
||||
void advanceFlow();
|
||||
}, [advanceFlow]);
|
||||
|
||||
useEffect(() => {
|
||||
void advanceFlow();
|
||||
}, [advanceFlow, authEnabled, isAnonymous, userId]);
|
||||
|
|
@ -280,6 +288,13 @@ export function OnboardingFlowProvider({ children }: { children: ReactNode }) {
|
|||
return (
|
||||
<OnboardingFlowContext.Provider value={contextValue}>
|
||||
{children}
|
||||
{authEnabled && (
|
||||
<PrivacyModal
|
||||
isOpen={activeBlockingModal === 'privacy'}
|
||||
onAccept={handlePrivacyAccepted}
|
||||
onDismiss={() => { }}
|
||||
/>
|
||||
)}
|
||||
{authEnabled && (
|
||||
<ClaimDataModal
|
||||
isOpen={activeBlockingModal === 'claim'}
|
||||
|
|
|
|||
Loading…
Reference in a new issue