Allow creating read-only documents through the /new endpoint with readonly=true (fixes issue #48)
This commit is contained in:
@@ -16,52 +16,66 @@ export function DocumentTitle() {
|
||||
}
|
||||
}, [updateDoc]);
|
||||
|
||||
const startEditing = useCallback(() => {
|
||||
ref.current?.select();
|
||||
ref.current?.focus();
|
||||
}, [ref.current]);
|
||||
|
||||
return (
|
||||
<updateDoc.Form method="post" action={`/actions/${doc.id}/update`}>
|
||||
if (doc.readOnly) {
|
||||
return (
|
||||
<div
|
||||
className="flex justify-center items-center w-full"
|
||||
title={doc.title}
|
||||
>
|
||||
<label className="relative block group">
|
||||
<PencilAltIcon className="h-5 w-5 absolute top-1/2 transform -translate-y-1/2 left-3 text-white opacity-0 transition pointer-events-none group-hover:opacity-80 group-focus:opacity-80" />
|
||||
<input
|
||||
ref={ref}
|
||||
className={
|
||||
"min-w-[15vw] border-none text-ellipsis text-slate-300 px-2 pl-10 py-1 rounded-sm bg-transparent placeholder:text-slate-400 focus:bg-black/30 focus:outline-none focus:border-none hover:bg-black hover:bg-opacity-30 hover:cursor-text transition dark:bg-transparent dark:text-slate-200 dark:placeholder:text-slate-400 dark:focus:bg-black dark:focus:bg-opacity-10 dark:hover:bg-black dark:hover:bg-opacity-10"
|
||||
}
|
||||
type="text"
|
||||
name="title"
|
||||
spellCheck="false"
|
||||
placeholder="Name your JSON file"
|
||||
value={editedTitle}
|
||||
onChange={(e) => setEditedTitle(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
|
||||
{match(editedTitle)
|
||||
.with(doc.title, () => <p className="ml-2 text-transparent">Save</p>)
|
||||
.with("", () => (
|
||||
<button
|
||||
className="ml-2 text-lime-500 hover:text-lime-600 transition"
|
||||
onClick={() => setEditedTitle(doc.title)}
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
))
|
||||
.otherwise(() => (
|
||||
<button
|
||||
type="submit"
|
||||
className="ml-2 text-lime-500 hover:text-lime-600 transition"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
))}
|
||||
<span
|
||||
className={
|
||||
"min-w-[15vw] border-none text-ellipsis text-slate-300 px-2 pl-10 py-1 rounded-sm bg-transparent placeholder:text-slate-400 focus:bg-black/30 focus:outline-none focus:border-none hover:cursor-text transition dark:bg-transparent dark:text-slate-200 dark:placeholder:text-slate-400 dark:focus:bg-black dark:focus:bg-opacity-10"
|
||||
}
|
||||
>
|
||||
{doc.title}
|
||||
</span>
|
||||
</div>
|
||||
</updateDoc.Form>
|
||||
);
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<updateDoc.Form method="post" action={`/actions/${doc.id}/update`}>
|
||||
<div
|
||||
className="flex justify-center items-center w-full"
|
||||
title={doc.title}
|
||||
>
|
||||
<label className="relative block group">
|
||||
<PencilAltIcon className="h-5 w-5 absolute top-1/2 transform -translate-y-1/2 left-3 text-white opacity-0 transition pointer-events-none group-hover:opacity-80 group-focus:opacity-80" />
|
||||
<input
|
||||
ref={ref}
|
||||
className={
|
||||
"min-w-[15vw] border-none text-ellipsis text-slate-300 px-2 pl-10 py-1 rounded-sm bg-transparent placeholder:text-slate-400 focus:bg-black/30 focus:outline-none focus:border-none hover:bg-black hover:bg-opacity-30 hover:cursor-text transition dark:bg-transparent dark:text-slate-200 dark:placeholder:text-slate-400 dark:focus:bg-black dark:focus:bg-opacity-10 dark:hover:bg-black dark:hover:bg-opacity-10"
|
||||
}
|
||||
type="text"
|
||||
name="title"
|
||||
spellCheck="false"
|
||||
placeholder="Name your JSON file"
|
||||
value={editedTitle}
|
||||
onChange={(e) => setEditedTitle(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
|
||||
{match(editedTitle)
|
||||
.with(doc.title, () => (
|
||||
<p className="ml-2 text-transparent">Save</p>
|
||||
))
|
||||
.with("", () => (
|
||||
<button
|
||||
className="ml-2 text-lime-500 hover:text-lime-600 transition"
|
||||
onClick={() => setEditedTitle(doc.title)}
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
))
|
||||
.otherwise(() => (
|
||||
<button
|
||||
type="submit"
|
||||
className="ml-2 text-lime-500 hover:text-lime-600 transition"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</updateDoc.Form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,13 @@ function SampleJSONPreview({
|
||||
}) {
|
||||
return (
|
||||
<JsonDocProvider
|
||||
doc={{ id: "sample", title: "Sample", type: "raw", contents: "" }}
|
||||
doc={{
|
||||
id: "sample",
|
||||
title: "Sample",
|
||||
type: "raw",
|
||||
readOnly: false,
|
||||
contents: "",
|
||||
}}
|
||||
path={initialSelection}
|
||||
>
|
||||
<JsonProvider initialJson={json}>
|
||||
|
||||
@@ -81,7 +81,7 @@ export function JsonEditor() {
|
||||
<CodeEditor
|
||||
language="json"
|
||||
content={jsonMapped.json}
|
||||
readOnly={false}
|
||||
readOnly={true}
|
||||
onUpdate={onUpdate}
|
||||
selection={selection}
|
||||
/>
|
||||
|
||||
+11
-2
@@ -3,6 +3,7 @@ import { customRandom } from "nanoid";
|
||||
type BaseJsonDocument = {
|
||||
id: string;
|
||||
title: string;
|
||||
readOnly: boolean;
|
||||
};
|
||||
|
||||
export type RawJsonDocument = BaseJsonDocument & {
|
||||
@@ -17,6 +18,7 @@ export type UrlJsonDocument = BaseJsonDocument & {
|
||||
|
||||
export type CreateJsonOptions = {
|
||||
ttl?: number;
|
||||
readOnly?: boolean;
|
||||
metadata?: any;
|
||||
};
|
||||
|
||||
@@ -41,11 +43,12 @@ export async function createFromUrl(
|
||||
options?: CreateJsonOptions
|
||||
): Promise<JSONDocument> {
|
||||
const docId = createId();
|
||||
const doc = {
|
||||
const doc: JSONDocument = {
|
||||
id: docId,
|
||||
type: <const>"url",
|
||||
url: url.href,
|
||||
title: title ?? url.hostname,
|
||||
readOnly: options?.readOnly ?? false,
|
||||
};
|
||||
|
||||
await DOCUMENTS.put(docId, JSON.stringify(doc), {
|
||||
@@ -62,7 +65,13 @@ export async function createFromRawJson(
|
||||
options?: CreateJsonOptions
|
||||
): Promise<JSONDocument> {
|
||||
const docId = createId();
|
||||
const doc = { id: docId, type: <const>"raw", contents, title: filename };
|
||||
const doc: JSONDocument = {
|
||||
id: docId,
|
||||
type: <const>"raw",
|
||||
contents,
|
||||
title: filename,
|
||||
readOnly: options?.readOnly ?? false,
|
||||
};
|
||||
|
||||
await DOCUMENTS.put(docId, JSON.stringify(doc), {
|
||||
expirationTtl: options?.ttl ?? undefined,
|
||||
|
||||
+9
-3
@@ -1,4 +1,4 @@
|
||||
import { LoaderFunction, redirect } from "remix";
|
||||
import { json, LoaderFunction, redirect } from "remix";
|
||||
import invariant from "tiny-invariant";
|
||||
import { sendEvent } from "~/graphJSON.server";
|
||||
import {
|
||||
@@ -12,6 +12,8 @@ export let loader: LoaderFunction = async ({ request, context }) => {
|
||||
const jsonUrl = url.searchParams.get("url");
|
||||
const base64EncodedJson = url.searchParams.get("j");
|
||||
const ttl = url.searchParams.get("ttl");
|
||||
const readOnly = url.searchParams.get("readonly");
|
||||
const title = url.searchParams.get("title");
|
||||
|
||||
if (!jsonUrl && !base64EncodedJson) {
|
||||
return redirect("/");
|
||||
@@ -27,12 +29,16 @@ export let loader: LoaderFunction = async ({ request, context }) => {
|
||||
invariant(options.ttl >= 60, "ttl must be at least 60 seconds");
|
||||
}
|
||||
|
||||
if (typeof readOnly === "string") {
|
||||
options.readOnly = readOnly === "true";
|
||||
}
|
||||
|
||||
if (jsonUrl) {
|
||||
const jsonURL = new URL(jsonUrl);
|
||||
|
||||
invariant(jsonURL, "url must be a valid URL");
|
||||
|
||||
const doc = await createFromUrl(jsonURL, jsonURL.href, options);
|
||||
const doc = await createFromUrl(jsonURL, title ?? jsonURL.href, options);
|
||||
|
||||
context.waitUntil(
|
||||
sendEvent({
|
||||
@@ -49,7 +55,7 @@ export let loader: LoaderFunction = async ({ request, context }) => {
|
||||
|
||||
if (base64EncodedJson) {
|
||||
const doc = await createFromRawJson(
|
||||
"Untitled",
|
||||
title ?? "Untitled",
|
||||
atob(base64EncodedJson),
|
||||
options
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user