From 48643f5ce114304bc6f2abaf7b79810d3f7cf5e3 Mon Sep 17 00:00:00 2001 From: Steven Vandevelde Date: Wed, 8 Apr 2026 21:52:11 +0200 Subject: [PATCH] chore: recognise atproto handle of user-data storage --- src/components/output/raw/atproto/element.js | 23 ++++ src/components/output/raw/atproto/types.d.ts | 1 + .../misc/scrobble/rocksky/index.inline.js | 106 +++++++++++++----- 3 files changed, 104 insertions(+), 26 deletions(-) diff --git a/src/components/output/raw/atproto/element.js b/src/components/output/raw/atproto/element.js index 8a089eee..8ad3a34c 100644 --- a/src/components/output/raw/atproto/element.js +++ b/src/components/output/raw/atproto/element.js @@ -139,6 +139,7 @@ class ATProtoOutput extends BroadcastedOutputElement { // SIGNALS #did = signal(/** @type {`did:${string}:${string}` | null} */ (null)); + #handle = signal(/** @type {string | null} */ (null)); #isOnline = signal(navigator.onLine); #rev = signal(/** @type {string | null} */ (null)); #revFetchedAt = 0; @@ -152,6 +153,7 @@ class ATProtoOutput extends BroadcastedOutputElement { #writeCancels = new Map(); did = this.#did.get; + handle = this.#handle.get; rev = this.#rev.get; ready = computed(() => { @@ -204,6 +206,7 @@ class ATProtoOutput extends BroadcastedOutputElement { this.#agent = null; this.#authenticated = Promise.withResolvers(); this.#did.value = null; + this.#handle.value = null; this.#pdsUrl = null; this.#rpc = null; } @@ -218,6 +221,7 @@ class ATProtoOutput extends BroadcastedOutputElement { this.#agent = null; this.#authenticated = Promise.withResolvers(); this.#did.value = null; + this.#handle.value = null; this.#pdsUrl = null; this.#rpc = null; @@ -285,6 +289,25 @@ class ATProtoOutput extends BroadcastedOutputElement { this.#pdsUrl = session.info.aud; this.#authenticated.resolve(); this.#startFirehose(); + this.#fetchHandle(session.info.sub); + } + + /** + * @param {string} did + */ + async #fetchHandle(did) { + const rpc = this.#rpc; + if (!rpc) return; + try { + const result = await ok(rpc.get("com.atproto.repo.describeRepo", { + params: { repo: did }, + })); + if (this.#did.value === did) { + this.#handle.value = result.handle ?? null; + } + } catch { + // Non-fatal; handle stays null + } } // FIREHOSE diff --git a/src/components/output/raw/atproto/types.d.ts b/src/components/output/raw/atproto/types.d.ts index a123030c..60631e52 100644 --- a/src/components/output/raw/atproto/types.d.ts +++ b/src/components/output/raw/atproto/types.d.ts @@ -5,6 +5,7 @@ export type ATProtoOutputElement = & OutputElement & { did: SignalReader; + handle: SignalReader; rev: SignalReader; getLatestCommit(): Promise; diff --git a/src/facets/misc/scrobble/rocksky/index.inline.js b/src/facets/misc/scrobble/rocksky/index.inline.js index 2e421605..3817e1d8 100644 --- a/src/facets/misc/scrobble/rocksky/index.inline.js +++ b/src/facets/misc/scrobble/rocksky/index.inline.js @@ -2,6 +2,11 @@ import { html, nothing, render as litRender } from "lit-html"; import foundation from "~/common/foundation.js"; import { effect, signal } from "~/common/signal.js"; +import { NAME as ATPROTO_NAME } from "~/components/output/raw/atproto/element.js"; + +/** + * @import { ATProtoOutputElement } from "~/components/output/raw/atproto/types.d.ts" + */ //////////////////////////////////////////// // SETUP @@ -25,6 +30,17 @@ if (!rocksky) { } await customElements.whenDefined(rocksky.localName); +const atprotoEl = signal( + /** @type {ATProtoOutputElement | undefined} */ (undefined), +); + +customElements.whenDefined(ATPROTO_NAME).then(async () => { + const outputOrchestrator = await foundation.orchestrator.output(); + + atprotoEl.value = /** @type {ATProtoOutputElement | undefined} */ ( + outputOrchestrator.root().querySelector(ATPROTO_NAME) + ); +}); //////////////////////////////////////////// // REACTIVE UI @@ -33,12 +49,15 @@ await customElements.whenDefined(rocksky.localName); const $signingIn = signal(false); const main = document.querySelector("main"); +if (!main) throw new Error("main element not found"); effect(() => { const isAuthenticated = rocksky.isAuthenticated(); const isAuthenticating = rocksky.isAuthenticating(); const handle = rocksky.handle(); const signingIn = $signingIn.value; + const atprotoDid = atprotoEl.value?.did(); + const atprotoHandle = atprotoEl.value?.handle(); litRender( html` @@ -50,7 +69,8 @@ effect(() => { + > + @@ -61,39 +81,58 @@ effect(() => {
${isAuthenticated ? html` -
- ${handle - ? html`

Connected as ${handle}.

` - : nothing} -
- -
+
+ ${handle + ? html` +

Connected as ${handle}.

+ ` + : nothing} +
+
- ` +
+ ` : html` -
-

Sign in with your AT Protocol identity to connect Rocksky.

-
- +
+

Sign in with your AT Protocol identity to connect Rocksky.

+ + +

+ +

+ + ${atprotoDid + ? html`

-

- -
- `} + ` + : nothing} +
+ `}
`, main, @@ -104,13 +143,28 @@ effect(() => { // ACTIONS //////////////////////////////////////////// +/** + * @param {any} e + */ async function handleSignIn(e) { e.preventDefault(); - const handle = e.target.querySelector("input")?.value?.trim(); + const handle = e.target?.querySelector("input")?.value?.trim(); if (!handle) return; $signingIn.value = true; try { - await rocksky.signIn(handle); + await rocksky?.signIn(handle); + } finally { + $signingIn.value = false; + } +} + +/** + * @param {string} did + */ +async function handleSignInWithAtproto(did) { + $signingIn.value = true; + try { + await rocksky?.signIn(did); } finally { $signingIn.value = false; } -- 2.51.2