Files
readest/apps/readest-app/src/app/reader/components/SyncInfoDialog.tsx
T
Huang Xin f5657fb3a0 fix(share): correct recipient import flow and assorted UI polish (#4043)
- ensureSharedBookLocal helper makes sure the local library has both the
  Book entry and the bytes on disk after /import succeeds; navigating
  into the reader before this lands on "Book not found"
- ShareLanding navigates via navigateToReader (path form on web) so the
  reader actually renders instead of hitting the App Router stub and
  going blank
- Loading + progress UI on the landing page while bytes stream in;
  Open-in-app disabled mid-import to avoid races
- UserInfo header: vertically center avatar with name/email, tighter
  mobile gap, and a fillContainer prop on UserAvatar so a parent can
  size the box via classes without the inline style fighting back
- Rename "Share current page" -> "Share reading progress" (and matching
  post-generation hint) and shrink the dialog from 480 to 460px
- Drop the unused Reload Page menu item from SettingsMenu
- Translate the two new i18n keys across 31 locales

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 16:16:46 +02:00

68 lines
2.3 KiB
TypeScript

import React from 'react';
import Dialog from '@/components/Dialog';
import { useTranslation } from '@/hooks/useTranslation';
import { BookMetadata } from '@/libs/document';
import { formatLocaleDateTime, getMetadataHashInfo } from '@/utils/book';
interface SyncInfoDialogProps {
isOpen: boolean;
metadata: BookMetadata | null | undefined;
storedMetaHash?: string;
/** Most recent sync timestamp across pull + push of config and notes. */
lastSyncedAt?: number;
onClose: () => void;
}
const Row: React.FC<{ label: string; value: React.ReactNode }> = ({ label, value }) => (
<div className='flex flex-col gap-1'>
<span className='text-base-content/60 text-sm uppercase tracking-wide sm:text-xs'>{label}</span>
<div className='bg-base-200 text-base-content/90 break-all rounded-md p-2 font-mono text-sm sm:text-xs'>
{value}
</div>
</div>
);
const SyncInfoDialog: React.FC<SyncInfoDialogProps> = ({
isOpen,
metadata,
storedMetaHash,
lastSyncedAt,
onClose,
}) => {
const _ = useTranslation();
const info = metadata ? getMetadataHashInfo(metadata) : undefined;
const displayHash = storedMetaHash || info?.metaHash || '';
const hashesMatch = !info || !storedMetaHash || storedMetaHash === info.metaHash;
const placeholder = _('(none)');
const lastSyncedLabel = lastSyncedAt ? formatLocaleDateTime(lastSyncedAt) : _('Never synced');
return (
<Dialog
isOpen={isOpen}
onClose={onClose}
snapHeight={0.7}
title={_('Sync Info')}
boxClassName='sm:!min-w-[520px] sm:h-auto'
>
{isOpen && (
<div className='mb-4 mt-0 flex flex-col gap-3 p-2 sm:p-4'>
<Row label={_('Meta Hash')} value={displayHash || placeholder} />
{!hashesMatch && info && <Row label={_('Computed Hash')} value={info.metaHash} />}
<Row label={_('Title')} value={info?.title || placeholder} />
<Row
label={_('Author')}
value={info && info.authors.length > 0 ? info.authors.join(', ') : placeholder}
/>
<Row
label={_('Identifiers')}
value={info && info.identifiers.length > 0 ? info.identifiers.join(', ') : placeholder}
/>
<Row label={_('Last Synced')} value={lastSyncedLabel} />
</div>
)}
</Dialog>
);
};
export default SyncInfoDialog;