From ea8f41a64b390c05c89e6f16e52f4378d25a3644 Mon Sep 17 00:00:00 2001 From: Charlotte Som Date: Sun, 11 Jan 2026 23:18:39 +0000 Subject: [PATCH] (wip) upload form --- .gitignore | 1 + _client_build.ts | 6 + client/upload.tsx | 249 ++++++++++++++++++++++++++++++++ deno.json | 2 + deno.lock | 77 ++++++++-- web/css/styles.css | 80 +++++++++- web/upload/client-metadata.json | 12 ++ web/upload/index.html | 9 ++ 8 files changed, 421 insertions(+), 15 deletions(-) create mode 100644 .gitignore create mode 100644 client/upload.tsx create mode 100644 web/upload/client-metadata.json create mode 100644 web/upload/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2aa3591 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.env.local diff --git a/_client_build.ts b/_client_build.ts index 1b4f4f7..aff7385 100644 --- a/_client_build.ts +++ b/_client_build.ts @@ -5,3 +5,9 @@ esbuild.build({ outDir: "./web/dist", watch: Deno.args.includes("--watch"), }); + +esbuild.build({ + in: ["./client/upload.tsx"], + outDir: "./web/dist", + watch: Deno.args.includes("--watch"), +}); diff --git a/client/upload.tsx b/client/upload.tsx new file mode 100644 index 0000000..321a704 --- /dev/null +++ b/client/upload.tsx @@ -0,0 +1,249 @@ +import * as oauth from "@atcute/oauth-browser-client"; + +import { + CompositeDidDocumentResolver, + CompositeHandleResolver, + DohJsonHandleResolver, + LocalActorResolver, + PlcDidDocumentResolver, + WebDidDocumentResolver, + WellKnownHandleResolver, +} from "@atcute/identity-resolver"; + +// import type { Infer as InferLexicon } from "@char/lexicon.ts"; +// import type { ATProtoUniverse } from "@char/lexicon.ts/atproto"; +// type UploadBlob = InferLexicon; +type UploadBlob = any; + +const didDocumentResolver = new CompositeDidDocumentResolver({ + methods: { + plc: new PlcDidDocumentResolver(), + web: new WebDidDocumentResolver(), + }, +}); + +oauth.configureOAuth({ + metadata: { + client_id: true + ? "http://localhost" + + "?redirect_uri=" + + encodeURIComponent("http://127.0.0.1:4080/upload/") + + "&scope=" + + encodeURIComponent("atproto transition:generic") + : "https://video.cerulea.blue/upload/client-metadata.json", + redirect_uri: true ? "http://127.0.0.1:4080/upload/" : "https://video.cerulea.blue/upload/", + }, + identityResolver: new LocalActorResolver({ + handleResolver: new CompositeHandleResolver({ + methods: { + dns: new DohJsonHandleResolver({ + dohUrl: "https://mozilla.cloudflare-dns.com/dns-query", + }), + http: new WellKnownHandleResolver(), + }, + }), + didDocumentResolver, + }), +}); + +type Did = `did:${"web" | "plc"}:${string}`; +type ActorIdentifier = Did | `${string}.${string}`; +const login = async (identifier: ActorIdentifier) => { + const authUrl = await oauth.createAuthorizationUrl({ + target: { type: "account", identifier: identifier }, + scope: "atproto transition:generic", + }); + await new Promise(resolve => setTimeout(resolve, 200)); + globalThis.location.assign(authUrl); +}; + +const loginForm = (): HTMLFormElement => { + const handleField = ( + + ) as HTMLInputElement; + + const form = ( +
+ {handleField} + +
+ ) as HTMLFormElement; + + form.addEventListener("submit", e => { + e.preventDefault(); + if (handleField.value) { + login(handleField.value as ActorIdentifier); + } + }); + + return form; +}; + +const auth = async (): Promise => { + for (const sessionId of oauth.listStoredSessions()) { + try { + const session = await oauth.getSession(sessionId); + const agent = new oauth.OAuthUserAgent(session); + return agent; + } catch { + continue; + } + } + + const params = new URLSearchParams(globalThis.location.hash.slice(1)); + if (params.has("state") && params.has("code")) { + history.replaceState(null, "", location.pathname + location.search); + const { session } = await oauth.finalizeAuthorization(params); + const agent = new oauth.OAuthUserAgent(session); + return agent; + } + + return undefined; +}; + +const uploadForm = (agent: oauth.OAuthUserAgent): HTMLFormElement => { + const uploadField = () as HTMLInputElement; + const preview = (