- 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.
128 lines
5.5 KiB
TypeScript
128 lines
5.5 KiB
TypeScript
'use client'
|
|
import React, {Dispatch, SetStateAction, useContext, useState} from "react";
|
|
import {Book, Globe, LucideIcon, Map, Pencil, Trash2, User, Wand2} from 'lucide-react';
|
|
import {useTranslations} from '@/lib/i18n';
|
|
import AlertBox from "@/components/ui/AlertBox";
|
|
import {SessionContext, SessionContextProps} from "@/context/SessionContext";
|
|
import {LangContext, LangContextProps} from "@/context/LangContext";
|
|
import {AlertContext, AlertContextProps} from "@/context/AlertContext";
|
|
import OfflineContext, {OfflineContextType} from '@/context/OfflineContext';
|
|
import {isDesktop} from '@/lib/configs';
|
|
import {apiDelete} from '@/lib/api/client';
|
|
import {deleteSeries} from '@/lib/tauri';
|
|
|
|
interface SeriesSettingOption {
|
|
id: string;
|
|
name: string;
|
|
icon: LucideIcon;
|
|
}
|
|
|
|
interface SeriesSettingSidebarProps {
|
|
selectedSetting: string;
|
|
setSelectedSetting: Dispatch<SetStateAction<string>>;
|
|
seriesId: string;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export default function SeriesSettingSidebar(
|
|
{
|
|
selectedSetting,
|
|
setSelectedSetting,
|
|
seriesId,
|
|
onClose
|
|
}: SeriesSettingSidebarProps) {
|
|
const t = useTranslations();
|
|
const {session}: SessionContextProps = useContext<SessionContextProps>(SessionContext);
|
|
const {lang}: LangContextProps = useContext<LangContextProps>(LangContext);
|
|
const {errorMessage, successMessage}: AlertContextProps = useContext<AlertContextProps>(AlertContext);
|
|
const {isCurrentlyOffline} = useContext(OfflineContext);
|
|
const userToken: string = session?.accessToken ? session?.accessToken : '';
|
|
|
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState<boolean>(false);
|
|
|
|
async function handleDeleteSeries(): Promise<void> {
|
|
try {
|
|
const useLocal: boolean = isDesktop && isCurrentlyOffline();
|
|
const deletedAt: number = Math.floor(Date.now() / 1000);
|
|
const success: boolean = useLocal
|
|
? await deleteSeries(seriesId, deletedAt)
|
|
: await apiDelete<boolean>('series/delete', {seriesId: seriesId}, userToken, lang);
|
|
if (success) {
|
|
successMessage(t('seriesSetting.deleteSuccess'));
|
|
onClose();
|
|
window.location.reload();
|
|
}
|
|
} catch (error: unknown) {
|
|
if (error instanceof Error) {
|
|
errorMessage(error.message);
|
|
} else {
|
|
errorMessage(t('seriesSetting.deleteError'));
|
|
}
|
|
}
|
|
}
|
|
|
|
async function handleDeleteConfirm(): Promise<void> {
|
|
await handleDeleteSeries();
|
|
setShowDeleteConfirm(false);
|
|
}
|
|
|
|
const settings: SeriesSettingOption[] = [
|
|
{id: 'basic-information', name: 'seriesSetting.basicInformation', icon: Pencil},
|
|
{id: 'books', name: 'seriesSetting.books', icon: Book},
|
|
{id: 'characters', name: 'seriesSetting.characters', icon: User},
|
|
{id: 'worlds', name: 'seriesSetting.worlds', icon: Globe},
|
|
{id: 'locations', name: 'seriesSetting.locations', icon: Map},
|
|
{id: 'spells', name: 'seriesSetting.spells', icon: Wand2},
|
|
];
|
|
|
|
return (
|
|
<div className="py-4 px-2 flex flex-col h-full">
|
|
<nav className="space-y-0.5 flex-1">
|
|
{settings.map((setting: SeriesSettingOption) => {
|
|
const Icon: LucideIcon = setting.icon;
|
|
const isActive: boolean = selectedSetting === setting.id;
|
|
return (
|
|
<button
|
|
key={setting.id}
|
|
onClick={(): void => setSelectedSetting(setting.id)}
|
|
className={`flex items-center w-full text-sm rounded-lg transition-colors duration-150 px-3 py-2 ${
|
|
isActive
|
|
? 'bg-secondary text-text-primary font-medium'
|
|
: 'text-text-secondary hover:bg-secondary/50 hover:text-text-primary'
|
|
}`}
|
|
>
|
|
<Icon
|
|
className={`mr-2.5 w-4 h-4 flex-shrink-0 ${isActive ? 'text-primary' : 'text-muted'}`}
|
|
strokeWidth={1.75}
|
|
/>
|
|
{t(setting.name)}
|
|
</button>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
<div className="mt-4 pt-3 border-t border-secondary">
|
|
<button
|
|
onClick={(): void => setShowDeleteConfirm(true)}
|
|
className="w-full flex items-center justify-center gap-2 px-3 py-2 text-sm text-accent-red hover:bg-accent-red/20 rounded-lg transition-colors duration-150"
|
|
>
|
|
<Trash2 className="w-4 h-4" strokeWidth={1.75}/>
|
|
{t('seriesSetting.deleteSeries')}
|
|
</button>
|
|
</div>
|
|
|
|
{showDeleteConfirm && (
|
|
<AlertBox
|
|
title={t('seriesSetting.deleteSeries')}
|
|
message={t('seriesSetting.deleteConfirmMessage')}
|
|
type="danger"
|
|
onConfirm={handleDeleteConfirm}
|
|
onCancel={(): void => setShowDeleteConfirm(false)}
|
|
confirmText={t('common.delete')}
|
|
cancelText={t('common.cancel')}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|