Initial QOffice implementation
This commit is contained in:
+143
@@ -0,0 +1,143 @@
|
||||
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 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;
|
||||
|
||||
return changed
|
||||
? {
|
||||
...state,
|
||||
word: needsSampleWordColor ? { ...state.word, html: sampleWordHtml } : state.word,
|
||||
workbook: { ...state.workbook, sheets },
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user