tangled
alpha
login
or
join now
ansxor.ca
/
dashsky
0
fork
atom
bluesky client without react native baggage written in sveltekit
0
fork
atom
overview
issues
pulls
pipelines
fixedd authentication
ansxor.ca
1 week ago
7fcca1f4
9ae5fc03
+70
-14
7 changed files
expand all
collapse all
unified
split
src
lib
atproto.ts
components
Avatar.svelte
Post.svelte
context.ts
routes
+layout.svelte
+page.svelte
oauth
callback
+page.svelte
+11
-1
src/lib/atproto.ts
···
1
1
-
import { Client, simpleFetchHandler } from '@atcute/client';
1
1
+
import { Client, ok, simpleFetchHandler } from '@atcute/client';
2
2
+
import { AppBskyActorDefs } from '@atcute/bluesky';
2
3
import {
3
4
configureOAuth,
4
5
createAuthorizationUrl,
···
78
79
/** Check if a session is active */
79
80
export function isLoggedIn(): boolean {
80
81
return rpc !== null;
82
82
+
}
83
83
+
84
84
+
/** Fetch the logged-in user's profile */
85
85
+
export async function getProfile(): Promise<AppBskyActorDefs.ProfileViewDetailed | null> {
86
86
+
if (!rpc || !currentSession) return null;
87
87
+
const data = await ok(rpc.get('app.bsky.actor.getProfile', {
88
88
+
params: { actor: currentSession.info.sub }
89
89
+
}));
90
90
+
return data;
81
91
}
82
92
83
93
function setSession(session: Session): void {
+6
src/lib/components/Avatar.svelte
···
1
1
+
<script lang="ts">
2
2
+
import { AppBskyActorDefs } from '@atcute/bluesky';
3
3
+
4
4
+
let { user }: { user: AppBskyActorDefs.ProfileViewDetailed } = $props()
5
5
+
</script>
6
6
+
<img class="ml-2.5 mr-2 w-10.5 h-10.5 rounded-lg" src={user.avatar} alt={`${user.displayName || user.handle}'s avatar'`}>
+3
-2
src/lib/components/Post.svelte
···
1
1
<script lang="ts">
2
2
import RichText from "./RichText.svelte";
3
3
+
import Avatar from "./Avatar.svelte";
3
4
import type { PostView } from "@atcute/bluesky/types/app/feed/defs";
4
5
5
6
let { post }: { post: PostView } = $props();
6
7
</script>
7
8
8
9
<article class="flex pr-4 pl-2.5 pt-2 pb-2 border-post-border border">
9
9
-
<img class="ml-2.5 mr-2 w-10.5 h-10.5 rounded-lg" src={post.author.avatar} alt={`${post.author.displayName || post.author.handle}'s avatar'`}>
10
10
+
<Avatar user={post.author} />
10
11
<div>
11
12
<div class="mb-1">
12
13
<a href="#" >
···
30
31
</div>
31
32
</div>
32
33
</div>
33
33
-
</article>
34
34
+
</article>
+17
src/lib/context.ts
···
1
1
+
import { getContext, setContext } from 'svelte';
2
2
+
import type { AppBskyActorDefs } from '@atcute/bluesky';
3
3
+
4
4
+
export interface UserContext {
5
5
+
loggedIn: boolean;
6
6
+
profile: AppBskyActorDefs.ProfileViewDetailed | null;
7
7
+
}
8
8
+
9
9
+
const USER_KEY = Symbol('user');
10
10
+
11
11
+
export function setUserContext(ctx: UserContext) {
12
12
+
return setContext(USER_KEY, ctx);
13
13
+
}
14
14
+
15
15
+
export function getUserContext(): UserContext {
16
16
+
return getContext(USER_KEY);
17
17
+
}
+21
src/routes/+layout.svelte
···
1
1
<script lang="ts">
2
2
import './layout.css';
3
3
import favicon from '$lib/assets/favicon.svg';
4
4
+
import { onMount } from 'svelte';
5
5
+
import { resumeSession, getProfile } from '$lib/atproto';
6
6
+
import { setUserContext } from '$lib/context';
7
7
+
import type { AppBskyActorDefs } from '@atcute/bluesky';
4
8
5
9
let { children } = $props();
10
10
+
11
11
+
let loggedIn = $state(false);
12
12
+
let profile = $state<AppBskyActorDefs.ProfileViewDetailed | null>(null);
13
13
+
14
14
+
setUserContext({
15
15
+
get loggedIn() { return loggedIn; },
16
16
+
set loggedIn(v: boolean) { loggedIn = v; },
17
17
+
get profile() { return profile; },
18
18
+
set profile(v) { profile = v; }
19
19
+
});
20
20
+
21
21
+
onMount(async () => {
22
22
+
loggedIn = await resumeSession();
23
23
+
if (loggedIn) {
24
24
+
profile = await getProfile();
25
25
+
}
26
26
+
});
6
27
</script>
7
28
8
29
<svelte:head><link rel="icon" href={favicon} /></svelte:head>
+10
-9
src/routes/+page.svelte
···
1
1
<script lang="ts">
2
2
import Post from '$lib/components/Post.svelte';
3
3
-
import { login, resumeSession } from '$lib/atproto';
4
4
-
import { onMount } from 'svelte';
3
3
+
import Avatar from '$lib/components/Avatar.svelte';
4
4
+
import { login } from '$lib/atproto';
5
5
+
import { getUserContext } from '$lib/context';
5
6
6
7
let { data } = $props();
7
7
-
let loggedIn = $state(false);
8
8
+
const user = getUserContext();
8
9
let handle = $state('');
9
10
let loggingIn = $state(false);
10
10
-
11
11
-
onMount(async () => {
12
12
-
loggedIn = await resumeSession();
13
13
-
});
14
11
15
12
async function handleLogin() {
16
13
if (!handle.trim()) return;
···
25
22
26
23
<div class="mx-auto flex max-h-screen max-w-290">
27
24
<div class="sidebar p-4">
28
28
-
{#if loggedIn}
29
29
-
<p>Logged in</p>
25
25
+
{#if user.loggedIn}
26
26
+
<p>Logged in</p>
27
27
+
<div class="flex items-center gap-2">
28
28
+
<Avatar user={user.profile} />
29
29
+
{user.profile?.handle}
30
30
+
</div>
30
31
{:else}
31
32
<form onsubmit={(e) => { e.preventDefault(); handleLogin(); }}>
32
33
<input
+2
-2
src/routes/oauth/callback/+page.svelte
···
6
6
let error = $state('');
7
7
8
8
onMount(async () => {
9
9
-
const params = new URLSearchParams(location.hash.slice(1));
10
10
-
history.replaceState(null, '', location.pathname + location.search);
9
9
+
const params = new URLSearchParams(location.hash.slice(1));
10
10
+
console.log(params)
11
11
12
12
try {
13
13
await handleCallback(params);