- Added offline detection logic with `OfflineContext` to improve app functionality in offline scenarios. - Integrated Tauri IPC functions to handle local tool settings and character attributes when offline. - Refined indentation logic in `TextEditor` for better compatibility with WebKit engines. - Removed unused `indent` property and related settings in editor components to simplify configuration. - Updated locale files with improved translation consistency and parameterized placeholders.
213 lines
9.0 KiB
TypeScript
213 lines
9.0 KiB
TypeScript
'use client'
|
|
import React, {lazy, Suspense, useContext, useRef, useState} from 'react';
|
|
import {Save} from 'lucide-react';
|
|
import {useTranslations} from '@/lib/i18n';
|
|
import SectionHeader from "@/components/ui/SectionHeader";
|
|
import IconButton from "@/components/ui/IconButton";
|
|
import PulseLoader from '@/components/ui/PulseLoader';
|
|
import ToggleSwitch from "@/components/form/ToggleSwitch";
|
|
import {BookContext, BookContextProps} from "@/context/BookContext";
|
|
import {SessionContext, SessionContextProps} from "@/context/SessionContext";
|
|
import {AlertContext, AlertContextProps} from "@/context/AlertContext";
|
|
import {LangContext, LangContextProps} from "@/context/LangContext";
|
|
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
|
|
import {isDesktop} from "@/lib/configs";
|
|
import {apiPatch} from "@/lib/api/client";
|
|
import {updateBookToolSetting} from "@/lib/tauri";
|
|
import {SettingRef} from "@/lib/types/settings";
|
|
import {BookProps} from "@/lib/types/book";
|
|
|
|
const BasicInformationSetting = lazy(function () {
|
|
return import('./BasicInformationSetting');
|
|
});
|
|
const GuideLineSetting = lazy(function () {
|
|
return import('./guideline/GuideLineSetting');
|
|
});
|
|
const StorySetting = lazy(function () {
|
|
return import('./story/StorySetting');
|
|
});
|
|
const QuillSenseSetting = lazy(function () {
|
|
return import('./quillsense/QuillSenseSetting');
|
|
});
|
|
const WorldSettings = lazy(function () {
|
|
return import('./world/settings/WorldSettings');
|
|
});
|
|
const LocationSettings = lazy(function () {
|
|
return import('./locations/settings/LocationSettings');
|
|
});
|
|
const CharacterSettings = lazy(function () {
|
|
return import('./characters/settings/CharacterSettings');
|
|
});
|
|
const SpellSettings = lazy(function () {
|
|
return import('./spells/settings/SpellSettings');
|
|
});
|
|
const ExportSetting = lazy(function () {
|
|
return import('./ExportSetting');
|
|
});
|
|
|
|
interface BookSettingOptionProps {
|
|
setting: string;
|
|
}
|
|
|
|
const selfManagedSettings: string[] = ['characters', 'spells', 'world', 'worlds', 'locations', 'export'];
|
|
|
|
type ToolName = 'worlds' | 'locations' | 'characters' | 'spells' | 'quillsense';
|
|
|
|
const toggleableSettings: Record<string, ToolName> = {
|
|
'world': 'worlds',
|
|
'worlds': 'worlds',
|
|
'locations': 'locations',
|
|
'characters': 'characters',
|
|
'spells': 'spells',
|
|
'quillsense': 'quillsense',
|
|
};
|
|
|
|
function getInitialToolEnabled(setting: string, book: BookProps | null): boolean {
|
|
const toolName: ToolName | undefined = toggleableSettings[setting];
|
|
if (!toolName) return false;
|
|
if (toolName === 'quillsense') return book?.quillsenseEnabled ?? false;
|
|
return book?.tools?.[toolName] ?? false;
|
|
}
|
|
|
|
export default function BookSettingOption({setting}: BookSettingOptionProps): React.JSX.Element {
|
|
const t = useTranslations();
|
|
const settingRef: React.RefObject<SettingRef | null> = useRef<SettingRef>(null);
|
|
const {book, setBook}: BookContextProps = useContext<BookContextProps>(BookContext);
|
|
const {session}: SessionContextProps = useContext<SessionContextProps>(SessionContext);
|
|
const {errorMessage}: AlertContextProps = useContext<AlertContextProps>(AlertContext);
|
|
const {lang}: LangContextProps = useContext<LangContextProps>(LangContext);
|
|
const {isCurrentlyOffline} = useContext(OfflineContext);
|
|
const userToken: string = session?.accessToken ?? '';
|
|
|
|
const isToggleable: boolean = setting in toggleableSettings;
|
|
const [toolEnabled, setToolEnabled] = useState<boolean>(getInitialToolEnabled(setting, book));
|
|
|
|
const showSaveButton: boolean = !selfManagedSettings.includes(setting);
|
|
|
|
function renderTitle(): string {
|
|
switch (setting) {
|
|
case 'basic-information':
|
|
return t("bookSettingOption.basicInformation");
|
|
case 'guide-line':
|
|
return t("bookSettingOption.guideLine");
|
|
case 'story':
|
|
return t("bookSettingOption.storyPlan");
|
|
case 'quillsense':
|
|
return t("bookSettingOption.quillsense");
|
|
case 'world':
|
|
return t("bookSettingOption.manageWorlds");
|
|
case 'locations':
|
|
return t("bookSettingOption.yourLocations");
|
|
case 'characters':
|
|
return t("bookSettingOption.characters");
|
|
case 'spells':
|
|
return t("bookSettingOption.spells");
|
|
case 'export':
|
|
return t("bookSettingOption.export");
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
async function handleToggleTool(enabled: boolean): Promise<void> {
|
|
const toolName: ToolName | undefined = toggleableSettings[setting];
|
|
if (!toolName || !book?.bookId) return;
|
|
const useLocal: boolean = isDesktop && (isCurrentlyOffline() || !!book.localBook);
|
|
if (useLocal && toolName === 'quillsense') {
|
|
errorMessage(t('bookSettingOption.quillsenseOffline'));
|
|
return;
|
|
}
|
|
try {
|
|
const result: boolean = useLocal
|
|
? await updateBookToolSetting(book.bookId, toolName, enabled)
|
|
: await apiPatch<boolean>('book/tool-setting', {
|
|
bookId: book.bookId,
|
|
toolName: toolName,
|
|
enabled: enabled
|
|
}, userToken, lang);
|
|
if (result && setBook && book) {
|
|
setToolEnabled(enabled);
|
|
if (toolName === 'quillsense') {
|
|
setBook({...book, quillsenseEnabled: enabled});
|
|
} else {
|
|
setBook({
|
|
...book,
|
|
tools: {
|
|
characters: toolName === 'characters' ? enabled : (book.tools?.characters ?? false),
|
|
worlds: toolName === 'worlds' ? enabled : (book.tools?.worlds ?? false),
|
|
locations: toolName === 'locations' ? enabled : (book.tools?.locations ?? false),
|
|
spells: toolName === 'spells' ? enabled : (book.tools?.spells ?? false),
|
|
}
|
|
});
|
|
}
|
|
}
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
} else {
|
|
errorMessage(t('bookSettingOption.unknownError'));
|
|
}
|
|
}
|
|
}
|
|
|
|
async function handleSaveClick(): Promise<void> {
|
|
if (settingRef.current?.handleSave) {
|
|
await settingRef.current.handleSave();
|
|
}
|
|
}
|
|
|
|
function renderHeaderActions(): React.JSX.Element {
|
|
return (
|
|
<div className="flex items-center gap-3">
|
|
{isToggleable && (
|
|
<ToggleSwitch
|
|
checked={toolEnabled}
|
|
onChange={handleToggleTool}
|
|
size="sm"
|
|
/>
|
|
)}
|
|
{showSaveButton && (
|
|
<IconButton icon={Save} variant="primary" onClick={handleSaveClick}/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="px-6">
|
|
<div className="sticky top-0 z-10 bg-darkest-background pt-6 pb-4">
|
|
<SectionHeader
|
|
title={renderTitle()}
|
|
actions={renderHeaderActions()}
|
|
/>
|
|
</div>
|
|
<div className="mb-6">
|
|
<Suspense fallback={<PulseLoader/>}>
|
|
{setting === 'basic-information' && <BasicInformationSetting ref={settingRef}/>}
|
|
{setting === 'guide-line' && <GuideLineSetting ref={settingRef}/>}
|
|
{setting === 'story' && <StorySetting ref={settingRef}/>}
|
|
{setting === 'quillsense' && <QuillSenseSetting ref={settingRef} toolEnabled={toolEnabled}/>}
|
|
{(setting === 'world' || setting === 'worlds') && (
|
|
<WorldSettings entityType="book" toolEnabled={toolEnabled}/>
|
|
)}
|
|
{setting === 'locations' && (
|
|
<LocationSettings entityType="book" toolEnabled={toolEnabled}/>
|
|
)}
|
|
{setting === 'characters' && (
|
|
<CharacterSettings entityType="book" toolEnabled={toolEnabled}/>
|
|
)}
|
|
{setting === 'spells' && (
|
|
<SpellSettings entityType="book" toolEnabled={toolEnabled}/>
|
|
)}
|
|
{setting === 'export' && <ExportSetting/>}
|
|
{!['basic-information', 'guide-line', 'story', 'world', 'worlds', 'locations', 'characters', 'spells', 'quillsense', 'export'].includes(setting) && (
|
|
<div className="text-text-secondary py-4 text-center">
|
|
{t("bookSettingOption.notAvailable")}
|
|
</div>
|
|
)}
|
|
</Suspense>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|