atmosphere explorer pds.ls
tool typescript atproto
427
fork

Configure Feed

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

at main 96 lines 3.5 kB view raw
1import { createSignal, For } from "solid-js"; 2import { buildScopeString, GRANULAR_SCOPES, scopeIdsToString } from "./scope-utils"; 3 4interface ScopeSelectorProps { 5 onConfirm: (scopeString: string, scopeIds: string) => void; 6 onCancel: () => void; 7 initialScopes?: Set<string>; 8} 9 10export const ScopeSelector = (props: ScopeSelectorProps) => { 11 const [selectedScopes, setSelectedScopes] = createSignal<Set<string>>( 12 props.initialScopes || new Set(["create", "update", "delete", "blob"]), 13 ); 14 15 const isBlobDisabled = () => { 16 const scopes = selectedScopes(); 17 return !scopes.has("create") && !scopes.has("update"); 18 }; 19 20 const toggleScope = (scopeId: string) => { 21 setSelectedScopes((prev) => { 22 const newSet = new Set(prev); 23 if (newSet.has(scopeId)) { 24 newSet.delete(scopeId); 25 if ( 26 (scopeId === "create" || scopeId === "update") && 27 !newSet.has("create") && 28 !newSet.has("update") 29 ) { 30 newSet.delete("blob"); 31 } 32 } else { 33 newSet.add(scopeId); 34 } 35 return newSet; 36 }); 37 }; 38 39 const handleConfirm = () => { 40 const scopes = selectedScopes(); 41 const scopeString = buildScopeString(scopes); 42 const scopeIds = scopeIdsToString(scopes); 43 props.onConfirm(scopeString, scopeIds); 44 }; 45 46 return ( 47 <div class="flex flex-col gap-y-3"> 48 <div class="flex items-center gap-2"> 49 <button 50 onclick={props.onCancel} 51 class="flex items-center rounded-md p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 52 > 53 <span class="iconify lucide--arrow-left"></span> 54 </button> 55 <div class="font-semibold">Select permissions</div> 56 </div> 57 <div class="flex flex-col px-1"> 58 <For each={GRANULAR_SCOPES}> 59 {(scope) => { 60 const isSelected = () => selectedScopes().has(scope.id); 61 const isDisabled = () => scope.id === "blob" && isBlobDisabled(); 62 63 return ( 64 <button 65 onclick={() => !isDisabled() && toggleScope(scope.id)} 66 disabled={isDisabled()} 67 class="group flex items-center gap-3 py-1.5" 68 classList={{ "opacity-50": isDisabled() }} 69 > 70 <div 71 class="flex size-5 items-center justify-center rounded border-2" 72 classList={{ 73 "bg-blue-500 border-transparent group-hover:bg-blue-600 group-active:bg-blue-400": 74 isSelected() && !isDisabled(), 75 "border-neutral-400 dark:border-neutral-500 group-hover:border-neutral-500 dark:group-hover:border-neutral-400 group-hover:bg-neutral-100 dark:group-hover:bg-neutral-800": 76 !isSelected() && !isDisabled(), 77 "border-neutral-300 dark:border-neutral-600": isDisabled(), 78 }} 79 > 80 {isSelected() && <span class="iconify lucide--check text-sm text-white"></span>} 81 </div> 82 <span>{scope.label}</span> 83 </button> 84 ); 85 }} 86 </For> 87 </div> 88 <button 89 onclick={handleConfirm} 90 class="dark:hover:bg-dark-200 dark:active:bg-dark-100 flex w-full items-center justify-center gap-2 rounded-lg border border-neutral-200 px-3 py-2 hover:bg-neutral-100 active:bg-neutral-200 dark:border-neutral-700" 91 > 92 Continue 93 </button> 94 </div> 95 ); 96};