Superpowered to do lists. No signup required.

update scopes, init update records

+3 -1
src/hooks.server.ts
··· 26 26 27 27 // set the authed agent 28 28 const authedAgent = new Agent(oauthSession); 29 - event.locals.authedAgent = authedAgent; 29 + if (!event.locals.authedAgent) { 30 + event.locals.authedAgent = authedAgent; 31 + } 30 32 31 33 // set the authed user with decrypted session DID 32 34 const user = await authedAgent.getProfile({ actor: decrypted });
+3 -3
src/lib/atproto.ts
··· 66 66 67 67 const publicUrl = "https://easytodo.link" 68 68 // localhost resolves to either 127.0.0.1 or [::1] (if ipv6) 69 - const url = dev ? "http://[::1]:5173" : publicUrl; 69 + const url = dev ? "http://127.0.0.1:5173" : publicUrl; 70 70 71 71 export const atclient = new NodeOAuthClient({ 72 72 stateStore: new AuthStateStore(db), ··· 77 77 : `http://localhost?redirect_uri=${ 78 78 encodeURIComponent(`${url}/oauth/callback`) 79 79 }&scope=${ 80 - encodeURIComponent(`atproto repo:link.easytodo.tasks.list repo:link.easytodo.tasks.task`) 80 + encodeURIComponent(`atproto repo:link.easytodo.tasks.list repo:link.easytodo.tasks.task rpc:app.bsky.actor.getProfile?aud=did:web:api.bsky.app%23bsky_appview`) 81 81 }`, 82 82 client_uri: url, 83 83 redirect_uris: [`${url}/oauth/callback`], 84 - scope: "atproto repo:link.easytodo.tasks.list repo:link.easytodo.tasks.task", 84 + scope: "atproto repo:link.easytodo.tasks.list repo:link.easytodo.tasks.task rpc:app.bsky.actor.getProfile?aud=did:web:api.bsky.app%23bsky_appview", 85 85 grant_types: ["authorization_code", "refresh_token"], 86 86 application_type: "web", 87 87 token_endpoint_auth_method: "none",
+2
src/lib/stores.svelte.ts
··· 38 38 // optional 39 39 duration?: number; 40 40 stopwatchInterval?: number; 41 + rkey?: string; 41 42 } 42 43 43 44 export type List = { 44 45 id: string; 45 46 title: string; 46 47 tasks: Task[]; 48 + rkey?: string; 47 49 } 48 50 49 51 export const local_lists = persisted<List[]>("local_lists", [
+10
src/lib/utils.ts
··· 4 4 return generateRandomString(10, alphabet("a-z", "0-9")); 5 5 } 6 6 7 + export function parseAtUri(uri: string) { 8 + const regex = /at:\/\/(?<did>did.*)\/(?<lexi>.*)\/(?<rkey>.*)/; 9 + const groups = regex.exec(uri)?.groups; 10 + return { 11 + did: groups?.did, 12 + lexi: groups?.lexi, 13 + rkey: groups?.rkey 14 + } 15 + } 16 + 7 17 export function formatSecondsToDuration(seconds: number = 0) { 8 18 let hours = Math.floor(seconds / 3600); 9 19 let minutes = Math.floor((seconds - (hours * 3600)) / 60);
+50 -22
src/routes/+page.server.ts
··· 1 1 import { atclient } from "$lib/atproto"; 2 2 import type { Task } from "$lib/stores.svelte"; 3 + import { parseAtUri } from "$lib/utils"; 3 4 import type { $Typed } from "@atproto/api"; 4 5 import type { Create, CreateResult } from "@atproto/api/dist/client/types/com/atproto/repo/applyWrites"; 5 6 import { isValidHandle } from "@atproto/syntax"; ··· 18 19 19 20 // get oauth authorizing url to redirect to 20 21 const redirectUrl = await atclient.authorize(handle, { 21 - scope: "atproto repo:link.easytodo.tasks.list repo:link.easytodo.tasks.task" 22 + scope: "atproto repo:link.easytodo.tasks.list repo:link.easytodo.tasks.task rpc:app.bsky.actor.getProfile?aud=did:web:api.bsky.app%23bsky_appview" 22 23 }); 23 24 24 25 if (!redirectUrl) { ··· 42 43 43 44 const formData = await request.formData(); 44 45 const id = formData.get("id") as string; 46 + const list_rkey = formData.get("rkey") as string; 45 47 const title = formData.get("title") as string; 46 48 const tasks = JSON.parse(formData.get("tasks") as string) as Task[]; 47 - 48 - const task_records = tasks.map((t) => { 49 - const { stopwatchInterval, ...rest } = t; 50 - return { 51 - $type: "link.easytodo.tasks.task", 52 - ...rest 53 - } 54 - }); 55 49 56 50 const response = await agent.com.atproto.repo.applyWrites({ 57 51 repo: user.did, 58 - writes: task_records.map((r) => { 59 - return { 60 - $type: 'com.atproto.repo.applyWrites#create', 61 - collection: "link.easytodo.tasks.task", 62 - value: r 63 - } 52 + writes: tasks.map((t) => { 53 + const { rkey: task_rkey, stopwatchInterval, ...rest } = t; 54 + if (task_rkey) { 55 + console.log("UPDATE TASK"); 56 + return { 57 + $type: 'com.atproto.repo.applyWrites#update', 58 + collection: "link.easytodo.tasks.task", 59 + rkey: task_rkey, 60 + value: { 61 + $type: "link.easytodo.tasks.task", 62 + ...rest 63 + } 64 + } 65 + } 66 + else { 67 + console.log("CREATE TASK"); 68 + return { 69 + $type: 'com.atproto.repo.applyWrites#create', 70 + collection: "link.easytodo.tasks.task", 71 + value: { 72 + $type: "link.easytodo.tasks.task", 73 + ...rest 74 + }, 75 + } 76 + } 64 77 }) 65 78 }); 66 - 79 + 67 80 if (response.success) { 68 81 console.log(response.data.results); 69 82 const list_record = { ··· 76 89 return { cid: t.cid, uri: t.uri } 77 90 }) 78 91 }; 79 - const { success, data } = await agent.com.atproto.repo.createRecord({ 80 - repo: user.did, 81 - collection: "link.easytodo.tasks.list", 82 - record: list_record 83 - }); 84 92 85 - return { saveListRecordResult: { success, uri: data.uri }}; 93 + if (list_rkey) { 94 + const { success, data } = await agent.com.atproto.repo.putRecord({ 95 + rkey: list_rkey, 96 + repo: user.did, 97 + collection: "link.easytodo.tasks.list", 98 + record: list_record 99 + }); 100 + console.log("UPDATE LIST", { success, uri: data.uri }); 101 + return { saveListRecordResult: { success, rkey: list_rkey, uri: data.uri }}; 102 + } 103 + else { 104 + const { success, data } = await agent.com.atproto.repo.createRecord({ 105 + repo: user.did, 106 + collection: "link.easytodo.tasks.list", 107 + record: list_record 108 + }); 109 + const { rkey } = parseAtUri(data.uri); 110 + console.log("CREATE LIST", { success, rkey, uri: data.uri }); 111 + return { saveListRecordResult: { success, rkey, uri: data.uri }}; 112 + } 113 + 86 114 } 87 115 } 88 116 };