Files
QOffice/src/App.tsx
T
2026-06-01 06:43:17 +03:00

159 lines
5.8 KiB
TypeScript

import { useMemo } from "react";
import { AppShell } from "./components/AppShell";
import { QOutlook } from "./features/qoutlook/QOutlook";
import { QPowerPoint } from "./features/qpowerpoint/QPowerPoint";
import { QWord } from "./features/qword/QWord";
import { QExcell } from "./features/qexcell/QExcell";
import { initialState } from "./data/sampleData";
import { usePersistentState } from "./hooks/usePersistentState";
import type { AppId, MailMessage, QOfficeState, SlideDeck, WordDocument, Workbook } from "./types";
const STORAGE_KEY = "qoffice-state-v1";
function migrateQOfficeState(state: QOfficeState): QOfficeState {
const launchChecklistAttachments = initialState.mail.messages.find((message) => message.id === "mail-1")?.attachments;
const sampleSheet = initialState.workbook.sheets.find((sheet) => sheet.id === "sheet-1");
const sampleSlidesById = Object.fromEntries(initialState.deck.slides.map((slide) => [slide.id, slide]));
const sampleWordHtml = initialState.word.html;
const needsSampleWordColor =
state.word.id === initialState.word.id &&
sampleWordHtml.includes("Ключевой результат") &&
!state.word.html.includes("Ключевой результат");
let changed = false;
if (needsSampleWordColor) {
changed = true;
}
const messages = state.mail.messages.map((message) => {
if (!launchChecklistAttachments || message.id !== "mail-1" || (message.attachments?.length ?? 0) > 0) {
return message;
}
changed = true;
return { ...message, attachments: launchChecklistAttachments };
});
const sheets =
state.workbook.id === initialState.workbook.id && sampleSheet
? state.workbook.sheets.map((sheet) => {
if (sheet.id !== "sheet-1") {
return sheet;
}
const needsMergedCells = (sheet.mergedCells?.length ?? 0) === 0;
const needsSampleCell = !sheet.cells.A8 && sampleSheet.cells.A8;
const missingSampleFormats = Object.entries(sampleSheet.cellFormats ?? {}).filter(([address]) => !sheet.cellFormats?.[address]);
const missingSampleCharts = (sampleSheet.charts ?? []).filter((chart) => !(sheet.charts ?? []).some((existingChart) => existingChart.id === chart.id));
if (!needsMergedCells && !needsSampleCell && missingSampleFormats.length === 0 && missingSampleCharts.length === 0) {
return sheet;
}
changed = true;
return {
...sheet,
cells: needsSampleCell ? { ...sheet.cells, A8: sampleSheet.cells.A8 } : sheet.cells,
mergedCells: needsMergedCells ? sampleSheet.mergedCells : sheet.mergedCells,
cellFormats:
missingSampleFormats.length > 0
? {
...sheet.cellFormats,
...Object.fromEntries(missingSampleFormats)
}
: sheet.cellFormats,
charts: missingSampleCharts.length > 0 ? [...(sheet.charts ?? []), ...missingSampleCharts] : sheet.charts
};
})
: state.workbook.sheets;
const slides =
state.deck.id === initialState.deck.id
? state.deck.slides.map((slide) => {
const sampleSlide = sampleSlidesById[slide.id];
if (!sampleSlide?.backgroundColor || slide.backgroundColor) {
return slide;
}
changed = true;
return { ...slide, backgroundColor: sampleSlide.backgroundColor };
})
: state.deck.slides;
return changed
? {
...state,
word: needsSampleWordColor ? { ...state.word, html: sampleWordHtml } : state.word,
workbook: { ...state.workbook, sheets },
deck: { ...state.deck, slides },
mail: { ...state.mail, messages }
}
: state;
}
export function App() {
const [state, setState] = usePersistentState<QOfficeState>(STORAGE_KEY, initialState, migrateQOfficeState);
const status = useMemo(() => {
const updatedAt =
state.activeApp === "word"
? state.word.updatedAt
: state.activeApp === "sheets"
? state.workbook.updatedAt
: state.activeApp === "slides"
? state.deck.updatedAt
: undefined;
return {
savedLabel: "Сохранено локально",
updatedAt
};
}, [state]);
function setActiveApp(activeApp: AppId) {
setState((current) => ({ ...current, activeApp }));
}
function updateWord(word: WordDocument) {
setState((current) => ({ ...current, word: { ...word, updatedAt: new Date().toISOString() } }));
}
function updateWorkbook(workbook: Workbook) {
setState((current) => ({ ...current, workbook: { ...workbook, updatedAt: new Date().toISOString() } }));
}
function updateDeck(deck: SlideDeck) {
setState((current) => ({ ...current, deck: { ...deck, updatedAt: new Date().toISOString() } }));
}
function updateMail(updater: (mail: QOfficeState["mail"]) => QOfficeState["mail"]) {
setState((current) => ({ ...current, mail: updater(current.mail) }));
}
function setMailFolder(folder: MailMessage["folder"]) {
updateMail((mail) => {
const firstInFolder = mail.messages.find((message) => message.folder === folder);
return {
...mail,
activeFolder: folder,
activeMessageId: firstInFolder?.id ?? mail.activeMessageId
};
});
}
const activeSurface =
state.activeApp === "word" ? (
<QWord document={state.word} onChange={updateWord} />
) : state.activeApp === "sheets" ? (
<QExcell workbook={state.workbook} onChange={updateWorkbook} />
) : state.activeApp === "slides" ? (
<QPowerPoint deck={state.deck} onChange={updateDeck} />
) : (
<QOutlook mail={state.mail} onChange={updateMail} onFolderChange={setMailFolder} />
);
return (
<AppShell activeApp={state.activeApp} onAppChange={setActiveApp} status={status}>
{activeSurface}
</AppShell>
);
}