diff --git a/src/components/PrivacyModal.tsx b/src/components/PrivacyModal.tsx index 0c63c61..600c7c7 100644 --- a/src/components/PrivacyModal.tsx +++ b/src/components/PrivacyModal.tsx @@ -1,15 +1,8 @@ 'use client'; -import { Fragment, useState, useEffect } from 'react'; -import { - Dialog, - DialogPanel, - DialogTitle, - Transition, - TransitionChild, -} from '@headlessui/react'; +import { useState, useEffect } from 'react'; import { updateAppConfig } from '@/lib/client/dexie'; -import { Button, dialogPanelStyles } from '@/components/ui'; +import { Button, ModalFrame, ModalTitle } from '@/components/ui'; interface PrivacyModalProps { isOpen: boolean; @@ -77,81 +70,46 @@ export function PrivacyModal({ isOpen, onAccept, onDismiss }: PrivacyModalProps) }; return ( - - { })}> - -
- + {})} panelTestId="privacy-modal" className="z-[80]"> + Privacy & Data Usage -
-
- - - - Privacy & Data Usage - + - - -
-
-
- setAgreed(e.target.checked)} - className="h-4 w-4 rounded border-line text-accent focus:ring-accent-line bg-surface" - /> -
-
- {' '} - - Privacy Policy - -
-
- -
- -
-
-
-
+
+
+
+ setAgreed(e.target.checked)} + className="h-4 w-4 rounded border-line text-accent focus:ring-accent-line bg-surface" + /> +
+
+ {' '} + + Privacy Policy +
-
-
+ +
+ +
+ + ); } @@ -179,64 +137,28 @@ export function showPrivacyModal(): void { }; return ( - { root.unmount(); container.remove(); }} > - - Privacy & Data Usage + + + +
+ -
- -
- - -
-
+ Close + + + ); }; diff --git a/src/components/auth/ClaimDataModal.tsx b/src/components/auth/ClaimDataModal.tsx index fbfa02f..54f80ec 100644 --- a/src/components/auth/ClaimDataModal.tsx +++ b/src/components/auth/ClaimDataModal.tsx @@ -1,16 +1,9 @@ 'use client'; -import { Fragment, useState } from 'react'; -import { - Dialog, - DialogPanel, - DialogTitle, - Transition, - TransitionChild, -} from '@headlessui/react'; +import { useState } from 'react'; import { useRouter } from 'next/navigation'; import toast from 'react-hot-toast'; -import { Button, dialogPanelStyles } from '@/components/ui'; +import { Button, ModalFrame, ModalTitle } from '@/components/ui'; export type ClaimableCounts = { documents: number; @@ -74,83 +67,48 @@ export default function ClaimDataModal({ }; return ( - - - + Existing Data Found + +

+ We found existing anonymous data from before auth was enabled. + Claim it now to attach it to your account. +

+ +
+
Claimable data
+
    +
  • {claimableCounts.documents} document(s)
  • +
  • {claimableCounts.audiobooks} audiobook(s)
  • +
  • {claimableCounts.preferences} preference set(s)
  • +
  • {claimableCounts.progress} reading progress record(s)
  • +
+
+ +

+ ⚠️ First user to claim this data will own it and revoke access for anyone else. +

+ +
+ - -
- -
- - -
-
+ Dismiss + + + + ); } diff --git a/src/components/documents/DexieMigrationModal.tsx b/src/components/documents/DexieMigrationModal.tsx index 6993eab..d065adf 100644 --- a/src/components/documents/DexieMigrationModal.tsx +++ b/src/components/documents/DexieMigrationModal.tsx @@ -1,13 +1,12 @@ 'use client'; -import { Fragment, useCallback, useEffect, useState } from 'react'; -import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react'; +import { useCallback, useEffect, useState } from 'react'; import { getAllEpubDocuments, getAllHtmlDocuments, getAllPdfDocuments, updateAppConfig } from '@/lib/client/dexie'; import { listDocuments, mimeTypeForDoc, uploadDocuments } from '@/lib/client/api/documents'; import { useDocuments } from '@/contexts/DocumentContext'; import type { BaseDocument } from '@/types/documents'; import { cacheStoredDocumentFromBytes } from '@/lib/client/cache/documents'; -import { Button, dialogPanelStyles } from '@/components/ui'; +import { Button, ModalFrame, ModalTitle } from '@/components/ui'; type DexieMigrationModalProps = { isOpen: boolean; @@ -137,78 +136,53 @@ export function DexieMigrationModal({ if (!isOpen) return null; return ( - - (closeDisabled ? null : onComplete())}> - -
- - -
-
- - - - {title} - -
-

- Found {localCount} document{localCount === 1 ? '' : 's'} stored locally from an older version. - {displayMissingCount > 0 ? ( - <> {displayMissingCount} {displayMissingCount === 1 ? 'is' : 'are'} not here yet. - ) : null} - {' '}This app now stores documents on the server and keeps a local cache for speed. -

- {isUploading && ( -
-

{status}

-
-
-
-
- )} - {!isUploading && status ?

{status}

: null} -
- -
- - -
- - + { + if (!closeDisabled) onComplete(); + }} + panelTestId="migration-modal" + className="z-[80]" + > + {title} +
+

+ Found {localCount} document{localCount === 1 ? '' : 's'} stored locally from an older version. + {displayMissingCount > 0 ? ( + <> {displayMissingCount} {displayMissingCount === 1 ? 'is' : 'are'} not here yet. + ) : null} + {' '}This app now stores documents on the server and keeps a local cache for speed. +

+ {isUploading && ( +
+

{status}

+
+
+
-
-
-
+ )} + {!isUploading && status ?

{status}

: null} + + +
+ + +
+ ); } diff --git a/src/components/documents/DocumentSelectionModal.tsx b/src/components/documents/DocumentSelectionModal.tsx index 4119f37..a89b152 100644 --- a/src/components/documents/DocumentSelectionModal.tsx +++ b/src/components/documents/DocumentSelectionModal.tsx @@ -1,9 +1,8 @@ 'use client'; -import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react'; -import { Fragment, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { BaseDocument } from '@/types/documents'; -import { Button, dialogPanelStyles } from '@/components/ui'; +import { Button, ModalFrame, ModalTitle } from '@/components/ui'; interface DocumentSelectionModalProps { isOpen: boolean; @@ -114,116 +113,83 @@ export function DocumentSelectionModal({ }; return ( - - - -
- - -
-
- - - - {title} - {files.length > 0 && ( -
- -
- )} -
- -
- {isLoading ? ( - - ) : errorMessage ? ( -
{errorMessage}
- ) : files.length === 0 ? ( -
No documents found.
- ) : ( -
- {files.map((file) => { - const isSelected = selectedIds.has(file.id); - return ( -
handleRowClick(e, file.id)} - className={`flex items-center gap-3 px-3 py-2 rounded-md cursor-pointer text-sm select-none - ${isSelected ? 'bg-accent-wash' : 'hover:bg-accent-wash'} - `} - > -
e.stopPropagation()}> - handleCheckboxChange(file.id, e.target.checked)} - className="rounded border-muted text-accent focus:ring-accent-line" - /> -
-
- {file.name} -
-
{formatSize(file.size)}
-
- ); - })} -
- )} -
- -
- - -
-
-
+ + + {title} + {files.length > 0 && ( +
+
-
-
-
+ )} + + +
+ {isLoading ? ( + + ) : errorMessage ? ( +
{errorMessage}
+ ) : files.length === 0 ? ( +
No documents found.
+ ) : ( +
+ {files.map((file) => { + const isSelected = selectedIds.has(file.id); + return ( +
handleRowClick(e, file.id)} + className={`flex items-center gap-3 px-3 py-2 rounded-md cursor-pointer text-sm select-none ${ + isSelected ? 'bg-accent-wash' : 'hover:bg-accent-wash' + }`} + > +
e.stopPropagation()}> + handleCheckboxChange(file.id, e.target.checked)} + className="rounded border-muted text-accent focus:ring-accent-line" + /> +
+
+ {file.name} +
+
{formatSize(file.size)}
+
+ ); + })} +
+ )} +
+ +
+ + +
+ ); } diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index 6ba92f4..d12917f 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -43,6 +43,7 @@ export function ModalFrame({ panelClassName, panelTestId, onKeyDown, + afterLeave, }: { open: boolean; onClose: () => void; @@ -52,9 +53,10 @@ export function ModalFrame({ panelClassName?: string; panelTestId?: string; onKeyDown?: KeyboardEventHandler; + afterLeave?: () => void; }) { return ( - +