- Introduced foundational UI components (`Badge`, `LockCard`, `SectionHeader`, `AvatarIcon`, etc.) for flexible layouts and consistent design. - Added migration support with the `MigrationModal` component and backend integration for exporting/importing data between Electron and Tauri. - Extended form components with `TextAreaInput`, `OrderInput`, `ToggleField`, and `ToolbarSelect` for improved input handling. - Updated `ScribeShell` with migration popup logic to prompt users for data migration. - Integrated `AlertStack` for better alert handling and notification management. - Enhanced Rust/Tauri services with migration command implementations. - Added translations and styles for new components.
208 lines
9.3 KiB
TypeScript
208 lines
9.3 KiB
TypeScript
'use client'
|
|
import React, {ChangeEvent, forwardRef, useContext, useEffect, useImperativeHandle, useState} from 'react';
|
|
import {apiGet, apiPost} from '@/lib/api/client';
|
|
import {isDesktop} from '@/lib/configs';
|
|
import * as tauri from '@/lib/tauri';
|
|
import OfflineContext, {OfflineContextType} from '@/context/OfflineContext';
|
|
import {AlertContext, AlertContextProps} from "@/context/AlertContext";
|
|
import {BookContext, BookContextProps} from '@/context/BookContext';
|
|
import {SessionContext, SessionContextProps} from "@/context/SessionContext";
|
|
import {GuideLine} from "@/lib/types/book";
|
|
import TextAreaInput from "@/components/form/TextAreaInput";
|
|
import InputField from "@/components/form/InputField";
|
|
import {useTranslations} from '@/lib/i18n';
|
|
import {LangContext, LangContextProps} from "@/context/LangContext";
|
|
import {SettingRef} from "@/lib/types/settings";
|
|
|
|
function GuideLineSetting(_props: object, ref: React.ForwardedRef<SettingRef>): React.JSX.Element {
|
|
const t = useTranslations();
|
|
const {lang}: LangContextProps = useContext<LangContextProps>(LangContext);
|
|
const {book}: BookContextProps = useContext<BookContextProps>(BookContext);
|
|
const {session}: SessionContextProps = useContext<SessionContextProps>(SessionContext);
|
|
const userToken: string = session?.accessToken ? session?.accessToken : '';
|
|
const {errorMessage, successMessage}: AlertContextProps = useContext<AlertContextProps>(AlertContext);
|
|
const {isCurrentlyOffline}: OfflineContextType = useContext<OfflineContextType>(OfflineContext);
|
|
const bookId: string = book?.bookId ?? '';
|
|
|
|
const [tone, setTone] = useState<string>('');
|
|
const [atmosphere, setAtmosphere] = useState<string>('');
|
|
const [writingStyle, setWritingStyle] = useState<string>('');
|
|
const [themes, setThemes] = useState<string>('');
|
|
const [symbolism, setSymbolism] = useState<string>('');
|
|
const [motifs, setMotifs] = useState<string>('');
|
|
const [narrativeVoice, setNarrativeVoice] = useState<string>('');
|
|
const [pacing, setPacing] = useState<string>('');
|
|
const [intendedAudience, setIntendedAudience] = useState<string>('');
|
|
const [keyMessages, setKeyMessages] = useState<string>('');
|
|
|
|
useEffect((): void => {
|
|
getGuideLine().then();
|
|
}, []);
|
|
|
|
useImperativeHandle(ref, (): SettingRef => ({
|
|
handleSave: savePersonal
|
|
}));
|
|
|
|
async function getGuideLine(): Promise<void> {
|
|
try {
|
|
let response: GuideLine;
|
|
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
|
|
response = await tauri.getGuideLine(bookId);
|
|
} else {
|
|
response = await apiGet<GuideLine>(
|
|
`book/guide-line`,
|
|
userToken,
|
|
lang,
|
|
{id: bookId},
|
|
);
|
|
}
|
|
if (response) {
|
|
setTone(response.tone);
|
|
setAtmosphere(response.atmosphere);
|
|
setWritingStyle(response.writingStyle);
|
|
setThemes(response.themes);
|
|
setSymbolism(response.symbolism);
|
|
setMotifs(response.motifs);
|
|
setNarrativeVoice(response.narrativeVoice);
|
|
setPacing(response.pacing);
|
|
setIntendedAudience(response.intendedAudience);
|
|
setKeyMessages(response.keyMessages);
|
|
}
|
|
} catch (error: unknown) {
|
|
if (error instanceof Error) {
|
|
errorMessage(error.message);
|
|
} else {
|
|
errorMessage(t("guideLineSetting.errorUnknown"));
|
|
}
|
|
}
|
|
}
|
|
|
|
async function savePersonal(): Promise<void> {
|
|
try {
|
|
let response: boolean;
|
|
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
|
|
response = await tauri.updateGuideLine({
|
|
bookId: bookId,
|
|
tone: tone,
|
|
atmosphere: atmosphere,
|
|
writingStyle: writingStyle,
|
|
themes: themes,
|
|
symbolism: symbolism,
|
|
motifs: motifs,
|
|
narrativeVoice: narrativeVoice,
|
|
pacing: pacing,
|
|
intendedAudience: intendedAudience,
|
|
keyMessages: keyMessages,
|
|
});
|
|
} else {
|
|
response = await apiPost<boolean>(
|
|
'book/guide-line',
|
|
{
|
|
bookId: bookId,
|
|
tone: tone,
|
|
atmosphere: atmosphere,
|
|
writingStyle: writingStyle,
|
|
themes: themes,
|
|
symbolism: symbolism,
|
|
motifs: motifs,
|
|
narrativeVoice: narrativeVoice,
|
|
pacing: pacing,
|
|
intendedAudience: intendedAudience,
|
|
keyMessages: keyMessages,
|
|
},
|
|
userToken,
|
|
lang,
|
|
);
|
|
}
|
|
if (!response) {
|
|
errorMessage(t("guideLineSetting.saveError"));
|
|
return;
|
|
}
|
|
successMessage(t("guideLineSetting.saveSuccess"));
|
|
} catch (error: unknown) {
|
|
if (error instanceof Error) {
|
|
errorMessage(error.message);
|
|
} else {
|
|
errorMessage(t("guideLineSetting.errorUnknown"));
|
|
}
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<InputField fieldName={t("guideLineSetting.tone")} input={
|
|
<TextAreaInput
|
|
value={tone}
|
|
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setTone(e.target.value)}
|
|
placeholder={t("guideLineSetting.tonePlaceholder")}
|
|
/>
|
|
}/>
|
|
<InputField fieldName={t("guideLineSetting.atmosphere")} input={
|
|
<TextAreaInput
|
|
value={atmosphere}
|
|
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setAtmosphere(e.target.value)}
|
|
placeholder={t("guideLineSetting.atmospherePlaceholder")}
|
|
/>
|
|
}/>
|
|
<InputField fieldName={t("guideLineSetting.writingStyle")} input={
|
|
<TextAreaInput
|
|
value={writingStyle}
|
|
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setWritingStyle(e.target.value)}
|
|
placeholder={t("guideLineSetting.writingStylePlaceholder")}
|
|
/>
|
|
}/>
|
|
<InputField fieldName={t("guideLineSetting.themes")} input={
|
|
<TextAreaInput
|
|
value={themes}
|
|
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setThemes(e.target.value)}
|
|
placeholder={t("guideLineSetting.themesPlaceholder")}
|
|
/>
|
|
}/>
|
|
<InputField fieldName={t("guideLineSetting.symbolism")} input={
|
|
<TextAreaInput
|
|
value={symbolism}
|
|
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setSymbolism(e.target.value)}
|
|
placeholder={t("guideLineSetting.symbolismPlaceholder")}
|
|
/>
|
|
}/>
|
|
<InputField fieldName={t("guideLineSetting.motifs")} input={
|
|
<TextAreaInput
|
|
value={motifs}
|
|
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setMotifs(e.target.value)}
|
|
placeholder={t("guideLineSetting.motifsPlaceholder")}
|
|
/>
|
|
}/>
|
|
<InputField fieldName={t("guideLineSetting.narrativeVoice")} input={
|
|
<TextAreaInput
|
|
value={narrativeVoice}
|
|
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setNarrativeVoice(e.target.value)}
|
|
placeholder={t("guideLineSetting.narrativeVoicePlaceholder")}
|
|
/>
|
|
}/>
|
|
<InputField fieldName={t("guideLineSetting.pacing")} input={
|
|
<TextAreaInput
|
|
value={pacing}
|
|
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setPacing(e.target.value)}
|
|
placeholder={t("guideLineSetting.pacingPlaceholder")}
|
|
/>
|
|
}/>
|
|
<InputField fieldName={t("guideLineSetting.intendedAudience")} input={
|
|
<TextAreaInput
|
|
value={intendedAudience}
|
|
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setIntendedAudience(e.target.value)}
|
|
placeholder={t("guideLineSetting.intendedAudiencePlaceholder")}
|
|
/>
|
|
}/>
|
|
<InputField fieldName={t("guideLineSetting.keyMessages")} input={
|
|
<TextAreaInput
|
|
value={keyMessages}
|
|
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setKeyMessages(e.target.value)}
|
|
placeholder={t("guideLineSetting.keyMessagesPlaceholder")}
|
|
/>
|
|
}/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default forwardRef<SettingRef, object>(GuideLineSetting);
|