tangled
alpha
login
or
join now
whey.party
/
red-dwarf
83
fork
atom
an independent Bluesky client using Constellation, PDS Queries, and other services
reddwarf.app
frontend
spa
bluesky
reddwarf
microcosm
client
app
83
fork
atom
overview
issues
25
pulls
pipelines
better settings styles
rimar1337
4 months ago
65f60fa0
fc6b4da8
+146
-24
2 changed files
expand all
collapse all
unified
split
src
routes
settings.tsx
styles
app.css
+55
-24
src/routes/settings.tsx
···
1
import { createFileRoute } from "@tanstack/react-router";
2
import { useAtom, useAtomValue, useSetAtom } from "jotai";
3
import { Slider, Switch } from "radix-ui";
4
-
import { useEffect,useState } from "react";
5
6
import { Header } from "~/components/Header";
7
import Login from "~/components/Login";
···
40
<Login />
41
</div>
42
<div className="h-4" />
0
0
0
0
0
0
0
0
0
0
0
43
<TextInputSetting
44
atom={constellationURLAtom}
45
title={"Constellation"}
···
69
init={defaultVideoCDN}
70
/>
71
72
-
<Hue />
73
<SwitchSetting
74
atom={enableBitesAtom}
75
title={"Bites"}
76
-
description={"Enable Wafrn Bites"}
77
//init={false}
78
-
/>
79
-
<p className="text-gray-500 dark:text-gray-400 py-4 px-6 text-sm">
80
-
please restart/refresh the app if changes arent applying correctly
0
81
</p>
82
</>
83
);
84
}
85
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
86
export function SwitchSetting({
87
atom,
88
title,
···
105
}
106
107
return (
108
-
<div className="flex items-center gap-4 px-4 py-2">
109
<div className="flex flex-col">
110
-
<label htmlFor="switch-demo" className="text-lg">
111
{title}
112
</label>
113
-
<span className="text-sm">{description}</span>
0
0
114
</div>
0
0
115
116
<Switch.Root
117
id="switch-demo"
118
checked={value}
119
onCheckedChange={(v) => setValue(v)}
120
-
className="w-10 h-6 bg-gray-300 rounded-full relative data-[state=checked]:bg-blue-500 transition-colors"
121
>
122
-
<Switch.Thumb
123
-
className="block w-5 h-5 bg-white rounded-full shadow-sm transition-transform translate-x-[2px] data-[state=checked]:translate-x-[20px]"
124
-
/>
125
</Switch.Root>
126
</div>
127
);
···
130
function Hue() {
131
const [hue, setHue] = useAtom(hueAtom);
132
return (
133
-
<div className="flex flex-col px-4 mt-4 ">
134
-
<span className="z-10">Hue</span>
135
-
<div className="flex flex-row items-center gap-4">
136
-
<SliderComponent
137
-
atom={hueAtom}
138
-
max={360}
139
-
/>
140
<button
141
onClick={() => setHue(defaulthue ?? 28)}
142
className="px-6 py-2 h-12 rounded-full bg-gray-100 dark:bg-gray-800
···
207
);
208
}
209
210
-
211
interface SliderProps {
212
atom: typeof hueAtom;
213
min?: number;
···
221
max = 100,
222
step = 1,
223
}) => {
224
-
225
-
const [value, setValue] = useAtom(atom)
226
227
return (
228
<Slider.Root
···
239
<Slider.Thumb className="shadow-[0_0_0_8px_var(--color-white)] dark:shadow-[0_0_0_8px_var(--color-gray-950)] block w-[3px] h-12 bg-gray-500 dark:bg-gray-400 rounded-md focus:outline-none" />
240
</Slider.Root>
241
);
242
-
};
···
1
import { createFileRoute } from "@tanstack/react-router";
2
import { useAtom, useAtomValue, useSetAtom } from "jotai";
3
import { Slider, Switch } from "radix-ui";
4
+
import { useEffect, useState } from "react";
5
6
import { Header } from "~/components/Header";
7
import Login from "~/components/Login";
···
40
<Login />
41
</div>
42
<div className="h-4" />
43
+
44
+
<SettingHeading title="Personalization" top />
45
+
<Hue />
46
+
47
+
<SettingHeading title="Network Configuration" />
48
+
<div className="flex flex-col px-4 pb-2">
49
+
<span className="text-md">Service Endpoints</span>
50
+
<span className="text-sm text-gray-500 dark:text-gray-400">
51
+
Customize the servers to be used by the app
52
+
</span>
53
+
</div>
54
<TextInputSetting
55
atom={constellationURLAtom}
56
title={"Constellation"}
···
80
init={defaultVideoCDN}
81
/>
82
83
+
<SettingHeading title="Experimental" />
84
<SwitchSetting
85
atom={enableBitesAtom}
86
title={"Bites"}
87
+
description={"Enable Wafrn Bites to bite other people"}
88
//init={false}
89
+
/>
90
+
<p className="text-gray-500 dark:text-gray-400 py-4 px-4 text-sm border rounded-xl mx-4 mt-8 mb-4">
91
+
Notice: Please restart/refresh the app if changes arent applying
92
+
correctly
93
</p>
94
</>
95
);
96
}
97
98
+
export function SettingHeading({
99
+
title,
100
+
top,
101
+
}: {
102
+
title: string;
103
+
top?: boolean;
104
+
}) {
105
+
return (
106
+
<div
107
+
className="px-4"
108
+
style={{ marginTop: top ? 0 : 18, paddingBottom: 12 }}
109
+
>
110
+
<span className=" text-sm font-medium text-gray-500 dark:text-gray-400">
111
+
{title}
112
+
</span>
113
+
</div>
114
+
);
115
+
}
116
+
117
export function SwitchSetting({
118
atom,
119
title,
···
136
}
137
138
return (
139
+
<div className="flex items-center gap-4 px-4 ">
140
<div className="flex flex-col">
141
+
<label htmlFor="switch-demo" className="text-md">
142
{title}
143
</label>
144
+
<span className="text-sm text-gray-500 dark:text-gray-400">
145
+
{description}
146
+
</span>
147
</div>
148
+
149
+
<div className="flex-1" />
150
151
<Switch.Root
152
id="switch-demo"
153
checked={value}
154
onCheckedChange={(v) => setValue(v)}
155
+
className="m3switch root"
156
>
157
+
<Switch.Thumb className="m3switch thumb " />
0
0
158
</Switch.Root>
159
</div>
160
);
···
163
function Hue() {
164
const [hue, setHue] = useAtom(hueAtom);
165
return (
166
+
<div className="flex flex-col px-4">
167
+
<span className="z-[2] text-md">Hue</span>
168
+
<span className="z-[2] text-sm text-gray-500 dark:text-gray-400">
169
+
Change the colors of the app
170
+
</span>
171
+
<div className="z-[1] flex flex-row items-center gap-4">
172
+
<SliderComponent atom={hueAtom} max={360} />
173
<button
174
onClick={() => setHue(defaulthue ?? 28)}
175
className="px-6 py-2 h-12 rounded-full bg-gray-100 dark:bg-gray-800
···
240
);
241
}
242
0
243
interface SliderProps {
244
atom: typeof hueAtom;
245
min?: number;
···
253
max = 100,
254
step = 1,
255
}) => {
256
+
const [value, setValue] = useAtom(atom);
0
257
258
return (
259
<Slider.Root
···
270
<Slider.Thumb className="shadow-[0_0_0_8px_var(--color-white)] dark:shadow-[0_0_0_8px_var(--color-gray-950)] block w-[3px] h-12 bg-gray-500 dark:bg-gray-400 rounded-md focus:outline-none" />
271
</Slider.Root>
272
);
273
+
};
+91
src/styles/app.css
···
277
}
278
}
279
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
280
}
···
277
}
278
}
279
}
280
+
}
281
+
282
+
:root{
283
+
--thumb-size: 2rem;
284
+
--root-size: 3.25rem;
285
+
286
+
--switch-off-border: var(--color-gray-400);
287
+
--switch-off-bg: var(--color-gray-200);
288
+
--switch-off-thumb: var(--color-gray-400);
289
+
290
+
291
+
--switch-on-bg: var(--color-gray-500);
292
+
--switch-on-thumb: var(--color-gray-50);
293
+
294
+
}
295
+
@media (prefers-color-scheme: dark) {
296
+
:root {
297
+
--switch-off-border: var(--color-gray-500);
298
+
--switch-off-bg: var(--color-gray-800);
299
+
--switch-off-thumb: var(--color-gray-500);
300
+
301
+
302
+
--switch-on-bg: var(--color-gray-400);
303
+
--switch-on-thumb: var(--color-gray-700);
304
+
}
305
+
}
306
+
307
+
.m3switch.root{
308
+
/*w-10 h-6 bg-gray-300 rounded-full relative data-[state=checked]:bg-gray-500 transition-colors*/
309
+
/*width: 40px;
310
+
height: 24px;*/
311
+
312
+
inline-size: var(--root-size);
313
+
block-size: 2rem;
314
+
border-radius: 99999px;
315
+
316
+
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
317
+
transition-timing-function: var(--default-transition-timing-function); /* cubic-bezier(0.4, 0, 0.2, 1) */
318
+
transition-duration: var(--default-transition-duration); /* 150ms */
319
+
320
+
.m3switch.thumb{
321
+
/*block w-5 h-5 bg-white rounded-full shadow-sm transition-transform translate-x-[2px] data-[state=checked]:translate-x-[20px]*/
322
+
323
+
height: var(--thumb-size);
324
+
width: var(--thumb-size);
325
+
display: inline-block;
326
+
border-radius: 9999px;
327
+
328
+
transform-origin: center;
329
+
330
+
transition-property: transform, translate, scale, rotate;
331
+
transition-timing-function: var(--default-transition-timing-function); /* cubic-bezier(0.4, 0, 0.2, 1) */
332
+
transition-duration: var(--default-transition-duration); /* 150ms */
333
+
334
+
}
335
+
336
+
&[aria-checked="true"] {
337
+
box-shadow: inset 0px 0px 0px 1.8px transparent;
338
+
background-color: var(--switch-on-bg);
339
+
340
+
.m3switch.thumb{
341
+
/*block w-5 h-5 bg-white rounded-full shadow-sm transition-transform translate-x-[2px] data-[state=checked]:translate-x-[20px]*/
342
+
343
+
background-color: var(--switch-on-thumb);
344
+
transform: translate(calc((var(--root-size) / 2) - 50%),0) scale(0.72);
345
+
&:active {
346
+
transform: translate(calc((var(--root-size) / 2) - 50%),0) scale(0.88);
347
+
}
348
+
349
+
}
350
+
&:active .m3switch.thumb{
351
+
transform: translate(calc((var(--root-size) / 2) - 50%),0) scale(0.88);
352
+
}
353
+
}
354
+
355
+
&[aria-checked="false"] {
356
+
box-shadow: inset 0px 0px 0px 1.8px var(--switch-off-border);
357
+
background-color: var(--switch-off-bg);
358
+
.m3switch.thumb{
359
+
/*block w-5 h-5 bg-white rounded-full shadow-sm transition-transform translate-x-[2px] data-[state=checked]:translate-x-[20px]*/
360
+
361
+
background-color: var(--switch-off-thumb);
362
+
transform: translate(calc(-1 * (var(--root-size) / 2) + 50%),0) scale(0.5);
363
+
&:active {
364
+
transform: translate(calc(-1 * (var(--root-size) / 2) + 50%),0) scale(0.88);
365
+
}
366
+
}
367
+
&:active .m3switch.thumb{
368
+
transform: translate(calc(-1 * (var(--root-size) / 2) + 50%),0) scale(0.88);
369
+
}
370
+
}
371
}