import { JsonObject, LocalNode, RawCoMap } from "cojson";
import { PageInfo } from "./types";
import { GridView } from "./grid-view.js";
import { useState, useMemo } from "react";
import { Button, Icon, Input, Modal } from "../ui";
import { styled } from "goober";
import { restoreCoMapToTimestamp } from "../utils/history";
import { CoValueEditor } from "./co-value-editor.js";
import { isWriter } from "../utils/permissions";
export function CoMapView({
coValue,
data,
node,
onNavigate,
}: {
coValue: RawCoMap;
data: JsonObject;
node: LocalNode;
onNavigate: (pages: PageInfo[]) => void;
}) {
return (
<>
>
);
}
function AddPropertyModal({
coValue,
node,
disabled,
}: {
coValue: RawCoMap;
node: LocalNode;
disabled: boolean;
}) {
const [isAddPropertyModalOpen, setIsAddPropertyModalOpen] = useState(false);
const [propertyName, setPropertyName] = useState("");
const openAddPropertyModal = () => {
setIsAddPropertyModalOpen(true);
setPropertyName("");
};
const handleCancel = () => {
setIsAddPropertyModalOpen(false);
setPropertyName("");
};
return (
<>
setPropertyName(e.target.value)}
placeholder="Enter property name"
/>
{propertyName && (
)}
>
);
}
function RestoreSnapshotModal({ coValue }: { coValue: RawCoMap }) {
const [isRestoreModalOpen, setIsRestoreModalOpen] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(-1);
const [removeUnknownProperties, setRemoveUnknownProperties] = useState(false);
const timestamps = useMemo(
() => coValue.core.verifiedTransactions.map((tx) => tx.madeAt),
[coValue.core.verifiedTransactions.length],
);
const coMapAtSelectedIndex = useMemo(() => {
if (selectedIndex === -1) return null;
return coValue.atTime(timestamps[selectedIndex]!).toJSON() as JsonObject;
}, [coValue, timestamps, selectedIndex]);
const openRestoreModal = () => {
setIsRestoreModalOpen(true);
setSelectedIndex(timestamps.length - 1);
};
const handleRestore = () => {
if (timestamps.length < 2) return;
if (timestamps.length === 0) return;
const selectedTimestamp = timestamps[selectedIndex];
if (selectedTimestamp === undefined) return;
restoreCoMapToTimestamp(
coValue,
selectedTimestamp,
removeUnknownProperties,
);
setIsRestoreModalOpen(false);
};
const handleClose = () => {
setIsRestoreModalOpen(false);
};
const canRestore = isWriter(coValue.group.myRole());
return (
<>
1 && canRestore}
>
{timestamps.length > 1 && (
<>
Select Timestamp
setSelectedIndex(Number(e.target.value))}
disabled={timestamps.length === 0}
/>
{timestamps[selectedIndex] !== undefined
? new Date(timestamps[selectedIndex]!).toISOString()
: "No timestamps available"}
{canRestore && (
setRemoveUnknownProperties(e.target.checked)}
/>
Remove unknown properties (properties that don't exist in the
selected snapshot)
)}
>
)}
{timestamps.length > 0 && timestamps[selectedIndex] !== undefined && (
State at that time:
{JSON.stringify(coMapAtSelectedIndex, null, 2)}
)}
{timestamps.length < 2 && (
At least 2 timestamps are required to restore a snapshot.
)}
>
);
}
const PreviewSection = styled("div")`
margin-top: 1.5rem;
`;
const PreviewLabel = styled("div")`
font-weight: 500;
margin-bottom: 0.5rem;
color: var(--j-text-color-strong);
`;
const PreviewPre = styled("pre")`
background-color: var(--j-foreground);
border: 1px solid var(--j-border-color);
border-radius: var(--j-radius-md);
padding: 1rem;
overflow-x: auto;
font-size: 0.875rem;
max-height: 400px;
overflow-y: auto;
color: var(--j-text-color);
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
`;
const RangeContainer = styled("div")`
display: flex;
flex-direction: column;
gap: 0.75rem;
`;
const RangeLabel = styled("label")`
font-weight: 500;
color: var(--j-text-color-strong);
font-size: 0.875rem;
`;
const RangeInput = styled("input")`
width: 100%;
height: 0.5rem;
border-radius: var(--j-radius-sm);
outline: none;
-webkit-appearance: none;
appearance: none;
background: var(--j-foreground);
cursor: pointer;
&::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 1.25rem;
height: 1.25rem;
border-radius: 50%;
background: var(--j-primary-color);
cursor: pointer;
border: 2px solid var(--j-background);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
&::-moz-range-thumb {
width: 1.25rem;
height: 1.25rem;
border-radius: 50%;
background: var(--j-primary-color);
cursor: pointer;
border: 2px solid var(--j-background);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`;
const TimestampDisplay = styled("div")`
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-size: 0.875rem;
color: var(--j-text-color);
padding: 0.5rem;
background-color: var(--j-foreground);
border: 1px solid var(--j-border-color);
border-radius: var(--j-radius-md);
text-align: center;
`;
const CheckboxContainer = styled("div")`
display: flex;
align-items: flex-start;
gap: 0.5rem;
margin-top: 1rem;
`;
const CheckboxInput = styled("input")`
width: 1rem;
height: 1rem;
margin-top: 0.125rem;
cursor: pointer;
accent-color: var(--j-primary-color);
`;
const CheckboxLabel = styled("label")`
font-size: 0.875rem;
color: var(--j-text-color);
cursor: pointer;
line-height: 1.25rem;
`;
const EditorContainer = styled("div")`
margin-top: 1rem;
`;