Files
ERitors-Scribe-Desktop/components/book/settings/characters/editor/CharacterEditorDetail.tsx
natreex cfd08e3261 Bump app version to 0.5.0 and implement offline mode support across components
- 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.
2026-03-24 22:45:10 -04:00

105 lines
4.8 KiB
TypeScript

'use client';
import React, {useContext, useEffect} from 'react';
import {Attribute, CharacterAttribute, CharacterProps} from '@/lib/types/character';
import {characterCategories} from '@/lib/constants/character';
import {SelectBoxProps} from '@/components/form/SelectBox';
import {SeriesCharacterProps} from '@/lib/types/series';
import {useTranslations} from '@/lib/i18n';
import DetailField from '@/components/ui/DetailField';
import AvatarIcon from '@/components/ui/AvatarIcon';
import {SessionContext, SessionContextProps} from '@/context/SessionContext';
import {AlertContext, AlertContextProps} from '@/context/AlertContext';
import {LangContext, LangContextProps} from '@/context/LangContext';
import {BookContext, BookContextProps} from '@/context/BookContext';
import OfflineContext, {OfflineContextType} from '@/context/OfflineContext';
import {isDesktop} from '@/lib/configs';
import {apiGet} from '@/lib/api/client';
import {getCharacterAttributes} from '@/lib/tauri';
type AttributeResponse = { type: string; values: Attribute[] }[];
interface CharacterEditorDetailProps {
character: CharacterProps;
seriesCharacter?: SeriesCharacterProps | null;
onLoadAttributes?: (attributes: CharacterAttribute) => void;
}
/**
* CharacterEditorDetail - Version sidebar lecture seule
* Layout linéaire simple, juste les infos essentielles empilées
* PAS de CollapsableArea, PAS de grids
*/
export default function CharacterEditorDetail({
character,
seriesCharacter,
onLoadAttributes,
}: CharacterEditorDetailProps): React.JSX.Element {
const t = useTranslations();
const {lang}: LangContextProps = useContext<LangContextProps>(LangContext);
const {session}: SessionContextProps = useContext<SessionContextProps>(SessionContext);
const {errorMessage}: AlertContextProps = useContext<AlertContextProps>(AlertContext);
const {book}: BookContextProps = useContext<BookContextProps>(BookContext);
const {isCurrentlyOffline} = useContext(OfflineContext);
useEffect(function (): void {
if (character?.id !== null) {
getAttributes().then();
}
}, [character?.id]);
async function getAttributes(): Promise<void> {
try {
const useLocal: boolean = isDesktop && (isCurrentlyOffline() || !!book?.localBook);
const response: AttributeResponse = useLocal
? await getCharacterAttributes(character.id!) as AttributeResponse
: await apiGet<AttributeResponse>(
'character/attribute',
session.accessToken,
lang,
{characterId: character?.id}
);
if (response && onLoadAttributes) {
const attributes: CharacterAttribute = {};
response.forEach(function (item: { type: string; values: Attribute[] }): void {
attributes[item.type] = item.values;
});
onLoadAttributes(attributes);
}
} catch (e: unknown) {
if (e instanceof Error) {
errorMessage(e.message);
}
}
}
function getCategoryLabel(category: string | null | undefined): string {
if (!category) return '';
const found: SelectBoxProps | undefined = characterCategories.find(function (c: SelectBoxProps): boolean {
return c.value === category;
});
return found ? t(found.label) : category;
}
return (
<div>
{character.image && (
<div className="flex justify-center mb-4">
<AvatarIcon size="xl" image={character.image} alt={character.name}/>
</div>
)}
<h3 className="text-text-primary font-semibold text-base mb-4">
{character.name} {character.lastName}
</h3>
<DetailField variant="compact" label={t('characterDetail.role')}
value={getCategoryLabel(character.category)}/>
<DetailField variant="compact" label={t('characterDetail.title')} value={character.title}/>
<DetailField variant="compact" label={t('characterDetail.gender')} value={character.gender}/>
<DetailField variant="compact" label={t('characterDetail.age')} value={character.age}/>
<DetailField variant="compact" label={t('characterDetail.biography')} value={character.biography}/>
<DetailField variant="compact" label={t('characterDetail.roleFull')} value={character.role}/>
</div>
);
}