A music player that connects to your cloud/distributed storage.
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: recognise atproto handle of user-data storage

+104 -26
+23
src/components/output/raw/atproto/element.js
··· 139 139 // SIGNALS 140 140 141 141 #did = signal(/** @type {`did:${string}:${string}` | null} */ (null)); 142 + #handle = signal(/** @type {string | null} */ (null)); 142 143 #isOnline = signal(navigator.onLine); 143 144 #rev = signal(/** @type {string | null} */ (null)); 144 145 #revFetchedAt = 0; ··· 152 153 #writeCancels = new Map(); 153 154 154 155 did = this.#did.get; 156 + handle = this.#handle.get; 155 157 rev = this.#rev.get; 156 158 157 159 ready = computed(() => { ··· 204 206 this.#agent = null; 205 207 this.#authenticated = Promise.withResolvers(); 206 208 this.#did.value = null; 209 + this.#handle.value = null; 207 210 this.#pdsUrl = null; 208 211 this.#rpc = null; 209 212 } ··· 218 221 this.#agent = null; 219 222 this.#authenticated = Promise.withResolvers(); 220 223 this.#did.value = null; 224 + this.#handle.value = null; 221 225 this.#pdsUrl = null; 222 226 this.#rpc = null; 223 227 ··· 285 289 this.#pdsUrl = session.info.aud; 286 290 this.#authenticated.resolve(); 287 291 this.#startFirehose(); 292 + this.#fetchHandle(session.info.sub); 293 + } 294 + 295 + /** 296 + * @param {string} did 297 + */ 298 + async #fetchHandle(did) { 299 + const rpc = this.#rpc; 300 + if (!rpc) return; 301 + try { 302 + const result = await ok(rpc.get("com.atproto.repo.describeRepo", { 303 + params: { repo: did }, 304 + })); 305 + if (this.#did.value === did) { 306 + this.#handle.value = result.handle ?? null; 307 + } 308 + } catch { 309 + // Non-fatal; handle stays null 310 + } 288 311 } 289 312 290 313 // FIREHOSE
+1
src/components/output/raw/atproto/types.d.ts
··· 5 5 & OutputElement 6 6 & { 7 7 did: SignalReader<string | null>; 8 + handle: SignalReader<string | null>; 8 9 rev: SignalReader<string | null>; 9 10 10 11 getLatestCommit(): Promise<string | null>;
+80 -26
src/facets/misc/scrobble/rocksky/index.inline.js
··· 2 2 3 3 import foundation from "~/common/foundation.js"; 4 4 import { effect, signal } from "~/common/signal.js"; 5 + import { NAME as ATPROTO_NAME } from "~/components/output/raw/atproto/element.js"; 6 + 7 + /** 8 + * @import { ATProtoOutputElement } from "~/components/output/raw/atproto/types.d.ts" 9 + */ 5 10 6 11 //////////////////////////////////////////// 7 12 // SETUP ··· 25 30 } 26 31 27 32 await customElements.whenDefined(rocksky.localName); 33 + const atprotoEl = signal( 34 + /** @type {ATProtoOutputElement | undefined} */ (undefined), 35 + ); 36 + 37 + customElements.whenDefined(ATPROTO_NAME).then(async () => { 38 + const outputOrchestrator = await foundation.orchestrator.output(); 39 + 40 + atprotoEl.value = /** @type {ATProtoOutputElement | undefined} */ ( 41 + outputOrchestrator.root().querySelector(ATPROTO_NAME) 42 + ); 43 + }); 28 44 29 45 //////////////////////////////////////////// 30 46 // REACTIVE UI ··· 33 49 const $signingIn = signal(false); 34 50 35 51 const main = document.querySelector("main"); 52 + if (!main) throw new Error("main element not found"); 36 53 37 54 effect(() => { 38 55 const isAuthenticated = rocksky.isAuthenticated(); 39 56 const isAuthenticating = rocksky.isAuthenticating(); 40 57 const handle = rocksky.handle(); 41 58 const signingIn = $signingIn.value; 59 + const atprotoDid = atprotoEl.value?.did(); 60 + const atprotoHandle = atprotoEl.value?.handle(); 42 61 43 62 litRender( 44 63 html` ··· 50 69 <use 51 70 xlink:href="images/diffuse-current.svg#diffuse" 52 71 href="images/diffuse-current.svg#diffuse" 53 - ></use> 72 + > 73 + </use> 54 74 </svg> 55 75 </a> 56 76 </div> ··· 61 81 <div class="facet__right"> 62 82 ${isAuthenticated 63 83 ? html` 64 - <div> 65 - ${handle 66 - ? html`<p>Connected as <strong>${handle}</strong>.</p>` 67 - : nothing} 68 - <div class="button-row"> 69 - <button @click="${() => rocksky.signOut()}"> 70 - <i class="ph-bold ph-plugs-connected"></i> 71 - Disconnect 72 - </button> 73 - </div> 84 + <div> 85 + ${handle 86 + ? html` 87 + <p>Connected as <strong>${handle}</strong>.</p> 88 + ` 89 + : nothing} 90 + <div class="button-row"> 91 + <button @click="${() => rocksky.signOut()}"> 92 + <i class="ph-bold ph-plugs-connected"></i> 93 + Disconnect 94 + </button> 74 95 </div> 75 - ` 96 + </div> 97 + ` 76 98 : html` 77 - <div> 78 - <p>Sign in with your AT Protocol identity to connect Rocksky.</p> 79 - <form @submit="${handleSignIn}"> 80 - <label 81 - >Handle 82 - <input placeholder="you.bsky.social" /> 83 - </label> 99 + <div> 100 + <p>Sign in with your AT Protocol identity to connect Rocksky.</p> 101 + <form @submit="${handleSignIn}"> 102 + <label>Handle 103 + <input placeholder="you.bsky.social" /> 104 + </label> 105 + <p class="button-row"> 106 + <button ?disabled="${isAuthenticating || signingIn}"> 107 + <i 108 + class="ph-bold ${isAuthenticating || signingIn 109 + ? "ph-spinner animate-spin" 110 + : "ph-at"}" 111 + ></i> 112 + Connect with AT Protocol 113 + </button> 114 + </p> 115 + </form> 116 + ${atprotoDid 117 + ? html` 84 118 <p class="button-row"> 85 - <button ?disabled="${isAuthenticating || signingIn}"> 119 + <button 120 + ?disabled="${isAuthenticating || signingIn}" 121 + @click="${() => handleSignInWithAtproto(atprotoDid)}" 122 + class="button--bg-accent" 123 + > 86 124 <i 87 125 class="ph-bold ${isAuthenticating || signingIn 88 126 ? "ph-spinner animate-spin" 89 127 : "ph-at"}" 90 128 ></i> 91 - Connect with AT Protocol 129 + Sign in as ${atprotoHandle ?? atprotoDid} 92 130 </button> 93 131 </p> 94 - </form> 95 - </div> 96 - `} 132 + ` 133 + : nothing} 134 + </div> 135 + `} 97 136 </div> 98 137 `, 99 138 main, ··· 104 143 // ACTIONS 105 144 //////////////////////////////////////////// 106 145 146 + /** 147 + * @param {any} e 148 + */ 107 149 async function handleSignIn(e) { 108 150 e.preventDefault(); 109 - const handle = e.target.querySelector("input")?.value?.trim(); 151 + const handle = e.target?.querySelector("input")?.value?.trim(); 110 152 if (!handle) return; 111 153 $signingIn.value = true; 112 154 try { 113 - await rocksky.signIn(handle); 155 + await rocksky?.signIn(handle); 156 + } finally { 157 + $signingIn.value = false; 158 + } 159 + } 160 + 161 + /** 162 + * @param {string} did 163 + */ 164 + async function handleSignInWithAtproto(did) { 165 + $signingIn.value = true; 166 + try { 167 + await rocksky?.signIn(did); 114 168 } finally { 115 169 $signingIn.value = false; 116 170 }