Remove CharacterComponent and CharacterDetail components
- Deleted `CharacterComponent` and `CharacterDetail` files from the project. - Refactored related logic to improve code maintainability and reduce redundancy.
This commit is contained in:
@@ -251,10 +251,6 @@ body {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.setting-container {
|
||||
height: calc(100vh - 10rem);
|
||||
}
|
||||
|
||||
.composer-panel-h {
|
||||
height: calc(100vh - 8rem);
|
||||
}
|
||||
|
||||
172
app/page.tsx
172
app/page.tsx
@@ -34,10 +34,32 @@ import OfflineContext, {OfflineMode} from "@/context/OfflineContext";
|
||||
import OfflinePinSetup from "@/components/offline/OfflinePinSetup";
|
||||
import OfflinePinVerify from "@/components/offline/OfflinePinVerify";
|
||||
import {SyncedBook, BookSyncCompare, compareBookSyncs} from "@/lib/models/SyncedBook";
|
||||
import {SyncedSeries, SeriesSyncCompare, compareSeriesSyncs} from "@/lib/models/SyncedSeries";
|
||||
import {BooksSyncContext} from "@/context/BooksSyncContext";
|
||||
import {SeriesSyncContext} from "@/context/SeriesSyncContext";
|
||||
import useSyncBooks from "@/hooks/useSyncBooks";
|
||||
import useSyncSeries from "@/hooks/useSyncSeries";
|
||||
import {LocalSyncQueueContext, LocalSyncOperation} from "@/context/SyncQueueContext";
|
||||
|
||||
interface RemovedItemRecord {
|
||||
removal_id: string;
|
||||
table_name: string;
|
||||
entity_id: string;
|
||||
book_id: string | null;
|
||||
user_id: string;
|
||||
deleted_at: number;
|
||||
}
|
||||
|
||||
interface SyncedBooksResponse {
|
||||
books: SyncedBook[];
|
||||
tombstones: RemovedItemRecord[];
|
||||
}
|
||||
|
||||
interface SyncedSeriesResponse {
|
||||
series: SyncedSeries[];
|
||||
tombstones: RemovedItemRecord[];
|
||||
}
|
||||
|
||||
const messagesMap = {
|
||||
fr: frMessages,
|
||||
en: enMessages
|
||||
@@ -45,22 +67,59 @@ const messagesMap = {
|
||||
|
||||
function AutoSyncOnReconnect() {
|
||||
const {offlineMode} = useContext(OfflineContext);
|
||||
const {syncAllToServer, refreshBooks, booksToSyncToServer} = useSyncBooks();
|
||||
const {syncAllToServer: syncAllBooksToServer, refreshBooks, booksToSyncToServer} = useSyncBooks();
|
||||
const {syncAllToServer: syncAllSeriesToServer, refreshSeries, seriesToSyncToServer} = useSyncSeries();
|
||||
const [pendingSync, setPendingSync] = useState<boolean>(false);
|
||||
const [isRefreshing, setIsRefreshing] = useState<boolean>(false);
|
||||
|
||||
const saveLastOnlineTimestamp = (): void => {
|
||||
const timestamp: number = Math.floor(Date.now() / 1000);
|
||||
localStorage.setItem('lastOnlineTimestamp', timestamp.toString());
|
||||
};
|
||||
|
||||
useEffect((): void => {
|
||||
if (!offlineMode.isOffline) {
|
||||
setPendingSync(true);
|
||||
refreshBooks();
|
||||
setIsRefreshing(true);
|
||||
Promise.all([refreshBooks(), refreshSeries()]).then(() => {
|
||||
setIsRefreshing(false);
|
||||
});
|
||||
}
|
||||
}, [offlineMode.isOffline]);
|
||||
|
||||
useEffect((): void => {
|
||||
if (pendingSync && booksToSyncToServer.length > 0) {
|
||||
syncAllToServer();
|
||||
if (pendingSync && !isRefreshing) {
|
||||
const syncPromises: Promise<void>[] = [];
|
||||
|
||||
if (booksToSyncToServer.length > 0) {
|
||||
syncPromises.push(syncAllBooksToServer());
|
||||
}
|
||||
if (seriesToSyncToServer.length > 0) {
|
||||
syncPromises.push(syncAllSeriesToServer());
|
||||
}
|
||||
|
||||
if (syncPromises.length > 0) {
|
||||
Promise.all(syncPromises).then(() => {
|
||||
saveLastOnlineTimestamp();
|
||||
});
|
||||
} else {
|
||||
saveLastOnlineTimestamp();
|
||||
}
|
||||
|
||||
setPendingSync(false);
|
||||
}
|
||||
}, [booksToSyncToServer, pendingSync]);
|
||||
}, [booksToSyncToServer, seriesToSyncToServer, pendingSync, isRefreshing]);
|
||||
|
||||
// Update lastOnlineTimestamp every 5 minutes while online
|
||||
useEffect((): (() => void) | void => {
|
||||
if (!offlineMode.isOffline) {
|
||||
const intervalId: NodeJS.Timeout = setInterval((): void => {
|
||||
saveLastOnlineTimestamp();
|
||||
}, 5 * 60 * 1000); // 5 minutes
|
||||
|
||||
return (): void => clearInterval(intervalId);
|
||||
}
|
||||
}, [offlineMode.isOffline]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -94,6 +153,13 @@ function ScribeContent() {
|
||||
const [serverOnlyBooks, setServerOnlyBooks] = useState<SyncedBook[]>([]);
|
||||
const [localOnlyBooks, setLocalOnlyBooks] = useState<SyncedBook[]>([]);
|
||||
|
||||
const [serverSyncedSeries, setServerSyncedSeries] = useState<SyncedSeries[]>([]);
|
||||
const [localSyncedSeries, setLocalSyncedSeries] = useState<SyncedSeries[]>([]);
|
||||
const [seriesSyncDiffsFromServer, setSeriesSyncDiffsFromServer] = useState<SeriesSyncCompare[]>([]);
|
||||
const [seriesSyncDiffsToServer, setSeriesSyncDiffsToServer] = useState<SeriesSyncCompare[]>([]);
|
||||
const [serverOnlySeries, setServerOnlySeries] = useState<SyncedSeries[]>([]);
|
||||
const [localOnlySeries, setLocalOnlySeries] = useState<SyncedSeries[]>([]);
|
||||
|
||||
const [currentCredits, setCurrentCredits] = useState<number>(160);
|
||||
const [amountSpent, setAmountSpent] = useState<number>(session.user?.aiUsage || 0);
|
||||
|
||||
@@ -218,7 +284,8 @@ function ScribeContent() {
|
||||
|
||||
useEffect((): void => {
|
||||
if (session.isConnected) {
|
||||
getBooks().then()
|
||||
refreshBooks().then()
|
||||
refreshSeries().then()
|
||||
setIsTermsAccepted(session.user?.termsAccepted ?? false);
|
||||
setHomeStepsGuide(User.guideTourDone(session.user?.guideTour ?? [], 'home-basic'));
|
||||
setIsLoading(false);
|
||||
@@ -230,7 +297,7 @@ function ScribeContent() {
|
||||
if (currentBook) {
|
||||
getLastChapter().then();
|
||||
} else {
|
||||
getBooks().then();
|
||||
refreshBooks().then();
|
||||
}
|
||||
}
|
||||
}, [currentBook]);
|
||||
@@ -269,17 +336,58 @@ function ScribeContent() {
|
||||
setLocalOnlyBooks(localSyncedBooks.filter((localBook: SyncedBook):boolean => !serverSyncedBooks.find((serverBook: SyncedBook):boolean => serverBook.id === localBook.id)))
|
||||
}, [localSyncedBooks, serverSyncedBooks]);
|
||||
|
||||
|
||||
async function getBooks(): Promise<void> {
|
||||
useEffect((): void => {
|
||||
const diffsFromServer: SeriesSyncCompare[] = [];
|
||||
const diffsToServer: SeriesSyncCompare[] = [];
|
||||
|
||||
serverSyncedSeries.forEach((serverSeries: SyncedSeries): void => {
|
||||
const localSeries: SyncedSeries | undefined = localSyncedSeries.find((series: SyncedSeries): boolean => series.id === serverSeries.id);
|
||||
if (!localSeries) {
|
||||
return;
|
||||
}
|
||||
|
||||
const diff: SeriesSyncCompare | null = compareSeriesSyncs(serverSeries, localSeries);
|
||||
if (diff) {
|
||||
diffsFromServer.push(diff);
|
||||
}
|
||||
});
|
||||
|
||||
localSyncedSeries.forEach((localSeries: SyncedSeries): void => {
|
||||
const serverSeries: SyncedSeries | undefined = serverSyncedSeries.find((series: SyncedSeries): boolean => series.id === localSeries.id);
|
||||
if (!serverSeries) {
|
||||
return;
|
||||
}
|
||||
|
||||
const diff: SeriesSyncCompare | null = compareSeriesSyncs(localSeries, serverSeries);
|
||||
if (diff) {
|
||||
diffsToServer.push(diff);
|
||||
}
|
||||
});
|
||||
|
||||
setSeriesSyncDiffsFromServer(diffsFromServer);
|
||||
setSeriesSyncDiffsToServer(diffsToServer);
|
||||
setServerOnlySeries(serverSyncedSeries.filter((serverSeries: SyncedSeries): boolean => !localSyncedSeries.find((localSeries: SyncedSeries): boolean => localSeries.id === serverSeries.id)));
|
||||
setLocalOnlySeries(localSyncedSeries.filter((localSeries: SyncedSeries): boolean => !serverSyncedSeries.find((serverSeries: SyncedSeries): boolean => serverSeries.id === localSeries.id)));
|
||||
}, [localSyncedSeries, serverSyncedSeries]);
|
||||
|
||||
async function refreshBooks(): Promise<void> {
|
||||
try {
|
||||
let localBooksResponse: SyncedBook[] = [];
|
||||
let serverBooksResponse: SyncedBook[] = [];
|
||||
|
||||
if (!isCurrentlyOffline()){
|
||||
if (!isCurrentlyOffline()) {
|
||||
if (offlineMode.isDatabaseInitialized) {
|
||||
localBooksResponse = await window.electron.invoke<SyncedBook[]>('db:books:synced');
|
||||
const lastOnlineStr: string | null = localStorage.getItem('lastOnlineTimestamp');
|
||||
const lastOnlineTimestamp: number = lastOnlineStr ? parseInt(lastOnlineStr, 10) : 0;
|
||||
const localTombstones: RemovedItemRecord[] = await window.electron.invoke<RemovedItemRecord[]>('db:tombstones:since', lastOnlineTimestamp);
|
||||
const serverResponse: SyncedBooksResponse = await System.authPostToServer<SyncedBooksResponse>('books/synced', { lastOnlineTimestamp, tombstones: localTombstones }, session.accessToken, locale);
|
||||
serverBooksResponse = serverResponse.books;
|
||||
await window.electron.invoke<void>('db:tombstones:apply:books', serverResponse.tombstones);
|
||||
} else {
|
||||
const serverResponse: SyncedBooksResponse = await System.authPostToServer<SyncedBooksResponse>('books/synced', { lastOnlineTimestamp: 0, tombstones: [] }, session.accessToken, locale);
|
||||
serverBooksResponse = serverResponse.books;
|
||||
}
|
||||
serverBooksResponse = await System.authGetQueryToServer<SyncedBook[]>('books/synced', session.accessToken, locale);
|
||||
} else {
|
||||
if (offlineMode.isDatabaseInitialized) {
|
||||
localBooksResponse = await window.electron.invoke<SyncedBook[]>('db:books:synced');
|
||||
@@ -296,7 +404,43 @@ function ScribeContent() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function refreshSeries(): Promise<void> {
|
||||
try {
|
||||
let localSeriesResponse: SyncedSeries[] = [];
|
||||
let serverSeriesResponse: SyncedSeries[] = [];
|
||||
|
||||
if (!isCurrentlyOffline()) {
|
||||
if (offlineMode.isDatabaseInitialized) {
|
||||
localSeriesResponse = await window.electron.invoke<SyncedSeries[]>('db:series:synced');
|
||||
const lastOnlineStr: string | null = localStorage.getItem('lastOnlineTimestamp');
|
||||
const lastOnlineTimestamp: number = lastOnlineStr ? parseInt(lastOnlineStr, 10) : 0;
|
||||
const localTombstones: RemovedItemRecord[] = await window.electron.invoke<RemovedItemRecord[]>('db:tombstones:since', lastOnlineTimestamp);
|
||||
const serverResponse: SyncedSeriesResponse = await System.authPostToServer<SyncedSeriesResponse>('series/synced', { lastOnlineTimestamp, tombstones: localTombstones }, session.accessToken, locale);
|
||||
serverSeriesResponse = serverResponse.series;
|
||||
await window.electron.invoke<void>('db:tombstones:apply:series', serverResponse.tombstones);
|
||||
} else {
|
||||
const serverResponse: SyncedSeriesResponse = await System.authPostToServer<SyncedSeriesResponse>('series/synced', { lastOnlineTimestamp: 0, tombstones: [] }, session.accessToken, locale);
|
||||
serverSeriesResponse = serverResponse.series;
|
||||
}
|
||||
} else {
|
||||
if (offlineMode.isDatabaseInitialized) {
|
||||
localSeriesResponse = await window.electron.invoke<SyncedSeries[]>('db:series:synced');
|
||||
}
|
||||
}
|
||||
|
||||
setServerSyncedSeries(serverSeriesResponse);
|
||||
setLocalSyncedSeries(localSeriesResponse);
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) {
|
||||
errorMessage(e.message);
|
||||
} else {
|
||||
errorMessage(t("homePage.errors.fetchSeriesError"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
useEffect(():void => {
|
||||
async function checkPinSetup() {
|
||||
if (session.isConnected && window.electron) {
|
||||
@@ -601,7 +745,8 @@ function ScribeContent() {
|
||||
addToQueue: addToLocalSyncQueue,
|
||||
isProcessing: isQueueProcessing,
|
||||
}}>
|
||||
<BooksSyncContext.Provider value={{serverSyncedBooks, localSyncedBooks, booksToSyncFromServer:bookSyncDiffsFromServer, booksToSyncToServer:bookSyncDiffsToServer, setServerSyncedBooks, setLocalSyncedBooks, setServerOnlyBooks, setLocalOnlyBooks, serverOnlyBooks, localOnlyBooks}}>
|
||||
<BooksSyncContext.Provider value={{serverSyncedBooks, localSyncedBooks, booksToSyncFromServer:bookSyncDiffsFromServer, booksToSyncToServer:bookSyncDiffsToServer, setServerSyncedBooks, setLocalSyncedBooks, setServerOnlyBooks, setLocalOnlyBooks, setBooksToSyncFromServer:setBookSyncDiffsFromServer, setBooksToSyncToServer:setBookSyncDiffsToServer, serverOnlyBooks, localOnlyBooks}}>
|
||||
<SeriesSyncContext.Provider value={{serverSyncedSeries, localSyncedSeries, seriesToSyncFromServer:seriesSyncDiffsFromServer, seriesToSyncToServer:seriesSyncDiffsToServer, setServerSyncedSeries, setLocalSyncedSeries, setServerOnlySeries, setLocalOnlySeries, setSeriesToSyncFromServer:setSeriesSyncDiffsFromServer, setSeriesToSyncToServer:setSeriesSyncDiffsToServer, serverOnlySeries, localOnlySeries}}>
|
||||
<AutoSyncOnReconnect/>
|
||||
<BookContext.Provider value={{book: currentBook, setBook: setCurrentBook}}>
|
||||
<ChapterContext.Provider value={{chapter: currentChapter, setChapter: setCurrentChapter}}>
|
||||
@@ -650,6 +795,7 @@ function ScribeContent() {
|
||||
</AIUsageContext.Provider>
|
||||
</ChapterContext.Provider>
|
||||
</BookContext.Provider>
|
||||
</SeriesSyncContext.Provider>
|
||||
</BooksSyncContext.Provider>
|
||||
</LocalSyncQueueContext.Provider>
|
||||
</SessionContext.Provider>
|
||||
|
||||
Reference in New Issue
Block a user