Refactor ScribeChapterComponent and offline handlers for seamless local and server operations
- Add stricter typings (`RefObject`) and improve type safety. - Introduce conditional logic for `localBook` to
This commit is contained in:
16
app/page.tsx
16
app/page.tsx
@@ -205,13 +205,11 @@ function ScribeContent() {
|
|||||||
let serverBooksResponse: SyncedBook[] = [];
|
let serverBooksResponse: SyncedBook[] = [];
|
||||||
|
|
||||||
if (!isCurrentlyOffline()){
|
if (!isCurrentlyOffline()){
|
||||||
// Mode online: récupérer les livres du serveur ET de la DB locale
|
|
||||||
if (offlineMode.isDatabaseInitialized) {
|
if (offlineMode.isDatabaseInitialized) {
|
||||||
localBooksResponse = await window.electron.invoke<SyncedBook[]>('db:books:synced');
|
localBooksResponse = await window.electron.invoke<SyncedBook[]>('db:books:synced');
|
||||||
}
|
}
|
||||||
serverBooksResponse = await System.authGetQueryToServer<SyncedBook[]>('books/synced', session.accessToken, locale);
|
serverBooksResponse = await System.authGetQueryToServer<SyncedBook[]>('books/synced', session.accessToken, locale);
|
||||||
} else {
|
} else {
|
||||||
// Mode offline: récupérer uniquement depuis la DB locale
|
|
||||||
if (offlineMode.isDatabaseInitialized) {
|
if (offlineMode.isDatabaseInitialized) {
|
||||||
localBooksResponse = await window.electron.invoke<SyncedBook[]>('db:books:synced');
|
localBooksResponse = await window.electron.invoke<SyncedBook[]>('db:books:synced');
|
||||||
}
|
}
|
||||||
@@ -364,13 +362,6 @@ function ScribeContent() {
|
|||||||
console.error('[Page] Error initializing user:', error);
|
console.error('[Page] Error initializing user:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setSession({
|
|
||||||
isConnected: true,
|
|
||||||
user: user,
|
|
||||||
accessToken: token,
|
|
||||||
});
|
|
||||||
setCurrentCredits(user.creditsBalance)
|
|
||||||
setAmountSpent(user.aiUsage)
|
|
||||||
if (window.electron && user.id) {
|
if (window.electron && user.id) {
|
||||||
try {
|
try {
|
||||||
const dbInitialized:boolean = await initializeDatabase(user.id);
|
const dbInitialized:boolean = await initializeDatabase(user.id);
|
||||||
@@ -392,6 +383,13 @@ function ScribeContent() {
|
|||||||
console.error('Failed to initialize database:', error);
|
console.error('Failed to initialize database:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setSession({
|
||||||
|
isConnected: true,
|
||||||
|
user: user,
|
||||||
|
accessToken: token,
|
||||||
|
});
|
||||||
|
setCurrentCredits(user.creditsBalance)
|
||||||
|
setAmountSpent(user.aiUsage)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (window.electron) {
|
if (window.electron) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -87,9 +87,8 @@ export default function BookList() {
|
|||||||
}
|
}
|
||||||
}, [groupedBooks]);
|
}, [groupedBooks]);
|
||||||
|
|
||||||
// Charger les livres quand les conditions sont remplies
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
const shouldFetchBooks =
|
const shouldFetchBooks:boolean|"" =
|
||||||
(session.isConnected || accessToken) &&
|
(session.isConnected || accessToken) &&
|
||||||
(!isCurrentlyOffline() || offlineMode.isDatabaseInitialized);
|
(!isCurrentlyOffline() || offlineMode.isDatabaseInitialized);
|
||||||
|
|
||||||
@@ -119,7 +118,6 @@ export default function BookList() {
|
|||||||
setBookGuide(false);
|
setBookGuide(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Mode offline: stocker dans localStorage
|
|
||||||
const completedGuides = JSON.parse(localStorage.getItem('completedGuides') || '[]');
|
const completedGuides = JSON.parse(localStorage.getItem('completedGuides') || '[]');
|
||||||
if (!completedGuides.includes('new-first-book')) {
|
if (!completedGuides.includes('new-first-book')) {
|
||||||
completedGuides.push('new-first-book');
|
completedGuides.push('new-first-book');
|
||||||
@@ -229,6 +227,7 @@ export default function BookList() {
|
|||||||
|
|
||||||
async function getBook(bookId: string): Promise<void> {
|
async function getBook(bookId: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
let localBookOnly: boolean = false;
|
||||||
let bookResponse: BookListProps|null = null;
|
let bookResponse: BookListProps|null = null;
|
||||||
if (isCurrentlyOffline()){
|
if (isCurrentlyOffline()){
|
||||||
if (!offlineMode.isDatabaseInitialized) {
|
if (!offlineMode.isDatabaseInitialized) {
|
||||||
@@ -236,10 +235,18 @@ export default function BookList() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bookResponse = await window.electron.invoke('db:book:bookBasicInformation', bookId)
|
bookResponse = await window.electron.invoke('db:book:bookBasicInformation', bookId)
|
||||||
|
if (bookResponse) {
|
||||||
|
localBookOnly = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
bookResponse = await System.authGetQueryToServer<BookListProps>(`book/basic-information`, accessToken, lang, {
|
const isOfflineBook: SyncedBook | undefined = localOnlyBooks.find((book: SyncedBook):boolean => book.id === bookId);
|
||||||
id: bookId
|
if (isOfflineBook) {
|
||||||
});
|
bookResponse = await window.electron.invoke('db:book:bookBasicInformation', bookId)
|
||||||
|
localBookOnly = true;
|
||||||
|
}
|
||||||
|
if (!bookResponse) {
|
||||||
|
bookResponse = await System.authGetQueryToServer<BookListProps>(`book/basic-information`, accessToken, lang, {id: bookId});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!bookResponse) {
|
if (!bookResponse) {
|
||||||
errorMessage(t("bookList.errorBookDetails"));
|
errorMessage(t("bookList.errorBookDetails"));
|
||||||
@@ -256,6 +263,7 @@ export default function BookList() {
|
|||||||
publicationDate: bookResponse?.desiredReleaseDate || '',
|
publicationDate: bookResponse?.desiredReleaseDate || '',
|
||||||
desiredWordCount: bookResponse?.desiredWordCount || 0,
|
desiredWordCount: bookResponse?.desiredWordCount || 0,
|
||||||
totalWordCount: 0,
|
totalWordCount: 0,
|
||||||
|
localBook: localBookOnly,
|
||||||
coverImage: bookResponse?.coverImage ? 'data:image/jpeg;base64,' + bookResponse.coverImage : '',
|
coverImage: bookResponse?.coverImage ? 'data:image/jpeg;base64,' + bookResponse.coverImage : '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {ChapterListProps, ChapterProps} from "@/lib/models/Chapter";
|
import {ChapterListProps, ChapterProps} from "@/lib/models/Chapter";
|
||||||
import {useContext, useEffect, useRef, useState} from "react";
|
import {RefObject, useContext, useEffect, useRef, useState} from "react";
|
||||||
import System from "@/lib/models/System";
|
import System from "@/lib/models/System";
|
||||||
import {BookContext} from "@/context/BookContext";
|
import {BookContext} from "@/context/BookContext";
|
||||||
import {AlertContext} from "@/context/AlertContext";
|
import {AlertContext} from "@/context/AlertContext";
|
||||||
@@ -32,8 +32,8 @@ export default function ScribeChapterComponent() {
|
|||||||
const [deleteConfirmationMessage, setDeleteConfirmationMessage] = useState<boolean>(false);
|
const [deleteConfirmationMessage, setDeleteConfirmationMessage] = useState<boolean>(false);
|
||||||
const [removeChapterId, setRemoveChapterId] = useState<string>('');
|
const [removeChapterId, setRemoveChapterId] = useState<string>('');
|
||||||
|
|
||||||
const chapterRefs = useRef<Map<string, HTMLDivElement>>(new Map());
|
const chapterRefs: RefObject<Map<string, HTMLDivElement>> = useRef<Map<string, HTMLDivElement>>(new Map());
|
||||||
const scrollContainerRef = useRef<HTMLUListElement>(null);
|
const scrollContainerRef: RefObject<HTMLUListElement | null> = useRef<HTMLUListElement>(null);
|
||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
if (book)
|
if (book)
|
||||||
@@ -46,9 +46,9 @@ export default function ScribeChapterComponent() {
|
|||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
if (chapter?.chapterId && scrollContainerRef.current) {
|
if (chapter?.chapterId && scrollContainerRef.current) {
|
||||||
setTimeout(() => {
|
setTimeout(():void => {
|
||||||
const element = chapterRefs.current.get(chapter.chapterId);
|
const element: HTMLDivElement | undefined = chapterRefs.current.get(chapter.chapterId);
|
||||||
const container = scrollContainerRef.current;
|
const container: HTMLUListElement | null = scrollContainerRef.current;
|
||||||
|
|
||||||
if (element && container) {
|
if (element && container) {
|
||||||
const containerRect:DOMRect = container.getBoundingClientRect();
|
const containerRect:DOMRect = container.getBoundingClientRect();
|
||||||
@@ -76,9 +76,13 @@ export default function ScribeChapterComponent() {
|
|||||||
let response: ChapterListProps[]|null;
|
let response: ChapterListProps[]|null;
|
||||||
if (isCurrentlyOffline()){
|
if (isCurrentlyOffline()){
|
||||||
response = await window.electron.invoke<ChapterListProps[]>('db:book:chapters', book?.bookId)
|
response = await window.electron.invoke<ChapterListProps[]>('db:book:chapters', book?.bookId)
|
||||||
|
} else {
|
||||||
|
if (book?.localBook){
|
||||||
|
response = await window.electron.invoke<ChapterListProps[]>('db:book:chapters', book?.bookId)
|
||||||
} else {
|
} else {
|
||||||
response = await System.authGetQueryToServer<ChapterListProps[]>(`book/chapters?id=${book?.bookId}`, userToken, lang);
|
response = await System.authGetQueryToServer<ChapterListProps[]>(`book/chapters?id=${book?.bookId}`, userToken, lang);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (response) {
|
if (response) {
|
||||||
setChapters(response);
|
setChapters(response);
|
||||||
}
|
}
|
||||||
@@ -94,13 +98,20 @@ export default function ScribeChapterComponent() {
|
|||||||
async function getChapter(chapterId: string): Promise<void> {
|
async function getChapter(chapterId: string): Promise<void> {
|
||||||
const version: number = chapter?.chapterContent.version ? chapter?.chapterContent.version : 2;
|
const version: number = chapter?.chapterContent.version ? chapter?.chapterContent.version : 2;
|
||||||
try {
|
try {
|
||||||
let response: ChapterProps | null = null
|
let response: ChapterProps | null
|
||||||
if (isCurrentlyOffline()) {
|
if (isCurrentlyOffline()) {
|
||||||
response = await window.electron.invoke<ChapterProps>('db:chapter:whole', {
|
response = await window.electron.invoke<ChapterProps>('db:chapter:whole', {
|
||||||
bookid: book?.bookId,
|
bookid: book?.bookId,
|
||||||
id: chapterId,
|
id: chapterId,
|
||||||
version: version,
|
version: version,
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
if (book?.localBook){
|
||||||
|
response = await window.electron.invoke<ChapterProps>('db:chapter:whole', {
|
||||||
|
bookid: book?.bookId,
|
||||||
|
id: chapterId,
|
||||||
|
version: version,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
response = await System.authGetQueryToServer<ChapterProps>(`chapter/whole`, userToken, lang, {
|
response = await System.authGetQueryToServer<ChapterProps>(`chapter/whole`, userToken, lang, {
|
||||||
bookid: book?.bookId,
|
bookid: book?.bookId,
|
||||||
@@ -108,6 +119,7 @@ export default function ScribeChapterComponent() {
|
|||||||
version: version,
|
version: version,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!response) {
|
if (!response) {
|
||||||
errorMessage(t("scribeChapterComponent.errorFetchChapter"));
|
errorMessage(t("scribeChapterComponent.errorFetchChapter"));
|
||||||
return;
|
return;
|
||||||
@@ -131,6 +143,13 @@ export default function ScribeChapterComponent() {
|
|||||||
chapterOrder: chapterOrder,
|
chapterOrder: chapterOrder,
|
||||||
title: title,
|
title: title,
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
if (book?.localBook){
|
||||||
|
response = await window.electron.invoke<boolean>('db:chapter:update',{
|
||||||
|
chapterId: chapterId,
|
||||||
|
chapterOrder: chapterOrder,
|
||||||
|
title: title,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
response = await System.authPostToServer<boolean>('chapter/update', {
|
response = await System.authPostToServer<boolean>('chapter/update', {
|
||||||
chapterId: chapterId,
|
chapterId: chapterId,
|
||||||
@@ -138,6 +157,7 @@ export default function ScribeChapterComponent() {
|
|||||||
title: title,
|
title: title,
|
||||||
}, userToken, lang);
|
}, userToken, lang);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!response) {
|
if (!response) {
|
||||||
errorMessage(t("scribeChapterComponent.errorChapterUpdate"));
|
errorMessage(t("scribeChapterComponent.errorChapterUpdate"));
|
||||||
return;
|
return;
|
||||||
@@ -172,11 +192,15 @@ export default function ScribeChapterComponent() {
|
|||||||
let response:boolean = false;
|
let response:boolean = false;
|
||||||
if (isCurrentlyOffline()) {
|
if (isCurrentlyOffline()) {
|
||||||
response = await window.electron.invoke<boolean>('db:chapter:remove', removeChapterId)
|
response = await window.electron.invoke<boolean>('db:chapter:remove', removeChapterId)
|
||||||
|
} else {
|
||||||
|
if (book?.localBook){
|
||||||
|
response = await window.electron.invoke<boolean>('db:chapter:remove', removeChapterId)
|
||||||
} else {
|
} else {
|
||||||
response = await System.authDeleteToServer<boolean>('chapter/remove', {
|
response = await System.authDeleteToServer<boolean>('chapter/remove', {
|
||||||
chapterId: removeChapterId,
|
chapterId: removeChapterId,
|
||||||
}, userToken, lang);
|
}, userToken, lang);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!response) {
|
if (!response) {
|
||||||
errorMessage(t("scribeChapterComponent.errorChapterDelete"));
|
errorMessage(t("scribeChapterComponent.errorChapterDelete"));
|
||||||
return;
|
return;
|
||||||
@@ -208,6 +232,13 @@ export default function ScribeChapterComponent() {
|
|||||||
chapterOrder: chapterOrder,
|
chapterOrder: chapterOrder,
|
||||||
title: chapterTitle
|
title: chapterTitle
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
if (book?.localBook){
|
||||||
|
chapterId = await window.electron.invoke<string>('db:chapter:add', {
|
||||||
|
bookId: book?.bookId,
|
||||||
|
chapterOrder: chapterOrder,
|
||||||
|
title: chapterTitle
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
chapterId = await System.authPostToServer<string>('chapter/add', {
|
chapterId = await System.authPostToServer<string>('chapter/add', {
|
||||||
bookId: book?.bookId,
|
bookId: book?.bookId,
|
||||||
@@ -215,6 +246,7 @@ export default function ScribeChapterComponent() {
|
|||||||
title: chapterTitle
|
title: chapterTitle
|
||||||
}, userToken, lang);
|
}, userToken, lang);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!chapterId) {
|
if (!chapterId) {
|
||||||
errorMessage(t("scribeChapterComponent.errorChapterSubmit", {chapterName: newChapterName}));
|
errorMessage(t("scribeChapterComponent.errorChapterSubmit", {chapterName: newChapterName}));
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export interface BookProps {
|
|||||||
desiredWordCount?: number;
|
desiredWordCount?: number;
|
||||||
totalWordCount?: number;
|
totalWordCount?: number;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
|
localBook?: boolean;
|
||||||
chapters?: ChapterProps[];
|
chapters?: ChapterProps[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user