Add terms of use translations, sync detection, and refactor book components
- Introduced new translations for terms of use in French and English locales. - Added sync status detection logic for books in `BookList` and `BookCard` components. - Refactored `BookCard` to handle additional props and improve layout flexibility. - Enhanced `TermsOfUse` component with complete localization support and refuse functionality. - Updated data decryption logic in Rust services to handle optional fields and additional metadata consistently. - Improved offline/online synchronization workflows with extended context properties.
This commit is contained in:
@@ -17,7 +17,8 @@ import GuideTour, {GuideStep} from "@/components/GuideTour";
|
||||
import {guideTourDone, setNewGuideTour} from "@/lib/utils/user";
|
||||
import {useTranslations} from '@/lib/i18n';
|
||||
import {LangContext, LangContextProps} from "@/context/LangContext";
|
||||
import {BooksSyncContext, BooksSyncContextProps} from "@/context/BooksSyncContext";
|
||||
import {BooksSyncContext, BooksSyncContextProps, SyncType} from "@/context/BooksSyncContext";
|
||||
import {BookSyncCompare, SyncedBook} from "@/lib/types/synced-book";
|
||||
import {SeriesListItemProps} from "@/lib/types/series";
|
||||
import SeriesCard, {SeriesCardProps} from "@/components/series/SeriesCard";
|
||||
import SeriesSetting from "@/components/series/SeriesSetting";
|
||||
@@ -35,8 +36,11 @@ export default function BookList() {
|
||||
const router = useRouter();
|
||||
const t = useTranslations();
|
||||
const {lang}: LangContextProps = useContext<LangContextProps>(LangContext)
|
||||
const {serverSyncedBooks}: BooksSyncContextProps = useContext<BooksSyncContextProps>(BooksSyncContext)
|
||||
const {isCurrentlyOffline}: OfflineContextType = useContext<OfflineContextType>(OfflineContext);
|
||||
const {
|
||||
serverSyncedBooks, booksToSyncFromServer, booksToSyncToServer,
|
||||
serverOnlyBooks, localOnlyBooks
|
||||
}: BooksSyncContextProps = useContext<BooksSyncContextProps>(BooksSyncContext);
|
||||
const {isCurrentlyOffline, offlineMode}: OfflineContextType = useContext<OfflineContextType>(OfflineContext);
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||
const [groupedItems, setGroupedItems] = useState<Record<string, CategoryItem[]>>({});
|
||||
@@ -95,27 +99,38 @@ export default function BookList() {
|
||||
]
|
||||
|
||||
useEffect((): void => {
|
||||
if (groupedItems && Object.keys(groupedItems).length > 0 && guideTourDone(session.user?.guideTour || [], 'new-first-book')) {
|
||||
setBookGuide(true);
|
||||
if (groupedItems && Object.keys(groupedItems).length > 0) {
|
||||
const notDone: boolean = isCurrentlyOffline()
|
||||
? localStorage.getItem('guide-tour-new-first-book') !== 'true'
|
||||
: guideTourDone(session.user?.guideTour || [], 'new-first-book');
|
||||
if (notDone) setBookGuide(true);
|
||||
}
|
||||
}, [groupedItems]);
|
||||
|
||||
useEffect((): void => {
|
||||
loadBooksAndSeries().then()
|
||||
}, [serverSyncedBooks]);
|
||||
const canLoad: boolean = !isDesktop ||
|
||||
(!isCurrentlyOffline() || offlineMode.isDatabaseInitialized);
|
||||
if (canLoad) loadBooksAndSeries().then();
|
||||
}, [serverSyncedBooks, offlineMode.isDatabaseInitialized, booksToSyncFromServer, booksToSyncToServer, serverOnlyBooks, localOnlyBooks]);
|
||||
|
||||
useEffect((): void => {
|
||||
if (accessToken) loadBooksAndSeries().then();
|
||||
}, [accessToken]);
|
||||
|
||||
async function handleFirstBookGuide(): Promise<void> {
|
||||
if (isCurrentlyOffline()) {
|
||||
localStorage.setItem('guide-tour-new-first-book', 'true');
|
||||
setBookGuide(false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response: boolean = await apiPost<boolean>(
|
||||
'logs/tour',
|
||||
{plateforme: 'web', tour: 'new-first-book'},
|
||||
{plateforme: 'desktop', tour: 'new-first-book'},
|
||||
session.accessToken, lang
|
||||
);
|
||||
if (response) {
|
||||
localStorage.setItem('guide-tour-new-first-book', 'true');
|
||||
setSession(setNewGuideTour(session, 'new-first-book'));
|
||||
setBookGuide(false);
|
||||
}
|
||||
@@ -291,6 +306,23 @@ export default function BookList() {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function detectBookSyncStatus(bookId: string): SyncType {
|
||||
if (!isDesktop || isCurrentlyOffline()) return 'synced';
|
||||
if (serverOnlyBooks.find((book: SyncedBook): boolean => book.id === bookId)) {
|
||||
return 'server-only';
|
||||
}
|
||||
if (localOnlyBooks.find((book: SyncedBook): boolean => book.id === bookId)) {
|
||||
return 'local-only';
|
||||
}
|
||||
if (booksToSyncFromServer.find((book: BookSyncCompare): boolean => book.id === bookId)) {
|
||||
return 'to-sync-from-server';
|
||||
}
|
||||
if (booksToSyncToServer.find((book: BookSyncCompare): boolean => book.id === bookId)) {
|
||||
return 'to-sync-to-server';
|
||||
}
|
||||
return 'synced';
|
||||
}
|
||||
|
||||
function handleBookClick(bookId: string): void {
|
||||
router.push(`/book/${bookId}`);
|
||||
}
|
||||
@@ -388,11 +420,12 @@ export default function BookList() {
|
||||
return (
|
||||
<div key={item.book.bookId}
|
||||
{...(idx === 0 && {'data-guide': 'book-card'})}
|
||||
className={`flex-shrink-0 w-64 sm:w-52 md:w-48 lg:w-56 xl:w-64 p-2 box-border ${guideTourDone(session.user?.guideTour || [], 'new-first-book') && 'mb-[200px]'}`}>
|
||||
className={`flex-shrink-0 w-64 sm:w-52 md:w-48 lg:w-56 xl:w-64 p-2 box-border ${bookGuide && 'mb-[200px]'}`}>
|
||||
<BookCard
|
||||
book={item.book}
|
||||
onClickCallback={handleBookClick}
|
||||
index={idx}
|
||||
syncStatus={detectBookSyncStatus(item.book.bookId)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user