an independent Bluesky client using Constellation, PDS Queries, and other services reddwarf.app
frontend spa bluesky reddwarf microcosm client app
99
fork

Configure Feed

Select the types of activity you want to include in your feed.

better settings styles

rimar1337 65f60fa0 fc6b4da8

+146 -24
+55 -24
src/routes/settings.tsx
··· 1 1 import { createFileRoute } from "@tanstack/react-router"; 2 2 import { useAtom, useAtomValue, useSetAtom } from "jotai"; 3 3 import { Slider, Switch } from "radix-ui"; 4 - import { useEffect,useState } from "react"; 4 + import { useEffect, useState } from "react"; 5 5 6 6 import { Header } from "~/components/Header"; 7 7 import Login from "~/components/Login"; ··· 40 40 <Login /> 41 41 </div> 42 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> 43 54 <TextInputSetting 44 55 atom={constellationURLAtom} 45 56 title={"Constellation"} ··· 69 80 init={defaultVideoCDN} 70 81 /> 71 82 72 - <Hue /> 83 + <SettingHeading title="Experimental" /> 73 84 <SwitchSetting 74 85 atom={enableBitesAtom} 75 86 title={"Bites"} 76 - description={"Enable Wafrn Bites"} 87 + description={"Enable Wafrn Bites to bite other people"} 77 88 //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 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 81 93 </p> 82 94 </> 83 95 ); 84 96 } 85 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 + 86 117 export function SwitchSetting({ 87 118 atom, 88 119 title, ··· 105 136 } 106 137 107 138 return ( 108 - <div className="flex items-center gap-4 px-4 py-2"> 139 + <div className="flex items-center gap-4 px-4 "> 109 140 <div className="flex flex-col"> 110 - <label htmlFor="switch-demo" className="text-lg"> 141 + <label htmlFor="switch-demo" className="text-md"> 111 142 {title} 112 143 </label> 113 - <span className="text-sm">{description}</span> 144 + <span className="text-sm text-gray-500 dark:text-gray-400"> 145 + {description} 146 + </span> 114 147 </div> 148 + 149 + <div className="flex-1" /> 115 150 116 151 <Switch.Root 117 152 id="switch-demo" 118 153 checked={value} 119 154 onCheckedChange={(v) => setValue(v)} 120 - className="w-10 h-6 bg-gray-300 rounded-full relative data-[state=checked]:bg-blue-500 transition-colors" 155 + className="m3switch root" 121 156 > 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 - /> 157 + <Switch.Thumb className="m3switch thumb " /> 125 158 </Switch.Root> 126 159 </div> 127 160 ); ··· 130 163 function Hue() { 131 164 const [hue, setHue] = useAtom(hueAtom); 132 165 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 - /> 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} /> 140 173 <button 141 174 onClick={() => setHue(defaulthue ?? 28)} 142 175 className="px-6 py-2 h-12 rounded-full bg-gray-100 dark:bg-gray-800 ··· 207 240 ); 208 241 } 209 242 210 - 211 243 interface SliderProps { 212 244 atom: typeof hueAtom; 213 245 min?: number; ··· 221 253 max = 100, 222 254 step = 1, 223 255 }) => { 224 - 225 - const [value, setValue] = useAtom(atom) 256 + const [value, setValue] = useAtom(atom); 226 257 227 258 return ( 228 259 <Slider.Root ··· 239 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" /> 240 271 </Slider.Root> 241 272 ); 242 - }; 273 + };
+91
src/styles/app.css
··· 277 277 } 278 278 } 279 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 + } 280 371 }