summaryrefslogtreecommitdiff
path: root/makima/frontend/src/routes/files.tsx
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2025-12-23 19:11:57 +0000
committersoryu <soryu@soryu.co>2025-12-23 19:11:57 +0000
commitf5222a7ae5ade5589436778cb01fc0abe625b3c3 (patch)
tree6e9739517d371179e6018412cba011b3f38868ef /makima/frontend/src/routes/files.tsx
parent3c0adec8e3a9dd3bc34251e87e0fb5314793426d (diff)
downloadsoryu-f5222a7ae5ade5589436778cb01fc0abe625b3c3.tar.gz
soryu-f5222a7ae5ade5589436778cb01fc0abe625b3c3.zip
Add editable file sections and a drag&drop feature
Diffstat (limited to 'makima/frontend/src/routes/files.tsx')
-rw-r--r--makima/frontend/src/routes/files.tsx65
1 files changed, 63 insertions, 2 deletions
diff --git a/makima/frontend/src/routes/files.tsx b/makima/frontend/src/routes/files.tsx
index 00c334d..79544c5 100644
--- a/makima/frontend/src/routes/files.tsx
+++ b/makima/frontend/src/routes/files.tsx
@@ -10,9 +10,10 @@ import type { FileDetail as FileDetailType, BodyElement } from "../lib/api";
export default function FilesPage() {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
- const { files, loading, error, fetchFile, editFile, removeFile } = useFiles();
+ const { files, loading, error, fetchFile, editFile, removeFile, saveFile } = useFiles();
const [fileDetail, setFileDetail] = useState<FileDetailType | null>(null);
const [detailLoading, setDetailLoading] = useState(false);
+ const [creating, setCreating] = useState(false);
// Load file detail when URL has an id
useEffect(() => {
@@ -72,6 +73,63 @@ export default function FilesPage() {
[fileDetail]
);
+ const handleBodyElementUpdate = useCallback(
+ async (index: number, element: BodyElement) => {
+ if (fileDetail && id) {
+ // Create new body array with updated element
+ const newBody = [...fileDetail.body];
+ newBody[index] = element;
+
+ // Update local state immediately for responsiveness
+ setFileDetail({
+ ...fileDetail,
+ body: newBody,
+ });
+
+ // Save to backend
+ await editFile(id, { body: newBody });
+ }
+ },
+ [fileDetail, id, editFile]
+ );
+
+ const handleBodyReorder = useCallback(
+ async (fromIndex: number, toIndex: number) => {
+ if (fileDetail && id) {
+ // Create new body array with reordered elements
+ const newBody = [...fileDetail.body];
+ const [movedElement] = newBody.splice(fromIndex, 1);
+ newBody.splice(toIndex, 0, movedElement);
+
+ // Update local state immediately for responsiveness
+ setFileDetail({
+ ...fileDetail,
+ body: newBody,
+ });
+
+ // Save to backend
+ await editFile(id, { body: newBody });
+ }
+ },
+ [fileDetail, id, editFile]
+ );
+
+ const handleCreate = useCallback(async () => {
+ if (creating) return;
+ setCreating(true);
+ try {
+ const newFile = await saveFile({
+ name: `Untitled ${new Date().toLocaleDateString()}`,
+ transcript: [],
+ });
+ if (newFile) {
+ navigate(`/files/${newFile.id}`);
+ }
+ } finally {
+ setCreating(false);
+ }
+ }, [creating, saveFile, navigate]);
+
return (
<div className="relative z-10 h-screen flex flex-col overflow-hidden">
<Masthead showTicker={false} showNav />
@@ -92,6 +150,8 @@ export default function FilesPage() {
onBack={handleBack}
onSave={handleSave}
onDelete={handleDelete}
+ onBodyElementUpdate={handleBodyElementUpdate}
+ onBodyReorder={handleBodyReorder}
/>
</div>
<div className="shrink-0">
@@ -105,9 +165,10 @@ export default function FilesPage() {
) : (
<FileList
files={files}
- loading={loading}
+ loading={loading || creating}
onSelect={handleSelectFile}
onDelete={handleDelete}
+ onCreate={handleCreate}
/>
)}
</main>