BlueSky & more on desktop lazurite.stormlightlabs.org/
tauri rust typescript bluesky appview atproto solid
2
fork

Configure Feed

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

at main 98 lines 3.5 kB view raw
1import { ActorSuggestionList, useActorSuggestions } from "$/components/actors/ActorSearch"; 2import { ActorTypeaheadLoading } from "$/components/actors/ActorTypeaheadLoading"; 3import { useActorTypeaheadCombobox } from "$/components/actors/hooks/useActorTypeaheadCombobox"; 4import { Icon } from "$/components/shared/Icon"; 5import type { ActorSuggestion } from "$/lib/types"; 6import * as logger from "@tauri-apps/plugin-log"; 7import { createSignal } from "solid-js"; 8import type { ProfileSelection } from "../types"; 9 10export function ProfilePicker(props: { onSubmit: (selection: ProfileSelection) => void }) { 11 let container: HTMLDivElement | undefined; 12 let input: HTMLInputElement | undefined; 13 const [value, setValue] = createSignal(""); 14 const typeahead = useActorSuggestions({ 15 container: () => container, 16 input: () => input, 17 onError: (error) => logger.warn(`Failed to load profile suggestions: ${String(error)}`), 18 value, 19 }); 20 const combobox = useActorTypeaheadCombobox({ 21 ariaControls: "profile-suggestions", 22 onSelect: submitSuggestion, 23 typeahead, 24 }); 25 26 function submitManualActor() { 27 const actor = value().trim(); 28 if (!actor) { 29 return; 30 } 31 32 typeahead.close(); 33 props.onSubmit({ actor }); 34 } 35 36 function submitSuggestion(suggestion: ActorSuggestion) { 37 typeahead.close(); 38 props.onSubmit({ 39 actor: suggestion.handle, 40 did: suggestion.did, 41 displayName: suggestion.displayName ?? null, 42 handle: suggestion.handle, 43 }); 44 } 45 46 return ( 47 <form 48 class="grid gap-3" 49 onSubmit={(event) => { 50 event.preventDefault(); 51 submitManualActor(); 52 }}> 53 <label class="grid gap-1.5"> 54 <span class="text-xs font-medium uppercase tracking-wide text-on-surface-variant">Handle or DID</span> 55 <div 56 class="relative" 57 ref={(element) => { 58 container = element as HTMLDivElement; 59 }}> 60 <input 61 ref={(element) => { 62 input = element; 63 }} 64 type="text" 65 role="combobox" 66 aria-autocomplete="list" 67 aria-controls={combobox.a11y.controls} 68 aria-activedescendant={combobox.a11y.activeDescendant()} 69 aria-expanded={combobox.a11y.expanded()} 70 class="ui-input ui-input-strong w-full rounded-xl px-4 py-2.5 pr-10" 71 placeholder="alice.bsky.social" 72 spellcheck={false} 73 value={value()} 74 onFocus={() => typeahead.focus()} 75 onInput={(event) => setValue(event.currentTarget.value)} 76 onKeyDown={(event) => combobox.handleKeyDown(event)} /> 77 78 <ActorTypeaheadLoading visible={typeahead.loading()} iconClass="animate-spin text-sm" /> 79 <ActorSuggestionList 80 activeIndex={typeahead.activeIndex()} 81 id="profile-suggestions" 82 open={typeahead.open()} 83 suggestions={typeahead.suggestions()} 84 title="Suggested profiles" 85 onSelect={submitSuggestion} /> 86 </div> 87 </label> 88 89 <button 90 type="submit" 91 disabled={!value().trim()} 92 class="flex items-center justify-center gap-2 rounded-xl border-0 bg-primary/15 px-4 py-2.5 text-sm font-medium text-primary transition duration-150 hover:-translate-y-px hover:bg-primary/25 disabled:cursor-not-allowed disabled:opacity-40"> 93 <Icon kind="profile" /> 94 Open profile 95 </button> 96 </form> 97 ); 98}