made a start on the logic for saving files

This commit is contained in:
Chris Kruining 2024-10-21 16:25:57 +02:00
parent 6064fd3b45
commit a6fc5720d4
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
5 changed files with 121 additions and 21 deletions

View file

@ -1,12 +1,13 @@
import { Menu } from "~/features/menu";
import { children, createEffect, createMemo, createResource, createSignal, onMount, ParentProps } from "solid-js";
import { MutarionKind, splitAt } from "~/utilities";
import { Sidebar } from "~/components/sidebar";
import { Component, createEffect, createMemo, createResource, createSignal, onMount, ParentComponent, ParentProps } from "solid-js";
import { emptyFolder, FolderEntry, walk as fileTreeWalk, Tree, FileEntry, Entry } from "~/components/filetree";
import { Menu } from "~/features/menu";
import { Grid, load, useFiles } from "~/features/file";
import { Command, Context, createCommand, Modifier, noop } from "~/features/command";
import { GridApi } from "~/features/file/grid";
import { emptyFolder, FolderEntry, walk as fileTreeWalk, Tree, FileEntry } from "~/components/filetree";
import css from "./edit.module.css";
import { splitAt } from "~/utilities";
import { match } from "ts-pattern";
async function* walk(directory: FileSystemDirectoryHandle, path: string[] = []): AsyncGenerator<{ id: string, handle: FileSystemFileHandle, path: string[], lang: string, entries: Map<string, string> }, void, never> {
for await (const handle of directory.values()) {
@ -31,13 +32,31 @@ async function* walk(directory: FileSystemDirectoryHandle, path: string[] = []):
}
};
function* breadthFirstTraverse(subject: FolderEntry): Generator<{ path: string[] } & Entry, void, unknown> {
const queue: ({ path: string[] } & Entry)[] = subject.entries.map(e => ({ path: [], ...e }));
while (queue.length > 0) {
const entry = queue.shift()!;
yield entry;
if (entry.kind === 'folder') {
queue.push(...entry.entries.map(e => ({ path: [...entry.path, entry.name], ...e })));
}
}
}
const findFile = (folder: FolderEntry, id: string) => {
return breadthFirstTraverse(folder).find((entry): entry is { path: string[] } & FileEntry => entry.kind === 'file' && entry.id === id);
}
export default function Edit(props: ParentProps) {
const filesContext = useFiles();
const [root, { mutate, refetch }] = createResource(() => filesContext?.get('root'));
const [tree, setFiles] = createSignal<FolderEntry>(emptyFolder);
const [columns, setColumns] = createSignal<string[]>([]);
const [rows, setRows] = createSignal<Map<string, Record<string, string>>>(new Map);
const [entries, setEntries] = createSignal<Map<string, Record<string, { id: String, value: string, handle: FileSystemFileHandle }>>>(new Map);
const [entries, setEntries] = createSignal<Map<string, Record<string, { id: string, value: string, handle: FileSystemFileHandle }>>>(new Map);
const [api, setApi] = createSignal<GridApi>();
const mutatedFiles = createMemo(() => {
@ -50,7 +69,7 @@ export default function Edit(props: ParentProps) {
return files.get(key)?.[lang]?.id;
})
.filter(Boolean)
.filter(file => file !== undefined)
);
});
@ -88,10 +107,6 @@ export default function Edit(props: ParentProps) {
}
});
createEffect(() => {
mutatedFiles()
});
const commands = {
open: createCommand('open', async () => {
const [fileHandle] = await window.showOpenFilePicker({
@ -118,7 +133,81 @@ export default function Edit(props: ParentProps) {
mutate(directory);
}),
save: createCommand('save', () => {
console.log('save');
const mutations = api()?.mutations() ?? [];
if (mutations.length === 0) {
return;
}
const rows = api()?.rows() ?? {};
const _entries = entries();
// Cases we can encounter:
// | | file extsis | no existing file |
// |---------|---------------------------|----------------------!
// | created | insert new key into file | create new file |
// | updated | update value | create new file (*1) |
// | deleted | remove key from file (*3) | no-op/skip (*2)(*3) |
//
// 1) This can happen if the key already exists in another language (so when adding a new language for example).
// 2) The same as with 1, when you delete a key, and there are not files for each language, then this is a valid case.
// 3) When a file has 0 keys, we can remove it.
for (const mutation of mutations) {
const [key, lang] = splitAt(mutation.key, mutation.key.lastIndexOf('.'));
const entry = _entries.get(key);
const localEntry = entry?.[lang];
console.log(entry, localEntry);
// TODO :: this is not really a matrix, we should resolve the file when one does not exist
//
// happy path :: When we do have both an entry and localEntry and the localEntry has an id and that file is found
// | | entry | localEntry | id | file |
// |---|-------!------------|----!------!
// | 1 | x | x | x | x |
// | 2 | x | x | x | |
// | 3 | x | x | | |
// | 4 | x | | | |
// | 5 | | | | |
if (!localEntry) {
throw new Error('invalid edge case???');
}
const file = findFile(tree(), localEntry.id);
const fileExists = file !== undefined;
console.log(key, file?.path.join('.'));
const fileLocalKey = key.slice(file?.path.join('.'));
const result = match([fileExists, mutation.kind])
.with([true, MutarionKind.Create], () => ({ action: MutarionKind.Create, key, value: rows[key][lang], file: file?.meta }))
.with([false, MutarionKind.Create], () => '2')
.with([true, MutarionKind.Update], () => ({ action: MutarionKind.Update, key, value: rows[key][lang], file: file?.meta }))
.with([false, MutarionKind.Update], () => '4')
.with([true, MutarionKind.Delete], () => ({ action: MutarionKind.Delete, key, file: file?.meta }))
.with([false, MutarionKind.Delete], () => '6')
.exhaustive();
console.log(mutation, key, lang, entry, file, result);
}
// for (const fileId of files) {
// const { path, meta } = findFile(tree(), fileId) ?? {};
// console.log(fileId, path, meta, entries());
// // TODO
// // - find file handle
// // - prepare data
// // -- clone entries map (so that order is preserved)
// // -- apply mutations
// // -- convert key to file local (ergo, remove the directory path prefix)
// // - write data to file
// }
}, { key: 's', modifier: Modifier.Control }),
saveAs: createCommand('save as', (handle?: FileSystemFileHandle) => {
console.log('save as ...', handle);