Forms now update documents directly.
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { DocumentList } from "@/components/DocumentList";
|
import { DocumentList } from "@/components/DocumentList";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import { displayName } from "@/lib/relationships";
|
import { displayName } from "@/lib/relationships";
|
||||||
import type {
|
import type {
|
||||||
@@ -7,12 +8,10 @@ import type {
|
|||||||
Relationship,
|
Relationship,
|
||||||
RelationshipType,
|
RelationshipType,
|
||||||
} from "@/lib/types";
|
} from "@/lib/types";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useState } from "react";
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { Loader } from "./Loader";
|
import { Loader } from "./Loader";
|
||||||
import { DocumentRow } from "./documents/DocumentRow";
|
import { DocumentRow } from "./documents/DocumentRow";
|
||||||
import { NewRelatedDocumentForm } from "./documents/NewRelatedDocumentForm";
|
import { NewRelatedDocumentForm } from "./documents/NewRelatedDocumentForm";
|
||||||
import { useDocument } from "@/context/document/DocumentContext";
|
|
||||||
|
|
||||||
interface RelationshipListProps {
|
interface RelationshipListProps {
|
||||||
root: AnyDocument;
|
root: AnyDocument;
|
||||||
@@ -27,99 +26,8 @@ export function RelationshipList({
|
|||||||
root,
|
root,
|
||||||
relationshipType,
|
relationshipType,
|
||||||
}: RelationshipListProps) {
|
}: RelationshipListProps) {
|
||||||
// const [items, setItems] = useState<AnyDocument[]>([]);
|
const [_loading, setLoading] = useState(true);
|
||||||
// const [relationshipId, setRelationshipId] = useState<string | null>(null);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
// const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// async function fetchItems() {
|
|
||||||
// const { relationship } = await queryClient.fetchQuery({
|
|
||||||
// queryKey: ["relationship", relationshipType, root.id],
|
|
||||||
// staleTime: 5 * 60 * 1000, // 5 mintues
|
|
||||||
// queryFn: async () => {
|
|
||||||
// setLoading(true);
|
|
||||||
// const relationship: Relationship = await pb
|
|
||||||
// .collection("relationships")
|
|
||||||
// .getFirstListItem(
|
|
||||||
// `primary = "${root.id}" && type = "${relationshipType}"`,
|
|
||||||
// {
|
|
||||||
// expand: "secondary",
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// setLoading(false);
|
|
||||||
//
|
|
||||||
// return { relationship };
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// setRelationshipId(relationship.id);
|
|
||||||
// setItems(relationship.expand?.secondary ?? []);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fetchItems();
|
|
||||||
// }, [root, relationshipType]);
|
|
||||||
|
|
||||||
// Handles creation of a new document and adds it to the relationship
|
|
||||||
// const handleCreate = async (doc: AnyDocument) => {
|
|
||||||
// setLoading(true);
|
|
||||||
// setError(null);
|
|
||||||
// try {
|
|
||||||
// // Check for existing relationship
|
|
||||||
// if (relationshipId) {
|
|
||||||
// console.debug("Adding to existing relationship", relationshipId);
|
|
||||||
// await pb.collection("relationships").update(relationshipId, {
|
|
||||||
// "+secondary": doc.id,
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// console.debug("Creating new relationship");
|
|
||||||
// const relationship = await pb.collection("relationships").create({
|
|
||||||
// primary: root.id,
|
|
||||||
// secondary: [doc.id],
|
|
||||||
// type: relationshipType,
|
|
||||||
// });
|
|
||||||
// setRelationshipId(relationship.id);
|
|
||||||
// }
|
|
||||||
// queryClient.invalidateQueries({
|
|
||||||
// queryKey: ["relationship", relationshipType, root.id],
|
|
||||||
// });
|
|
||||||
// setItems((prev) => [doc, ...prev]);
|
|
||||||
// } catch (e: any) {
|
|
||||||
// setError(e?.message || "Failed to add document to relationship.");
|
|
||||||
// } finally {
|
|
||||||
// setLoading(false);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// const handleRemove = async (documentId: DocumentId) => {
|
|
||||||
// setLoading(true);
|
|
||||||
// setError(null);
|
|
||||||
//
|
|
||||||
// try {
|
|
||||||
// if (relationshipId) {
|
|
||||||
// console.debug("Removing from existing relationship", relationshipId);
|
|
||||||
// await pb.collection("relationships").update(relationshipId, {
|
|
||||||
// "secondary-": documentId,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// queryClient.invalidateQueries({
|
|
||||||
// queryKey: ["relationship", relationshipType, root.id],
|
|
||||||
// });
|
|
||||||
// setItems((prev) => prev.filter((item) => item.id != documentId));
|
|
||||||
// } catch (e: any) {
|
|
||||||
// setError(
|
|
||||||
// e?.message || `Failed to remove document from ${relationshipType}.`,
|
|
||||||
// );
|
|
||||||
// } finally {
|
|
||||||
// setLoading(false);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// if (loading) {
|
|
||||||
// <Loader />;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const { state, dispatch } = useDocument();
|
const { state, dispatch } = useDocument();
|
||||||
|
|
||||||
if (state.status === "loading") {
|
if (state.status === "loading") {
|
||||||
@@ -162,10 +70,6 @@ export function RelationshipList({
|
|||||||
relationship: updatedRelationship,
|
relationship: updatedRelationship,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
dispatch({
|
|
||||||
type: "setRelatedDocument",
|
|
||||||
doc,
|
|
||||||
});
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
setError(e?.message || "Failed to add document to relationship.");
|
setError(e?.message || "Failed to add document to relationship.");
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,27 +1,31 @@
|
|||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import type { Location } from "@/lib/types";
|
import type { Location } from "@/lib/types";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders an editable location form
|
* Renders an editable location form
|
||||||
*/
|
*/
|
||||||
export const LocationEditForm = ({ location }: { location: Location }) => {
|
export const LocationEditForm = ({ location }: { location: Location }) => {
|
||||||
|
const { dispatch } = useDocument();
|
||||||
async function saveLocationName(name: string) {
|
async function saveLocationName(name: string) {
|
||||||
await pb.collection("documents").update(location.id, {
|
const updated: Location = await pb.collection("documents").update(location.id, {
|
||||||
data: {
|
data: {
|
||||||
...location.data,
|
...location.data,
|
||||||
name,
|
name,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
dispatch({ type: "setDocument", doc: updated });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveLocationDescription(description: string) {
|
async function saveLocationDescription(description: string) {
|
||||||
await pb.collection("documents").update(location.id, {
|
const updated: Location = await pb.collection("documents").update(location.id, {
|
||||||
data: {
|
data: {
|
||||||
...location.data,
|
...location.data,
|
||||||
description,
|
description,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
dispatch({ type: "setDocument", doc: updated });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { pb } from "@/lib/pocketbase";
|
|||||||
import { BaseForm } from "@/components/form/BaseForm";
|
import { BaseForm } from "@/components/form/BaseForm";
|
||||||
import { MultiLineInput } from "@/components/form/MultiLineInput";
|
import { MultiLineInput } from "@/components/form/MultiLineInput";
|
||||||
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a form to add a new location. Calls onCreate with the new location document.
|
* Renders a form to add a new location. Calls onCreate with the new location document.
|
||||||
@@ -15,6 +16,7 @@ export const NewLocationForm = ({
|
|||||||
campaign: CampaignId;
|
campaign: CampaignId;
|
||||||
onCreate: (location: Location) => Promise<void>;
|
onCreate: (location: Location) => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { dispatch } = useDocument();
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
const [adding, setAdding] = useState(false);
|
const [adding, setAdding] = useState(false);
|
||||||
@@ -36,6 +38,7 @@ export const NewLocationForm = ({
|
|||||||
});
|
});
|
||||||
setName("");
|
setName("");
|
||||||
setDescription("");
|
setDescription("");
|
||||||
|
dispatch({ type: "setDocument", doc: locationDoc});
|
||||||
await onCreate(locationDoc);
|
await onCreate(locationDoc);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
setError(e?.message || "Failed to add location.");
|
setError(e?.message || "Failed to add location.");
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import type { Monster } from "@/lib/types";
|
import type { Monster } from "@/lib/types";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders an editable monster row
|
* Renders an editable monster row
|
||||||
*/
|
*/
|
||||||
export const MonsterEditForm = ({ monster }: { monster: Monster }) => {
|
export const MonsterEditForm = ({ monster }: { monster: Monster }) => {
|
||||||
|
const { dispatch } = useDocument();
|
||||||
async function saveMonsterName(name: string) {
|
async function saveMonsterName(name: string) {
|
||||||
await pb.collection("documents").update(monster.id, {
|
const updated = await pb.collection("documents").update(monster.id, {
|
||||||
data: {
|
data: {
|
||||||
...monster.data,
|
...monster.data,
|
||||||
name,
|
name,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
dispatch({ type: "setDocument", doc: updated });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { CampaignId, Monster } from "@/lib/types";
|
|||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import { BaseForm } from "@/components/form/BaseForm";
|
import { BaseForm } from "@/components/form/BaseForm";
|
||||||
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a form to add a new monster. Calls onCreate with the new monster document.
|
* Renders a form to add a new monster. Calls onCreate with the new monster document.
|
||||||
@@ -14,6 +15,7 @@ export const NewMonsterForm = ({
|
|||||||
campaign: CampaignId;
|
campaign: CampaignId;
|
||||||
onCreate: (monster: Monster) => Promise<void>;
|
onCreate: (monster: Monster) => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { dispatch } = useDocument();
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [adding, setAdding] = useState(false);
|
const [adding, setAdding] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@@ -32,6 +34,7 @@ export const NewMonsterForm = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
setName("");
|
setName("");
|
||||||
|
dispatch({ type: "setDocument", doc: monsterDoc });
|
||||||
await onCreate(monsterDoc);
|
await onCreate(monsterDoc);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
setError(e?.message || "Failed to add monster.");
|
setError(e?.message || "Failed to add monster.");
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { pb } from "@/lib/pocketbase";
|
|||||||
import { BaseForm } from "@/components/form/BaseForm";
|
import { BaseForm } from "@/components/form/BaseForm";
|
||||||
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
||||||
import { MultiLineInput } from "@/components/form/MultiLineInput";
|
import { MultiLineInput } from "@/components/form/MultiLineInput";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a form to add a new npc. Calls onCreate with the new npc document.
|
* Renders a form to add a new npc. Calls onCreate with the new npc document.
|
||||||
@@ -15,6 +16,7 @@ export const NewNpcForm = ({
|
|||||||
campaign: CampaignId;
|
campaign: CampaignId;
|
||||||
onCreate: (npc: Npc) => Promise<void>;
|
onCreate: (npc: Npc) => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { dispatch } = useDocument();
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
const [adding, setAdding] = useState(false);
|
const [adding, setAdding] = useState(false);
|
||||||
@@ -36,6 +38,7 @@ export const NewNpcForm = ({
|
|||||||
});
|
});
|
||||||
setName("");
|
setName("");
|
||||||
setDescription("");
|
setDescription("");
|
||||||
|
dispatch({ type: "setDocument", doc: npcDoc });
|
||||||
await onCreate(npcDoc);
|
await onCreate(npcDoc);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
setError(e?.message || "Failed to add npc.");
|
setError(e?.message || "Failed to add npc.");
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import type { Npc } from "@/lib/types";
|
import type { Npc } from "@/lib/types";
|
||||||
|
|
||||||
@@ -6,22 +7,25 @@ import type { Npc } from "@/lib/types";
|
|||||||
* Renders an editable npc form
|
* Renders an editable npc form
|
||||||
*/
|
*/
|
||||||
export const NpcEditForm = ({ npc }: { npc: Npc }) => {
|
export const NpcEditForm = ({ npc }: { npc: Npc }) => {
|
||||||
|
const { dispatch } = useDocument();
|
||||||
async function saveNpcName(name: string) {
|
async function saveNpcName(name: string) {
|
||||||
await pb.collection("documents").update(npc.id, {
|
const updated: Npc = await pb.collection("documents").update(npc.id, {
|
||||||
data: {
|
data: {
|
||||||
...npc.data,
|
...npc.data,
|
||||||
name,
|
name,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
dispatch({ type: "setDocument", doc: updated });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveNpcDescription(description: string) {
|
async function saveNpcDescription(description: string) {
|
||||||
await pb.collection("documents").update(npc.id, {
|
const updated: Npc = await pb.collection("documents").update(npc.id, {
|
||||||
data: {
|
data: {
|
||||||
...npc.data,
|
...npc.data,
|
||||||
description,
|
description,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
dispatch({ type: "setDocument", doc: updated });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { CampaignId, Scene } from "@/lib/types";
|
|||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import { BaseForm } from "@/components/form/BaseForm";
|
import { BaseForm } from "@/components/form/BaseForm";
|
||||||
import { MultiLineInput } from "@/components/form/MultiLineInput";
|
import { MultiLineInput } from "@/components/form/MultiLineInput";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a form to add a new scene. Calls onCreate with the new scene document.
|
* Renders a form to add a new scene. Calls onCreate with the new scene document.
|
||||||
@@ -16,6 +17,7 @@ export const NewSceneForm = ({
|
|||||||
campaign: CampaignId;
|
campaign: CampaignId;
|
||||||
onCreate: (scene: Scene) => Promise<void>;
|
onCreate: (scene: Scene) => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { dispatch } = useDocument();
|
||||||
const [text, setText] = useState("");
|
const [text, setText] = useState("");
|
||||||
const [adding, setAdding] = useState(false);
|
const [adding, setAdding] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@@ -34,6 +36,7 @@ export const NewSceneForm = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
setText("");
|
setText("");
|
||||||
|
dispatch({ type: "setDocument", doc: sceneDoc });
|
||||||
await onCreate(sceneDoc);
|
await onCreate(sceneDoc);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
setError(e?.message || "Failed to add scene.");
|
setError(e?.message || "Failed to add scene.");
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import type { Scene } from "@/lib/types";
|
import type { Scene } from "@/lib/types";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders an editable scene form
|
* Renders an editable scene form
|
||||||
*/
|
*/
|
||||||
export const SceneEditForm = ({ scene }: { scene: Scene }) => {
|
export const SceneEditForm = ({ scene }: { scene: Scene }) => {
|
||||||
|
const { dispatch } = useDocument();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
async function saveScene(text: string) {
|
async function saveScene(text: string) {
|
||||||
await pb.collection("documents").update(scene.id, {
|
const updated: Scene = await pb.collection("documents").update(scene.id, {
|
||||||
data: {
|
data: {
|
||||||
...scene.data,
|
...scene.data,
|
||||||
text,
|
text,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
queryClient.invalidateQueries({
|
dispatch({ type: "setDocument", doc: updated });
|
||||||
queryKey: ["relationship"],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { CampaignId, Secret } from "@/lib/types";
|
|||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import { BaseForm } from "@/components/form/BaseForm";
|
import { BaseForm } from "@/components/form/BaseForm";
|
||||||
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a form to add a new secret. Calls onCreate with the new secret document.
|
* Renders a form to add a new secret. Calls onCreate with the new secret document.
|
||||||
@@ -16,6 +17,7 @@ export const NewSecretForm = ({
|
|||||||
campaign: CampaignId;
|
campaign: CampaignId;
|
||||||
onCreate: (secret: Secret) => Promise<void>;
|
onCreate: (secret: Secret) => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { dispatch } = useDocument();
|
||||||
const [newSecret, setNewSecret] = useState("");
|
const [newSecret, setNewSecret] = useState("");
|
||||||
const [adding, setAdding] = useState(false);
|
const [adding, setAdding] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@@ -35,6 +37,7 @@ export const NewSecretForm = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
setNewSecret("");
|
setNewSecret("");
|
||||||
|
dispatch({ type: "setDocument", doc: secretDoc as Secret});
|
||||||
await onCreate(secretDoc);
|
await onCreate(secretDoc);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
setError(e?.message || "Failed to add secret.");
|
setError(e?.message || "Failed to add secret.");
|
||||||
|
|||||||
@@ -1,20 +1,16 @@
|
|||||||
// Displays a single secret with discovered checkbox and text.
|
// Displays a single secret with discovered checkbox and text.
|
||||||
import type { Secret, Session } from "@/lib/types";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
|
import { pb } from "@/lib/pocketbase";
|
||||||
|
import type { Secret } from "@/lib/types";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders an editable secret form.
|
* Renders an editable secret form.
|
||||||
* Handles updating the discovered state and discoveredIn relationship.
|
* Handles updating the discovered state and discoveredIn relationship.
|
||||||
*/
|
*/
|
||||||
export const SecretEditForm = ({
|
export const SecretEditForm = ({ secret }: { secret: Secret }) => {
|
||||||
secret,
|
const { dispatch } = useDocument();
|
||||||
session,
|
|
||||||
}: {
|
|
||||||
secret: Secret;
|
|
||||||
session?: Session;
|
|
||||||
}) => {
|
|
||||||
const [checked, setChecked] = useState(
|
const [checked, setChecked] = useState(
|
||||||
!!(secret.data as any)?.secret?.discovered,
|
!!(secret.data as any)?.secret?.discovered,
|
||||||
);
|
);
|
||||||
@@ -25,43 +21,28 @@ export const SecretEditForm = ({
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setChecked(newChecked);
|
setChecked(newChecked);
|
||||||
try {
|
try {
|
||||||
await pb.collection("documents").update(secret.id, {
|
const updated: Secret = await pb
|
||||||
|
.collection("documents")
|
||||||
|
.update(secret.id, {
|
||||||
data: {
|
data: {
|
||||||
...secret.data,
|
...secret.data,
|
||||||
discovered: newChecked,
|
discovered: newChecked,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (session || !newChecked) {
|
dispatch({ type: "setDocument", doc: updated });
|
||||||
// If the session exists or the element is being unchecked, remove any
|
|
||||||
// existing discoveredIn relationship
|
|
||||||
const rels = await pb.collection("relationships").getList(1, 1, {
|
|
||||||
filter: `primary = "${secret.id}" && type = "discoveredIn"`,
|
|
||||||
});
|
|
||||||
if (rels.items.length > 0) {
|
|
||||||
await pb.collection("relationships").delete(rels.items[0].id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (session) {
|
|
||||||
if (newChecked) {
|
|
||||||
await pb.collection("relationships").create({
|
|
||||||
primary: secret.id,
|
|
||||||
secondary: [session.id],
|
|
||||||
type: "discoveredIn",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveText(text: string) {
|
async function saveText(text: string) {
|
||||||
await pb.collection("documents").update(secret.id, {
|
const updated: Secret = await pb.collection("documents").update(secret.id, {
|
||||||
data: {
|
data: {
|
||||||
...secret.data,
|
...secret.data,
|
||||||
text,
|
text,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
dispatch({ type: "setDocument", doc: updated });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import type { Session } from "@/lib/types";
|
import type { Session } from "@/lib/types";
|
||||||
|
|
||||||
export const SessionEditForm = ({ session }: { session: Session }) => {
|
export const SessionEditForm = ({ session }: { session: Session }) => {
|
||||||
|
const { dispatch } = useDocument();
|
||||||
|
|
||||||
async function saveStrongStart(strongStart: string) {
|
async function saveStrongStart(strongStart: string) {
|
||||||
const freshRecord: Session = await pb
|
const doc: Session = await pb.collection("documents").update(session.id, {
|
||||||
.collection("documents")
|
|
||||||
.update(session.id, {
|
|
||||||
data: {
|
data: {
|
||||||
...session.data,
|
...session.data,
|
||||||
strongStart,
|
strongStart,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
dispatch({
|
||||||
|
type: "setDocument",
|
||||||
|
doc,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { CampaignId, Treasure } from "@/lib/types";
|
|||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import { BaseForm } from "@/components/form/BaseForm";
|
import { BaseForm } from "@/components/form/BaseForm";
|
||||||
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a form to add a new treasure. Calls onCreate with the new treasure document.
|
* Renders a form to add a new treasure. Calls onCreate with the new treasure document.
|
||||||
@@ -16,6 +17,7 @@ export const NewTreasureForm = ({
|
|||||||
campaign: CampaignId;
|
campaign: CampaignId;
|
||||||
onCreate: (treasure: Treasure) => Promise<void>;
|
onCreate: (treasure: Treasure) => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { dispatch } = useDocument();
|
||||||
const [newTreasure, setNewTreasure] = useState("");
|
const [newTreasure, setNewTreasure] = useState("");
|
||||||
const [adding, setAdding] = useState(false);
|
const [adding, setAdding] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@@ -35,6 +37,10 @@ export const NewTreasureForm = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
setNewTreasure("");
|
setNewTreasure("");
|
||||||
|
dispatch({
|
||||||
|
type: "setDocument",
|
||||||
|
doc: treasureDoc,
|
||||||
|
});
|
||||||
await onCreate(treasureDoc);
|
await onCreate(treasureDoc);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
setError(e?.message || "Failed to add treasure.");
|
setError(e?.message || "Failed to add treasure.");
|
||||||
|
|||||||
@@ -1,20 +1,16 @@
|
|||||||
// Displays a single treasure with discovered checkbox and text.
|
// Displays a single treasure with discovered checkbox and text.
|
||||||
import type { Treasure, Session } from "@/lib/types";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
||||||
|
import { useDocument } from "@/context/document/DocumentContext";
|
||||||
|
import { pb } from "@/lib/pocketbase";
|
||||||
|
import type { Treasure } from "@/lib/types";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders an editable treasure form.
|
* Renders an editable treasure form.
|
||||||
* Handles updating the discovered state and discoveredIn relationship.
|
* Handles updating the discovered state and discoveredIn relationship.
|
||||||
*/
|
*/
|
||||||
export const TreasureEditForm = ({
|
export const TreasureEditForm = ({ treasure }: { treasure: Treasure }) => {
|
||||||
treasure,
|
const { dispatch } = useDocument();
|
||||||
session,
|
|
||||||
}: {
|
|
||||||
treasure: Treasure;
|
|
||||||
session?: Session;
|
|
||||||
}) => {
|
|
||||||
const [checked, setChecked] = useState(
|
const [checked, setChecked] = useState(
|
||||||
!!(treasure.data as any)?.treasure?.discovered,
|
!!(treasure.data as any)?.treasure?.discovered,
|
||||||
);
|
);
|
||||||
@@ -25,7 +21,9 @@ export const TreasureEditForm = ({
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setChecked(newChecked);
|
setChecked(newChecked);
|
||||||
try {
|
try {
|
||||||
await pb.collection("documents").update(treasure.id, {
|
const updated: Treasure = await pb
|
||||||
|
.collection("documents")
|
||||||
|
.update(treasure.id, {
|
||||||
data: {
|
data: {
|
||||||
...treasure.data,
|
...treasure.data,
|
||||||
treasure: {
|
treasure: {
|
||||||
@@ -34,37 +32,22 @@ export const TreasureEditForm = ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (session || !newChecked) {
|
dispatch({ type: "setDocument", doc: updated });
|
||||||
// If the session exists or the element is being unchecked, remove any
|
|
||||||
// existing discoveredIn relationship
|
|
||||||
const rels = await pb.collection("relationships").getList(1, 1, {
|
|
||||||
filter: `primary = "${treasure.id}" && type = "discoveredIn"`,
|
|
||||||
});
|
|
||||||
if (rels.items.length > 0) {
|
|
||||||
await pb.collection("relationships").delete(rels.items[0].id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (session) {
|
|
||||||
if (newChecked) {
|
|
||||||
await pb.collection("relationships").create({
|
|
||||||
primary: treasure.id,
|
|
||||||
secondary: [session.id],
|
|
||||||
type: "discoveredIn",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveText(text: string) {
|
async function saveText(text: string) {
|
||||||
await pb.collection("documents").update(treasure.id, {
|
const updated: Treasure = await pb
|
||||||
|
.collection("documents")
|
||||||
|
.update(treasure.id, {
|
||||||
data: {
|
data: {
|
||||||
...treasure.data,
|
...treasure.data,
|
||||||
text,
|
text,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
dispatch({ type: "setDocument", doc: updated });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import { type AnyDocument, type DocumentId } from "@/lib/types";
|
import { type AnyDocument, type DocumentId } from "@/lib/types";
|
||||||
|
import type { RecordModel } from "pocketbase";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { createContext, useContext, useEffect, useReducer } from "react";
|
import { createContext, useContext, useEffect, useReducer } from "react";
|
||||||
import type { DocumentAction } from "./actions";
|
import type { DocumentAction } from "./actions";
|
||||||
import { reducer } from "./reducer";
|
import { reducer } from "./reducer";
|
||||||
import { loading, type DocumentState } from "./state";
|
import { loading, type DocumentState } from "./state";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
|
||||||
import type { RecordModel } from "pocketbase";
|
|
||||||
|
|
||||||
type DocumentContextValue = {
|
type DocumentContextValue = {
|
||||||
state: DocumentState<AnyDocument>;
|
state: DocumentState<AnyDocument>;
|
||||||
@@ -27,19 +26,15 @@ export function DocumentProvider({
|
|||||||
documentId: DocumentId;
|
documentId: DocumentId;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const [state, dispatch] = useReducer(reducer, loading());
|
const [state, dispatch] = useReducer(reducer, loading());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchDocumentAndRelations() {
|
async function fetchDocumentAndRelations() {
|
||||||
const doc: AnyDocument = await queryClient.fetchQuery({
|
const doc: AnyDocument = await pb
|
||||||
queryKey: ["document", documentId],
|
.collection("documents")
|
||||||
staleTime: 5 * 60 * 1000, // 5 mintues
|
.getOne(documentId, {
|
||||||
queryFn: () =>
|
|
||||||
pb.collection("documents").getOne(documentId, {
|
|
||||||
expand:
|
expand:
|
||||||
"relationships_via_primary,relationships_via_primary.secondary",
|
"relationships_via_primary,relationships_via_primary.secondary",
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|||||||
@@ -11,14 +11,10 @@ export type DocumentAction<D extends AnyDocument> =
|
|||||||
relatedDocuments: AnyDocument[];
|
relatedDocuments: AnyDocument[];
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: "update";
|
type: "setDocument";
|
||||||
data: D["data"];
|
doc: AnyDocument;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: "setRelationship";
|
type: "setRelationship";
|
||||||
relationship: Relationship;
|
relationship: Relationship;
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "setRelatedDocument";
|
|
||||||
doc: AnyDocument;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type {
|
|||||||
AnyDocument,
|
AnyDocument,
|
||||||
DocumentId,
|
DocumentId,
|
||||||
Relationship,
|
Relationship,
|
||||||
RelationshipId,
|
|
||||||
RelationshipType,
|
RelationshipType,
|
||||||
} from "@/lib/types";
|
} from "@/lib/types";
|
||||||
import type { DocumentAction } from "./actions";
|
import type { DocumentAction } from "./actions";
|
||||||
@@ -43,18 +42,23 @@ export function reducer<D extends AnyDocument>(
|
|||||||
>,
|
>,
|
||||||
};
|
};
|
||||||
|
|
||||||
case "update":
|
case "setDocument":
|
||||||
if (state.status === "ready") {
|
return ifStatus("ready", state, (state) => {
|
||||||
|
if (state.doc.id === action.doc.id) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
doc: {
|
doc: action.doc as D,
|
||||||
...state.doc,
|
};
|
||||||
data: action.data,
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
relatedDocs: {
|
||||||
|
...state.relatedDocs,
|
||||||
|
[action.doc.id]: action.doc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} else {
|
});
|
||||||
return state;
|
|
||||||
}
|
|
||||||
case "setRelationship":
|
case "setRelationship":
|
||||||
return ifStatus("ready", state, (state) => ({
|
return ifStatus("ready", state, (state) => ({
|
||||||
...state,
|
...state,
|
||||||
@@ -63,13 +67,5 @@ export function reducer<D extends AnyDocument>(
|
|||||||
[action.relationship.type]: action.relationship,
|
[action.relationship.type]: action.relationship,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
case "setRelatedDocument":
|
|
||||||
return ifStatus("ready", state, (state) => ({
|
|
||||||
...state,
|
|
||||||
relatedDocs: {
|
|
||||||
...state.relatedDocs,
|
|
||||||
[action.doc.id]: action.doc,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user