File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 77 import { validatePassword } from ' $lib/inputvalidation/passwordValidator' ;
88 import type { AnyComponent } from ' $lib/types/AnyComponent' ;
99 import type { ValidationResult } from ' $lib/types/ValidationResult' ;
10- import { debounce } from ' $lib/utils/debounce' ;
10+ import { useDebounce } from ' $lib/utils/debounce' ;
1111 import type { Snippet } from ' svelte' ;
1212 import type { FullAutoFill } from ' svelte/elements' ;
1313 import PasswordStrengthMeter from ' ./impl/PasswordStrengthMeter.svelte' ;
4242
4343 let validationResult = $state <ValidationResult | null >(null );
4444
45- const requestHIBP = debounce (async (str : string ) => {
45+ const requestHIBP = useDebounce (async (str : string ) => {
4646 try {
4747 const pwnedCount = await checkPwnedCount (str );
4848 if (pwnedCount > 0 ) {
Original file line number Diff line number Diff line change 1111 } from ' $lib/inputvalidation/usernameValidator' ;
1212 import type { AnyComponent } from ' $lib/types/AnyComponent' ;
1313 import type { ValidationResult } from ' $lib/types/ValidationResult' ;
14- import { debounce } from ' $lib/utils/debounce' ;
14+ import { useDebounce } from ' $lib/utils/debounce' ;
1515 import type { Snippet } from ' svelte' ;
1616 import type { FullAutoFill } from ' svelte/elements' ;
1717
4040 let checkResponses = $state <Map <string , ValidationResult >>(new Map ());
4141 let validationResult = $state <ValidationResult | null >(null );
4242
43- const requestAvailability = debounce (async (username : string ) => {
43+ const requestAvailability = useDebounce (async (username : string ) => {
4444 try {
4545 const response = await accountV2Api .accountCheckUsername ({ username });
4646 validationResult = mapUsernameCheckResponse (response );
Original file line number Diff line number Diff line change 11import { browser } from '$app/environment' ;
2+ import { onDestroy } from 'svelte' ;
23
34export type StorageType = 'local' | 'session' ;
45
@@ -39,6 +40,13 @@ export class PersistedState<T> {
3940 }
4041 }
4142
43+ /** Detach the cross-tab `storage` listener. No-op if none was attached. */
44+ dispose ( ) {
45+ if ( this . #storage === localStorage ) {
46+ if ( browser ) window . removeEventListener ( 'storage' , this . #onStorage) ;
47+ }
48+ }
49+
4250 #onStorage = ( event : StorageEvent ) => {
4351 if ( event . storageArea !== this . #storage) return ;
4452 if ( event . key !== this . #key) return ;
@@ -75,3 +83,14 @@ export class PersistedState<T> {
7583 return JSON . parse ( raw ) as T ;
7684 }
7785}
86+
87+ /** Component-aware `PersistedState` that detaches the storage listener on unmount. */
88+ export function usePersistedState < T > (
89+ key : string ,
90+ defaultValue : T ,
91+ options ?: LocalStorageStateOptions
92+ ) : PersistedState < T > {
93+ const state = new PersistedState ( key , defaultValue , options ) ;
94+ onDestroy ( ( ) => state . dispose ( ) ) ;
95+ return state ;
96+ }
Original file line number Diff line number Diff line change 1+ import { onDestroy } from 'svelte' ;
2+
13export interface Debounced < Args extends unknown [ ] > {
24 ( ...args : Args ) : void ;
35 cancel ( ) : void ;
@@ -39,3 +41,13 @@ export function debounce<Args extends unknown[]>(
3941
4042 return debounced ;
4143}
44+
45+ /** Component-aware `debounce` that cancels any pending invocation on unmount. */
46+ export function useDebounce < Args extends unknown [ ] > (
47+ fn : ( ...args : Args ) => void ,
48+ delay : number
49+ ) : Debounced < Args > {
50+ const d = debounce ( fn , delay ) ;
51+ onDestroy ( d . cancel ) ;
52+ return d ;
53+ }
Original file line number Diff line number Diff line change 1515 import { Separator } from ' $lib/components/ui/separator' ;
1616 import { handleApiError } from ' $lib/errorhandling/apiErrorHandling' ;
1717 import type { ValidationResult } from ' $lib/types/ValidationResult' ;
18- import { debounce } from ' $lib/utils/debounce' ;
18+ import { useDebounce } from ' $lib/utils/debounce' ;
1919
2020 registerBreadcrumbs (() => [{ label: ' Blacklists' }]);
2121
139139 }
140140 }
141141
142- const debouncedLoadUsernames = debounce (loadUsernames , 400 );
142+ const debouncedLoadUsernames = useDebounce (loadUsernames , 400 );
143143 $effect (() => {
144144 if (usernameEntry .length == 0 ) {
145145 debouncedLoadUsernames .cancel ();
150150 debouncedLoadUsernames ();
151151 });
152152
153- const debouncedLoadEmails = debounce (loadEmails , 400 );
153+ const debouncedLoadEmails = useDebounce (loadEmails , 400 );
154154 $effect (() => {
155155 if (emailEntry .length == 0 ) {
156156 debouncedLoadEmails .cancel ();
Original file line number Diff line number Diff line change 8585 import { CardHeader , CardTitle } from ' $lib/components/ui/card' ;
8686 import { Input } from ' $lib/components/ui/input' ;
8787 import { handleApiError } from ' $lib/errorhandling/apiErrorHandling' ;
88- import { debounce } from ' $lib/utils/debounce' ;
88+ import { useDebounce } from ' $lib/utils/debounce' ;
8989
9090 let isFetching = $state (false );
9191
117117 page = Math .floor (response .offset / response .limit ) + 1 ;
118118 }
119119
120- const applyFilterQuery = debounce ((query : string | undefined ) => (filterQuery = query ), 800 );
120+ const applyFilterQuery = useDebounce ((query : string | undefined ) => (filterQuery = query ), 800 );
121121 $effect (() => {
122122 const queries: string [] = [];
123123
Original file line number Diff line number Diff line change 1111 import Sidebar from ' ./Sidebar.svelte' ;
1212 import ' ../app.css' ;
1313 import { IsMobile } from ' $lib/hooks/is-mobile.svelte' ;
14- import { PersistedState } from ' $lib/state/classes/persisted-state.svelte' ;
14+ import { usePersistedState } from ' $lib/state/classes/persisted-state.svelte' ;
1515 import DialogManager from ' $lib/components/dialog-manager/dialog-manager.svelte' ;
1616
1717 interface Props {
2323 let meta = $derived (buildMetaData (page .url ));
2424
2525 const mobile = new IsMobile ();
26- const sidebarOpen = new PersistedState (' sidebarOpen' , false );
26+ const sidebarOpen = usePersistedState (' sidebarOpen' , false );
2727 const isOpen = $derived (mobile .current ? false : sidebarOpen .value );
2828 </script >
2929
You can’t perform that action at this time.
0 commit comments