an appview-less Bluesky client using Constellation and PDS Queries
reddwarf.app
frontend
spa
bluesky
reddwarf
microcosm
1import { createFileRoute } from "@tanstack/react-router";
2import { useAtom } from "jotai";
3
4import { Header } from "~/components/Header";
5import Login from "~/components/Login";
6import {
7 constellationURLAtom,
8 defaultconstellationURL,
9 defaultImgCDN,
10 defaultslingshotURL,
11 defaultVideoCDN,
12 imgCDNAtom,
13 slingshotURLAtom,
14 videoCDNAtom,
15} from "~/utils/atoms";
16
17export const Route = createFileRoute("/settings")({
18 component: Settings,
19});
20
21export function Settings() {
22 return (
23 <>
24 <Header
25 title="Settings"
26 backButtonCallback={() => {
27 if (window.history.length > 1) {
28 window.history.back();
29 } else {
30 window.location.assign("/");
31 }
32 }}
33 />
34 <div className="lg:hidden"><Login /></div>
35 <div className="h-4" />
36 <TextInputSetting
37 atom={constellationURLAtom}
38 title={"Constellation"}
39 description={
40 "Customize the Constellation instance to be used by Red Dwarf"
41 }
42 init={defaultconstellationURL}
43 />
44 <TextInputSetting
45 atom={slingshotURLAtom}
46 title={"Slingshot"}
47 description={"Customize the Slingshot instance to be used by Red Dwarf"}
48 init={defaultslingshotURL}
49 />
50 <TextInputSetting
51 atom={imgCDNAtom}
52 title={"Image CDN"}
53 description={
54 "Customize the Constellation instance to be used by Red Dwarf"
55 }
56 init={defaultImgCDN}
57 />
58 <TextInputSetting
59 atom={videoCDNAtom}
60 title={"Video CDN"}
61 description={"Customize the Slingshot instance to be used by Red Dwarf"}
62 init={defaultVideoCDN}
63 />
64 <p className="text-gray-500 dark:text-gray-400 py-4 px-6 text-sm">please restart/refresh the app if changes arent applying correctly</p>
65 </>
66 );
67}
68
69export function TextInputSetting({
70 atom,
71 title,
72 description,
73 init,
74}: {
75 atom: typeof constellationURLAtom;
76 title?: string;
77 description?: string;
78 init?: string;
79}) {
80 const [value, setValue] = useAtom(atom);
81 return (
82 <div className="flex flex-col gap-2 px-4 py-2">
83 {/* <div>
84 {title && (
85 <h3 className="text-sm font-medium text-gray-900 dark:text-gray-100">
86 {title}
87 </h3>
88 )}
89 {description && (
90 <p className="text-sm text-gray-500 dark:text-gray-400">
91 {description}
92 </p>
93 )}
94 </div> */}
95
96 <div className="flex flex-row gap-2 items-center">
97 <div className="m3input-field m3input-label m3input-border size-md flex-1">
98 <input type="text" placeholder=" " value={value} onChange={(e) => setValue(e.target.value)}/>
99 <label>{title}</label>
100 </div>
101 {/* <input
102 type="text"
103 value={value}
104 onChange={(e) => setValue(e.target.value)}
105 className="flex-1 px-3 py-2 rounded-lg bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700
106 text-gray-900 dark:text-gray-100 placeholder:text-gray-500 dark:placeholder:text-gray-400
107 focus:outline-none focus:ring-2 focus:ring-gray-400 dark:focus:ring-gray-600"
108 placeholder="Enter value..."
109 /> */}
110 <button
111 onClick={() => setValue(init ?? "")}
112 className="px-6 py-2 h-12 rounded-full bg-gray-100 dark:bg-gray-800
113 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 transition"
114 >
115 Reset
116 </button>
117 </div>
118 </div>
119 );
120}