replies timeline only, appview-less bluesky client
at main 5.0 kB view raw
1<script lang="ts"> 2 import { defaultSettings, needsReload, settings } from '$lib/settings'; 3 import { get } from 'svelte/store'; 4 import ColorPicker from 'svelte-awesome-color-picker'; 5 import Tabs from './Tabs.svelte'; 6 import { portal } from 'svelte-portal'; 7 import { cache } from '$lib/cache'; 8 import { router } from '$lib/state.svelte'; 9 10 interface Props { 11 tab: string; 12 } 13 14 let { tab }: Props = $props(); 15 16 let localSettings = $state(get(settings)); 17 let hasReloadChanges = $derived(needsReload($settings, localSettings)); 18 19 $effect(() => { 20 $settings.theme = localSettings.theme; 21 }); 22 23 const handleSave = () => { 24 settings.set(localSettings); 25 window.location.reload(); 26 }; 27 28 const handleReset = () => { 29 const confirmed = confirm('reset all settings to defaults?'); 30 if (!confirmed) return; 31 settings.reset(); 32 window.location.reload(); 33 }; 34 35 const handleClearCache = () => { 36 cache.clear(); 37 alert('cache cleared!'); 38 }; 39 40 const onTabChange = (tab: string) => router.replace(`/settings/${tab}`); 41</script> 42 43{#snippet advancedTab()} 44 <div class="space-y-3 p-4"> 45 <div> 46 <h3 class="header">api endpoints</h3> 47 <div class="borders space-y-4"> 48 {#snippet _input(name: string, desc: string)} 49 <div> 50 <label for={name} class="header-desc block"> 51 {desc} 52 </label> 53 <input 54 id={name} 55 type="url" 56 bind:value={localSettings.endpoints[name]} 57 placeholder={defaultSettings.endpoints[name]} 58 class="single-line-input" 59 /> 60 </div> 61 {/snippet} 62 {@render _input('slingshot', 'slingshot url (for fetching records & resolving identity)')} 63 {@render _input('spacedust', 'spacedust url (for notifications)')} 64 {@render _input('constellation', 'constellation url (for backlinks)')} 65 {@render _input('jetstream', 'jetstream url (for real-time updates)')} 66 </div> 67 </div> 68 69 <div class="borders"> 70 <label for="social-app-url" class="mb-2 block text-sm font-semibold text-(--nucleus-fg)/80"> 71 social-app url (for when copying links to posts / profiles) 72 </label> 73 <input 74 id="social-app-url" 75 type="url" 76 bind:value={localSettings.socialAppUrl} 77 placeholder={defaultSettings.socialAppUrl} 78 class="single-line-input" 79 /> 80 </div> 81 82 <h3 class="header">cache management</h3> 83 <div class="borders"> 84 <p class="header-desc">clears cached data (records, DID documents, handles, etc.)</p> 85 <button onclick={handleClearCache} class="action-button"> clear cache </button> 86 </div> 87 88 <h3 class="header">reset settings</h3> 89 <div class="borders"> 90 <p class="header-desc">resets all settings to their default values</p> 91 <button 92 onclick={handleReset} 93 class="action-button border-red-600 text-red-600 hover:bg-red-600/20" 94 > 95 reset to defaults 96 </button> 97 </div> 98 </div> 99{/snippet} 100 101{#snippet styleTab()} 102 <div class="space-y-5 p-4"> 103 <div> 104 <h3 class="header">colors</h3> 105 <div class="borders"> 106 {#snippet color(name: string, desc: string)} 107 <div> 108 <label for={name} class="header-desc block"> 109 {desc} 110 </label> 111 <div class="color-picker"> 112 <ColorPicker 113 bind:hex={localSettings.theme[name]} 114 isAlpha={false} 115 position="responsive" 116 label={localSettings.theme[name]} 117 /> 118 </div> 119 </div> 120 {/snippet} 121 {@render color('fg', 'foreground color')} 122 {@render color('bg', 'background color')} 123 {@render color('accent', 'accent color')} 124 {@render color('accent2', 'secondary accent color')} 125 </div> 126 </div> 127 </div> 128{/snippet} 129 130<div class="flex flex-col"> 131 <div class="mb-6 flex items-center justify-between p-4 pb-0"> 132 <div> 133 <h2 class="text-3xl font-bold">settings</h2> 134 <div class="mt-2 flex gap-2"> 135 <div class="h-1 w-8 rounded-full bg-(--nucleus-accent)"></div> 136 <div class="h-1 w-9.5 rounded-full bg-(--nucleus-accent2)"></div> 137 </div> 138 </div> 139 {#if hasReloadChanges} 140 <button onclick={handleSave} class="action-button animate-pulse shadow-lg"> 141 save & reload 142 </button> 143 {/if} 144 </div> 145 146 <div class="flex-1"> 147 {#if tab === 'advanced'} 148 {@render advancedTab()} 149 {:else if tab === 'moderation'} 150 <div class="p-4"> 151 <div class="flex h-64 items-center justify-center"> 152 <div class="text-center"> 153 <div class="mb-4 text-6xl opacity-50">🚧</div> 154 <h3 class="text-xl font-bold opacity-80">todo</h3> 155 </div> 156 </div> 157 </div> 158 {:else if tab === 'style'} 159 {@render styleTab()} 160 {/if} 161 </div> 162 163 <div 164 use:portal={'#footer-portal'} 165 class=" 166 z-20 w-full max-w-2xl bg-(--nucleus-bg) p-4 pt-2 pb-1 shadow-[0_-10px_20px_-5px_rgba(0,0,0,0.1)] 167 " 168 > 169 <Tabs tabs={['style', 'moderation', 'advanced']} activeTab={tab} {onTabChange} /> 170 </div> 171</div> 172 173<style> 174 @reference "../app.css"; 175 .borders { 176 @apply rounded-sm border-2 border-dashed border-(--nucleus-fg)/10 p-4; 177 } 178 .header-desc { 179 @apply mb-2 text-sm text-(--nucleus-fg)/80; 180 } 181 .header { 182 @apply mb-2 text-lg font-bold; 183 } 184</style>