beatufitull front end for ozone modration ,, wit catpucoin and ebergarden !
0
fork

Configure Feed

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

woag login autogil

+106 -10
+1 -1
src/components/Advertisement.svelte
··· 23 23 > 24 24 </iframe> 25 25 <button 26 - class="absolute top-0 right-0 cursor-pointer bg-black text-ctp-subtext0 hover:bg-ctp-surface0" 26 + class="absolute top-0 right-0 cursor-pointer bg-ctp-crust text-ctp-subtext0 hover:bg-ctp-surface0" 27 27 onclick={() => { 28 28 ads.toggle(false); 29 29 }}
+105 -9
src/routes/login/+page.svelte
··· 5 5 import { Agent, AtpAgent } from '@atproto/api'; 6 6 import Button from '../../components/ui/Button.svelte'; 7 7 import Input from '../../components/ui/Input.svelte'; 8 - import { CatIcon, OrbitIcon, XIcon } from 'lucide-svelte'; 8 + import { CatIcon, OrbitIcon } from 'lucide-svelte'; 9 9 import Advertisement from '$components/Advertisement.svelte'; 10 + import type { ProfileView } from '@atproto/api/dist/client/types/app/bsky/actor/defs'; 10 11 11 12 let handleInput = $state(''); 12 13 let passwordInput = $state(''); 13 14 let aliemMode = $state(false); 15 + let suggestions = $state<ProfileView[]>([]); 16 + let suggestionsOpen = $state(false); 17 + let suggestionsLoading = $state(false); 18 + let suggestionsRequestId = 0; 19 + let suggestionsDebounce: ReturnType<typeof setTimeout> | null = null; 14 20 const authMethod = getAuthenticationMethod(); 15 21 const PASSWORD_SESSION_KEY = 'meowzone-password-session'; 16 22 ··· 68 74 69 75 function handleInputChange(e: Event) { 70 76 const target = e.target as HTMLInputElement; 71 - if (target.value == 'koi.rip' || target.value == 'pci.express') { 77 + const nextValue = target.value; 78 + if (nextValue == 'koi.rip' || nextValue == 'pci.express') { 72 79 aliemMode = true; 73 80 } else { 74 81 aliemMode = false; 75 82 } 76 - handleInput = target.value; 83 + handleInput = nextValue; 84 + scheduleSuggestions(nextValue); 77 85 if ($error) error.set(null); 78 86 } 79 87 88 + function scheduleSuggestions(term: string) { 89 + if (suggestionsDebounce) clearTimeout(suggestionsDebounce); 90 + 91 + const trimmed = term.trim(); 92 + if (!trimmed || trimmed.length < 2 || trimmed.startsWith('did:')) { 93 + suggestions = []; 94 + suggestionsOpen = false; 95 + suggestionsLoading = false; 96 + return; 97 + } 98 + 99 + suggestionsDebounce = setTimeout(() => { 100 + void loadSuggestions(trimmed); 101 + }, 200); 102 + } 103 + 104 + async function loadSuggestions(term: string) { 105 + const requestId = ++suggestionsRequestId; 106 + suggestionsLoading = true; 107 + suggestionsOpen = true; 108 + const nextSuggestions = await fetchSuggestions(term); 109 + if (requestId !== suggestionsRequestId) return; 110 + suggestions = nextSuggestions; 111 + suggestionsOpen = true; 112 + suggestionsLoading = false; 113 + } 114 + 115 + function handleHandleBlur() { 116 + setTimeout(() => { 117 + suggestionsOpen = false; 118 + }, 120); 119 + } 120 + 121 + function onSelectSuggestion(value: string) { 122 + handleInput = value; 123 + suggestionsOpen = false; 124 + suggestions = []; 125 + if (value == 'koi.rip' || value == 'pci.express') { 126 + aliemMode = true; 127 + } else { 128 + aliemMode = false; 129 + } 130 + } 131 + 80 132 function handlePasswordInputChange(e: Event) { 81 133 const target = e.target as HTMLInputElement; 82 134 passwordInput = target.value; ··· 88 140 handleLogin(); 89 141 } 90 142 } 143 + 144 + async function fetchSuggestions(term: string) { 145 + const publicAgent = new AtpAgent({ service: 'https://public.api.bsky.app' }); 146 + try { 147 + const res = await publicAgent.searchActors({ term, limit: 5 }); 148 + return res.data.actors; 149 + } catch (err) { 150 + console.error('Failed to fetch suggestions from public API:', err); 151 + return []; 152 + } 153 + } 91 154 </script> 92 155 93 156 <div class="flex min-h-screen items-center justify-center"> ··· 107 170 </p> 108 171 109 172 <div class="space-y-4"> 110 - <div> 173 + <div class="relative"> 111 174 <Input 112 175 id="handle" 113 176 type="text" 114 177 placeholder="ur-habdle.ebil.club" 115 178 bind:value={handleInput} 116 - on:input={handleInputChange} 117 - on:keypress={handleKeyPress} 179 + oninput={handleInputChange} 180 + onblur={handleHandleBlur} 181 + onfocus={() => (suggestionsOpen = suggestions.length > 0)} 182 + onkeypress={handleKeyPress} 118 183 disabled={$isLoading} 119 184 className="mt-1" 120 185 /> 186 + {#if suggestionsOpen && handleInput.trim().length >= 2 && !handleInput 187 + .trim() 188 + .startsWith('did:')} 189 + <div 190 + class="absolute z-20 mt-1 w-full overflow-hidden rounded-lg border border-ctp-surface1 bg-ctp-base shadow-lg" 191 + > 192 + {#if suggestionsLoading} 193 + <p class="px-3 py-2 text-sm text-ctp-subtext1">loading suggestions...</p> 194 + {:else if suggestions.length === 0} 195 + <p class="px-3 py-2 text-sm text-ctp-subtext1">no suggestions found</p> 196 + {:else} 197 + {#each suggestions as suggestion} 198 + <button 199 + type="button" 200 + class="block w-full cursor-pointer px-3 py-2 text-left text-sm text-ctp-text hover:bg-ctp-surface0" 201 + onmousedown={(event) => event.preventDefault()} 202 + onclick={() => onSelectSuggestion(suggestion.handle)} 203 + > 204 + <div class="flex items-center"> 205 + <img 206 + src={suggestion.avatar} 207 + alt={suggestion.handle} 208 + class="mr-2 h-6 w-6 rounded-full" 209 + /> 210 + <p>@{suggestion.handle}</p> 211 + </div> 212 + </button> 213 + {/each} 214 + {/if} 215 + </div> 216 + {/if} 121 217 </div> 122 218 123 219 {#if authMethod === 'password'} ··· 127 223 type="password" 128 224 placeholder={aliemMode ? 'ᔑ!¡!¡ !¡ᔑᓭᓭ∴𝙹∷↸' : 'app password'} 129 225 bind:value={passwordInput} 130 - on:input={handlePasswordInputChange} 131 - on:keypress={handleKeyPress} 226 + oninput={handlePasswordInputChange} 227 + onkeypress={handleKeyPress} 132 228 disabled={$isLoading} 133 229 className="mt-1" 134 230 /> ··· 154 250 <Button 155 251 variant="primary" 156 252 fullWidth={true} 157 - on:click={handleLogin} 253 + onclick={handleLogin} 158 254 disabled={$isLoading || 159 255 !handleInput.trim() || 160 256 (authMethod === 'password' && !passwordInput.trim())}