+1
-1
src/components/Login.tsx
+1
-1
src/components/Login.tsx
···
161
onClick={onClick}
162
className={`px-4 py-2 text-sm font-medium transition-colors rounded-full flex-1 ${
163
active
164
-
? "text-gray-950 dark:text-gray-200 border-gray-500 bg-gray-400 dark:bg-gray-500"
165
: "text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200"
166
}`}
167
>
···
161
onClick={onClick}
162
className={`px-4 py-2 text-sm font-medium transition-colors rounded-full flex-1 ${
163
active
164
+
? "text-gray-50 dark:text-gray-200 border-gray-500 bg-gray-400 dark:bg-gray-500"
165
: "text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200"
166
}`}
167
>
+7
-3
src/routes/notifications.tsx
+7
-3
src/routes/notifications.tsx
···
1
import { createFileRoute } from "@tanstack/react-router";
2
import React, { useEffect, useRef,useState } from "react";
3
4
import { useAuth } from "~/providers/UnifiedAuthProvider";
5
6
const HANDLE_DID_CACHE_TIMEOUT = 60 * 60 * 1000; // 1 hour
7
···
70
}
71
}
72
73
useEffect(() => {
74
if (!did) return;
75
setLoading(true);
76
setError(null);
77
const urls = [
78
-
`https://constellation.microcosm.blue/links?target=${encodeURIComponent(did)}&collection=app.bsky.feed.post&path=.facets[app.bsky.richtext.facet].features[app.bsky.richtext.facet%23mention].did`,
79
-
`https://constellation.microcosm.blue/links?target=${encodeURIComponent(did)}&collection=app.bsky.feed.post&path=.facets[].features[app.bsky.richtext.facet%23mention].did`,
80
-
`https://constellation.microcosm.blue/links?target=${encodeURIComponent(did)}&collection=app.bsky.graph.follow&path=.subject`,
81
];
82
let ignore = false;
83
Promise.all(
···
1
import { createFileRoute } from "@tanstack/react-router";
2
+
import { useAtom } from "jotai";
3
import React, { useEffect, useRef,useState } from "react";
4
5
import { useAuth } from "~/providers/UnifiedAuthProvider";
6
+
import { constellationURLAtom } from "~/utils/atoms";
7
8
const HANDLE_DID_CACHE_TIMEOUT = 60 * 60 * 1000; // 1 hour
9
···
72
}
73
}
74
75
+
const [constellationURL] = useAtom(constellationURLAtom)
76
+
77
useEffect(() => {
78
if (!did) return;
79
setLoading(true);
80
setError(null);
81
const urls = [
82
+
`https://${constellationURL}/links?target=${encodeURIComponent(did)}&collection=app.bsky.feed.post&path=.facets[app.bsky.richtext.facet].features[app.bsky.richtext.facet%23mention].did`,
83
+
`https://${constellationURL}/links?target=${encodeURIComponent(did)}&collection=app.bsky.feed.post&path=.facets[].features[app.bsky.richtext.facet%23mention].did`,
84
+
`https://${constellationURL}/links?target=${encodeURIComponent(did)}&collection=app.bsky.graph.follow&path=.subject`,
85
];
86
let ignore = false;
87
Promise.all(
+70
src/routes/settings.tsx
+70
src/routes/settings.tsx
···
1
import { createFileRoute } from "@tanstack/react-router";
2
+
import { useAtom } from "jotai";
3
4
import { Header } from "~/components/Header";
5
import Login from "~/components/Login";
6
+
import {
7
+
constellationURLAtom,
8
+
defaultconstellationURL,
9
+
defaultslingshotURL,
10
+
slingshotURLAtom,
11
+
} from "~/utils/atoms";
12
13
export const Route = createFileRoute("/settings")({
14
component: Settings,
···
28
}}
29
/>
30
<Login />
31
+
<TextInputSetting
32
+
atom={constellationURLAtom}
33
+
title={"Constellation URL"}
34
+
description={
35
+
"customize the Constellation instance to be used by Red Dwarf"
36
+
}
37
+
init={defaultconstellationURL}
38
+
/>
39
+
<TextInputSetting
40
+
atom={slingshotURLAtom}
41
+
title={"Slingshot URL"}
42
+
description={"customize the Slingshot instance to be used by Red Dwarf"}
43
+
init={defaultslingshotURL}
44
+
/>
45
</>
46
);
47
}
48
+
49
+
export function TextInputSetting({
50
+
atom,
51
+
title,
52
+
description,
53
+
init,
54
+
}: {
55
+
atom: typeof constellationURLAtom;
56
+
title?: string;
57
+
description?: string;
58
+
init?: string;
59
+
}) {
60
+
const [value, setValue] = useAtom(atom);
61
+
return (
62
+
<div className="flex flex-col gap-2 p-4 rounded-2xl border border-gray-200 dark:border-gray-800 ">
63
+
<div>
64
+
{title && (
65
+
<h3 className="text-sm font-medium text-gray-900 dark:text-gray-100">
66
+
{title}
67
+
</h3>
68
+
)}
69
+
{description && (
70
+
<p className="text-sm text-gray-500 dark:text-gray-400">
71
+
{description}
72
+
</p>
73
+
)}
74
+
</div>
75
+
76
+
<div className="flex flex-row gap-2 items-center">
77
+
<input
78
+
type="text"
79
+
value={value}
80
+
onChange={(e) => setValue(e.target.value)}
81
+
className="flex-1 px-3 py-2 rounded-lg bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700
82
+
text-gray-900 dark:text-gray-100 placeholder:text-gray-500 dark:placeholder:text-gray-400
83
+
focus:outline-none focus:ring-2 focus:ring-gray-400 dark:focus:ring-gray-600"
84
+
placeholder="Enter value..."
85
+
/>
86
+
<button
87
+
onClick={() => setValue(init ?? "")}
88
+
className="px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-800
89
+
text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 transition"
90
+
>
91
+
Reset
92
+
</button>
93
+
</div>
94
+
</div>
95
+
);
96
+
}
+11
src/utils/atoms.ts
+11
src/utils/atoms.ts
···
21
{}
22
);
23
24
+
export const defaultconstellationURL = 'constellation.microcosm.blue'
25
+
export const constellationURLAtom = atomWithStorage<string>(
26
+
'constellationURL',
27
+
defaultconstellationURL
28
+
)
29
+
export const defaultslingshotURL = 'slingshot.microcosm.blue'
30
+
export const slingshotURLAtom = atomWithStorage<string>(
31
+
'slingshotURL',
32
+
defaultslingshotURL
33
+
)
34
+
35
export const isAtTopAtom = atom<boolean>(true);
36
37
type ComposerState =
+13
-6
src/utils/useQuery.ts
+13
-6
src/utils/useQuery.ts
···
7
useQuery,
8
type UseQueryResult} from "@tanstack/react-query";
9
10
export function constructIdentityQuery(didorhandle?: string) {
11
return queryOptions({
12
queryKey: ["identity", didorhandle],
13
queryFn: async () => {
14
if (!didorhandle) return undefined as undefined
15
const res = await fetch(
16
-
`https://slingshot.microcosm.blue/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${encodeURIComponent(didorhandle)}`
17
);
18
if (!res.ok) throw new Error("Failed to fetch post");
19
try {
···
63
queryKey: ["post", uri],
64
queryFn: async () => {
65
if (!uri) return undefined as undefined
66
const res = await fetch(
67
-
`https://slingshot.microcosm.blue/xrpc/com.bad-example.repo.getUriRecord?at_uri=${encodeURIComponent(uri)}`
68
);
69
let data: any;
70
try {
···
126
queryKey: ["profile", uri],
127
queryFn: async () => {
128
if (!uri) return undefined as undefined
129
const res = await fetch(
130
-
`https://slingshot.microcosm.blue/xrpc/com.bad-example.repo.getUriRecord?at_uri=${encodeURIComponent(uri)}`
131
);
132
let data: any;
133
try {
···
249
const path = query?.path
250
const cursor = query.cursor
251
const dids = query?.dids
252
const res = await fetch(
253
-
`https://constellation.microcosm.blue${method}?target=${encodeURIComponent(target)}${collection ? `&collection=${encodeURIComponent(collection)}` : ""}${path ? `&path=${encodeURIComponent(path)}` : ""}${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}${dids ? dids.map((did) => `&did=${encodeURIComponent(did)}`).join("") : ""}`
254
);
255
if (!res.ok) throw new Error("Failed to fetch post");
256
try {
···
450
queryKey: ["arbitrary", uri],
451
queryFn: async () => {
452
if (!uri) return undefined as undefined
453
const res = await fetch(
454
-
`https://slingshot.microcosm.blue/xrpc/com.bad-example.repo.getUriRecord?at_uri=${encodeURIComponent(uri)}`
455
);
456
let data: any;
457
try {
···
624
collection: string
625
path: string
626
}) {
627
-
const constellationHost = 'constellation.microcosm.blue'
628
console.log(
629
'yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks',
630
query,
···
7
useQuery,
8
type UseQueryResult} from "@tanstack/react-query";
9
10
+
import { constellationURLAtom, slingshotURLAtom, store } from "./atoms";
11
+
12
export function constructIdentityQuery(didorhandle?: string) {
13
return queryOptions({
14
queryKey: ["identity", didorhandle],
15
queryFn: async () => {
16
if (!didorhandle) return undefined as undefined
17
+
const slingshoturl = store.get(slingshotURLAtom)
18
const res = await fetch(
19
+
`https://${slingshoturl}/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${encodeURIComponent(didorhandle)}`
20
);
21
if (!res.ok) throw new Error("Failed to fetch post");
22
try {
···
66
queryKey: ["post", uri],
67
queryFn: async () => {
68
if (!uri) return undefined as undefined
69
+
const slingshoturl = store.get(slingshotURLAtom)
70
const res = await fetch(
71
+
`https://${slingshoturl}/xrpc/com.bad-example.repo.getUriRecord?at_uri=${encodeURIComponent(uri)}`
72
);
73
let data: any;
74
try {
···
130
queryKey: ["profile", uri],
131
queryFn: async () => {
132
if (!uri) return undefined as undefined
133
+
const slingshoturl = store.get(slingshotURLAtom)
134
const res = await fetch(
135
+
`https://${slingshoturl}/xrpc/com.bad-example.repo.getUriRecord?at_uri=${encodeURIComponent(uri)}`
136
);
137
let data: any;
138
try {
···
254
const path = query?.path
255
const cursor = query.cursor
256
const dids = query?.dids
257
+
const constellation = store.get(constellationURLAtom);
258
const res = await fetch(
259
+
`https://${constellation}${method}?target=${encodeURIComponent(target)}${collection ? `&collection=${encodeURIComponent(collection)}` : ""}${path ? `&path=${encodeURIComponent(path)}` : ""}${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}${dids ? dids.map((did) => `&did=${encodeURIComponent(did)}`).join("") : ""}`
260
);
261
if (!res.ok) throw new Error("Failed to fetch post");
262
try {
···
456
queryKey: ["arbitrary", uri],
457
queryFn: async () => {
458
if (!uri) return undefined as undefined
459
+
const slingshoturl = store.get(slingshotURLAtom)
460
const res = await fetch(
461
+
`https://${slingshoturl}/xrpc/com.bad-example.repo.getUriRecord?at_uri=${encodeURIComponent(uri)}`
462
);
463
let data: any;
464
try {
···
631
collection: string
632
path: string
633
}) {
634
+
const constellationHost = store.get(constellationURLAtom)
635
console.log(
636
'yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks',
637
query,