Superpowered to do lists. No signup required.

update scopes, init update records

+3 -1
src/hooks.server.ts
··· 26 27 // set the authed agent 28 const authedAgent = new Agent(oauthSession); 29 - event.locals.authedAgent = authedAgent; 30 31 // set the authed user with decrypted session DID 32 const user = await authedAgent.getProfile({ actor: decrypted });
··· 26 27 // set the authed agent 28 const authedAgent = new Agent(oauthSession); 29 + if (!event.locals.authedAgent) { 30 + event.locals.authedAgent = authedAgent; 31 + } 32 33 // set the authed user with decrypted session DID 34 const user = await authedAgent.getProfile({ actor: decrypted });
+3 -3
src/lib/atproto.ts
··· 66 67 const publicUrl = "https://easytodo.link" 68 // localhost resolves to either 127.0.0.1 or [::1] (if ipv6) 69 - const url = dev ? "http://[::1]:5173" : publicUrl; 70 71 export const atclient = new NodeOAuthClient({ 72 stateStore: new AuthStateStore(db), ··· 77 : `http://localhost?redirect_uri=${ 78 encodeURIComponent(`${url}/oauth/callback`) 79 }&scope=${ 80 - encodeURIComponent(`atproto repo:link.easytodo.tasks.list repo:link.easytodo.tasks.task`) 81 }`, 82 client_uri: url, 83 redirect_uris: [`${url}/oauth/callback`], 84 - scope: "atproto repo:link.easytodo.tasks.list repo:link.easytodo.tasks.task", 85 grant_types: ["authorization_code", "refresh_token"], 86 application_type: "web", 87 token_endpoint_auth_method: "none",
··· 66 67 const publicUrl = "https://easytodo.link" 68 // localhost resolves to either 127.0.0.1 or [::1] (if ipv6) 69 + const url = dev ? "http://127.0.0.1:5173" : publicUrl; 70 71 export const atclient = new NodeOAuthClient({ 72 stateStore: new AuthStateStore(db), ··· 77 : `http://localhost?redirect_uri=${ 78 encodeURIComponent(`${url}/oauth/callback`) 79 }&scope=${ 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 }`, 82 client_uri: url, 83 redirect_uris: [`${url}/oauth/callback`], 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 grant_types: ["authorization_code", "refresh_token"], 86 application_type: "web", 87 token_endpoint_auth_method: "none",
+2
src/lib/stores.svelte.ts
··· 38 // optional 39 duration?: number; 40 stopwatchInterval?: number; 41 } 42 43 export type List = { 44 id: string; 45 title: string; 46 tasks: Task[]; 47 } 48 49 export const local_lists = persisted<List[]>("local_lists", [
··· 38 // optional 39 duration?: number; 40 stopwatchInterval?: number; 41 + rkey?: string; 42 } 43 44 export type List = { 45 id: string; 46 title: string; 47 tasks: Task[]; 48 + rkey?: string; 49 } 50 51 export const local_lists = persisted<List[]>("local_lists", [
+10
src/lib/utils.ts
··· 4 return generateRandomString(10, alphabet("a-z", "0-9")); 5 } 6 7 export function formatSecondsToDuration(seconds: number = 0) { 8 let hours = Math.floor(seconds / 3600); 9 let minutes = Math.floor((seconds - (hours * 3600)) / 60);
··· 4 return generateRandomString(10, alphabet("a-z", "0-9")); 5 } 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 + 17 export function formatSecondsToDuration(seconds: number = 0) { 18 let hours = Math.floor(seconds / 3600); 19 let minutes = Math.floor((seconds - (hours * 3600)) / 60);
+50 -22
src/routes/+page.server.ts
··· 1 import { atclient } from "$lib/atproto"; 2 import type { Task } from "$lib/stores.svelte"; 3 import type { $Typed } from "@atproto/api"; 4 import type { Create, CreateResult } from "@atproto/api/dist/client/types/com/atproto/repo/applyWrites"; 5 import { isValidHandle } from "@atproto/syntax"; ··· 18 19 // get oauth authorizing url to redirect to 20 const redirectUrl = await atclient.authorize(handle, { 21 - scope: "atproto repo:link.easytodo.tasks.list repo:link.easytodo.tasks.task" 22 }); 23 24 if (!redirectUrl) { ··· 42 43 const formData = await request.formData(); 44 const id = formData.get("id") as string; 45 const title = formData.get("title") as string; 46 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 56 const response = await agent.com.atproto.repo.applyWrites({ 57 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 - } 64 }) 65 }); 66 - 67 if (response.success) { 68 console.log(response.data.results); 69 const list_record = { ··· 76 return { cid: t.cid, uri: t.uri } 77 }) 78 }; 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 85 - return { saveListRecordResult: { success, uri: data.uri }}; 86 } 87 } 88 };
··· 1 import { atclient } from "$lib/atproto"; 2 import type { Task } from "$lib/stores.svelte"; 3 + import { parseAtUri } from "$lib/utils"; 4 import type { $Typed } from "@atproto/api"; 5 import type { Create, CreateResult } from "@atproto/api/dist/client/types/com/atproto/repo/applyWrites"; 6 import { isValidHandle } from "@atproto/syntax"; ··· 19 20 // get oauth authorizing url to redirect to 21 const redirectUrl = await atclient.authorize(handle, { 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" 23 }); 24 25 if (!redirectUrl) { ··· 43 44 const formData = await request.formData(); 45 const id = formData.get("id") as string; 46 + const list_rkey = formData.get("rkey") as string; 47 const title = formData.get("title") as string; 48 const tasks = JSON.parse(formData.get("tasks") as string) as Task[]; 49 50 const response = await agent.com.atproto.repo.applyWrites({ 51 repo: user.did, 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 + } 77 }) 78 }); 79 + 80 if (response.success) { 81 console.log(response.data.results); 82 const list_record = { ··· 89 return { cid: t.cid, uri: t.uri } 90 }) 91 }; 92 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 + 114 } 115 } 116 };