Allow creating read-only documents through the /new endpoint with readonly=true (fixes issue #48)
This commit is contained in:
@@ -16,11 +16,22 @@ export function DocumentTitle() {
|
|||||||
}
|
}
|
||||||
}, [updateDoc]);
|
}, [updateDoc]);
|
||||||
|
|
||||||
const startEditing = useCallback(() => {
|
if (doc.readOnly) {
|
||||||
ref.current?.select();
|
return (
|
||||||
ref.current?.focus();
|
<div
|
||||||
}, [ref.current]);
|
className="flex justify-center items-center w-full"
|
||||||
|
title={doc.title}
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
return (
|
return (
|
||||||
<updateDoc.Form method="post" action={`/actions/${doc.id}/update`}>
|
<updateDoc.Form method="post" action={`/actions/${doc.id}/update`}>
|
||||||
<div
|
<div
|
||||||
@@ -44,7 +55,9 @@ export function DocumentTitle() {
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
{match(editedTitle)
|
{match(editedTitle)
|
||||||
.with(doc.title, () => <p className="ml-2 text-transparent">Save</p>)
|
.with(doc.title, () => (
|
||||||
|
<p className="ml-2 text-transparent">Save</p>
|
||||||
|
))
|
||||||
.with("", () => (
|
.with("", () => (
|
||||||
<button
|
<button
|
||||||
className="ml-2 text-lime-500 hover:text-lime-600 transition"
|
className="ml-2 text-lime-500 hover:text-lime-600 transition"
|
||||||
@@ -64,4 +77,5 @@ export function DocumentTitle() {
|
|||||||
</div>
|
</div>
|
||||||
</updateDoc.Form>
|
</updateDoc.Form>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,13 @@ function SampleJSONPreview({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<JsonDocProvider
|
<JsonDocProvider
|
||||||
doc={{ id: "sample", title: "Sample", type: "raw", contents: "" }}
|
doc={{
|
||||||
|
id: "sample",
|
||||||
|
title: "Sample",
|
||||||
|
type: "raw",
|
||||||
|
readOnly: false,
|
||||||
|
contents: "",
|
||||||
|
}}
|
||||||
path={initialSelection}
|
path={initialSelection}
|
||||||
>
|
>
|
||||||
<JsonProvider initialJson={json}>
|
<JsonProvider initialJson={json}>
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export function JsonEditor() {
|
|||||||
<CodeEditor
|
<CodeEditor
|
||||||
language="json"
|
language="json"
|
||||||
content={jsonMapped.json}
|
content={jsonMapped.json}
|
||||||
readOnly={false}
|
readOnly={true}
|
||||||
onUpdate={onUpdate}
|
onUpdate={onUpdate}
|
||||||
selection={selection}
|
selection={selection}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+11
-2
@@ -3,6 +3,7 @@ import { customRandom } from "nanoid";
|
|||||||
type BaseJsonDocument = {
|
type BaseJsonDocument = {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
readOnly: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RawJsonDocument = BaseJsonDocument & {
|
export type RawJsonDocument = BaseJsonDocument & {
|
||||||
@@ -17,6 +18,7 @@ export type UrlJsonDocument = BaseJsonDocument & {
|
|||||||
|
|
||||||
export type CreateJsonOptions = {
|
export type CreateJsonOptions = {
|
||||||
ttl?: number;
|
ttl?: number;
|
||||||
|
readOnly?: boolean;
|
||||||
metadata?: any;
|
metadata?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -41,11 +43,12 @@ export async function createFromUrl(
|
|||||||
options?: CreateJsonOptions
|
options?: CreateJsonOptions
|
||||||
): Promise<JSONDocument> {
|
): Promise<JSONDocument> {
|
||||||
const docId = createId();
|
const docId = createId();
|
||||||
const doc = {
|
const doc: JSONDocument = {
|
||||||
id: docId,
|
id: docId,
|
||||||
type: <const>"url",
|
type: <const>"url",
|
||||||
url: url.href,
|
url: url.href,
|
||||||
title: title ?? url.hostname,
|
title: title ?? url.hostname,
|
||||||
|
readOnly: options?.readOnly ?? false,
|
||||||
};
|
};
|
||||||
|
|
||||||
await DOCUMENTS.put(docId, JSON.stringify(doc), {
|
await DOCUMENTS.put(docId, JSON.stringify(doc), {
|
||||||
@@ -62,7 +65,13 @@ export async function createFromRawJson(
|
|||||||
options?: CreateJsonOptions
|
options?: CreateJsonOptions
|
||||||
): Promise<JSONDocument> {
|
): Promise<JSONDocument> {
|
||||||
const docId = createId();
|
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), {
|
await DOCUMENTS.put(docId, JSON.stringify(doc), {
|
||||||
expirationTtl: options?.ttl ?? undefined,
|
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 invariant from "tiny-invariant";
|
||||||
import { sendEvent } from "~/graphJSON.server";
|
import { sendEvent } from "~/graphJSON.server";
|
||||||
import {
|
import {
|
||||||
@@ -12,6 +12,8 @@ export let loader: LoaderFunction = async ({ request, context }) => {
|
|||||||
const jsonUrl = url.searchParams.get("url");
|
const jsonUrl = url.searchParams.get("url");
|
||||||
const base64EncodedJson = url.searchParams.get("j");
|
const base64EncodedJson = url.searchParams.get("j");
|
||||||
const ttl = url.searchParams.get("ttl");
|
const ttl = url.searchParams.get("ttl");
|
||||||
|
const readOnly = url.searchParams.get("readonly");
|
||||||
|
const title = url.searchParams.get("title");
|
||||||
|
|
||||||
if (!jsonUrl && !base64EncodedJson) {
|
if (!jsonUrl && !base64EncodedJson) {
|
||||||
return redirect("/");
|
return redirect("/");
|
||||||
@@ -27,12 +29,16 @@ export let loader: LoaderFunction = async ({ request, context }) => {
|
|||||||
invariant(options.ttl >= 60, "ttl must be at least 60 seconds");
|
invariant(options.ttl >= 60, "ttl must be at least 60 seconds");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof readOnly === "string") {
|
||||||
|
options.readOnly = readOnly === "true";
|
||||||
|
}
|
||||||
|
|
||||||
if (jsonUrl) {
|
if (jsonUrl) {
|
||||||
const jsonURL = new URL(jsonUrl);
|
const jsonURL = new URL(jsonUrl);
|
||||||
|
|
||||||
invariant(jsonURL, "url must be a valid URL");
|
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(
|
context.waitUntil(
|
||||||
sendEvent({
|
sendEvent({
|
||||||
@@ -49,7 +55,7 @@ export let loader: LoaderFunction = async ({ request, context }) => {
|
|||||||
|
|
||||||
if (base64EncodedJson) {
|
if (base64EncodedJson) {
|
||||||
const doc = await createFromRawJson(
|
const doc = await createFromRawJson(
|
||||||
"Untitled",
|
title ?? "Untitled",
|
||||||
atob(base64EncodedJson),
|
atob(base64EncodedJson),
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user