Major Features: - Multi-source download system (Soulseek/Lidarr with fallback) - Configurable enrichment speed control (1-5x) - Mobile touch drag support for seek sliders - iOS PWA media controls (Control Center, Lock Screen) - Artist name alias resolution via Last.fm - Circuit breaker pattern for audio analysis Critical Fixes: - Audio analyzer stability (non-ASCII, BrokenProcessPool, OOM) - Discovery system race conditions and import failures - Radio decade categorization using originalYear - LastFM API response normalization - Mood bucket infinite loop prevention Security: - Bull Board admin authentication - Lidarr webhook signature verification - JWT token expiration and refresh - Encryption key validation on startup Closes #2, #6, #9, #13, #21, #26, #31, #34, #35, #37, #40, #43
66 lines
1.8 KiB
TypeScript
66 lines
1.8 KiB
TypeScript
/**
|
|
* Query Events - Typed event system for cache invalidation
|
|
*
|
|
* Provides a type-safe wrapper around CustomEvent for triggering React Query cache invalidation.
|
|
* Events are dispatched when data changes and listeners refetch queries to update the UI.
|
|
*/
|
|
|
|
/**
|
|
* Available query event types
|
|
*/
|
|
export type QueryEventType =
|
|
| "audiobook-progress-updated"
|
|
| "podcast-progress-updated"
|
|
| "library-updated"
|
|
| "mixes-updated"; // Include existing event for consistency
|
|
|
|
/**
|
|
* Event payload interface - can be extended for event-specific data
|
|
*/
|
|
export interface QueryEventDetail {
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
/**
|
|
* Dispatch a typed query event
|
|
*
|
|
* @param eventType - The type of event to dispatch
|
|
* @param detail - Optional event payload data
|
|
*
|
|
* @example
|
|
* dispatchQueryEvent("audiobook-progress-updated", { audiobookId: "123" });
|
|
*/
|
|
export function dispatchQueryEvent(
|
|
eventType: QueryEventType,
|
|
detail?: QueryEventDetail
|
|
): void {
|
|
window.dispatchEvent(
|
|
new CustomEvent(eventType, { detail: detail || {} })
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Subscribe to a typed query event
|
|
*
|
|
* @param eventType - The type of event to listen for
|
|
* @param handler - Callback function to execute when event fires
|
|
* @returns Cleanup function to remove the event listener
|
|
*
|
|
* @example
|
|
* const unsubscribe = subscribeQueryEvent("audiobook-progress-updated", () => {
|
|
* queryClient.refetchQueries({ queryKey: ["audiobook", id] });
|
|
* });
|
|
* // Later: unsubscribe();
|
|
*/
|
|
export function subscribeQueryEvent(
|
|
eventType: QueryEventType,
|
|
handler: (event: CustomEvent<QueryEventDetail>) => void
|
|
): () => void {
|
|
const listener = handler as EventListener;
|
|
window.addEventListener(eventType, listener);
|
|
|
|
return () => {
|
|
window.removeEventListener(eventType, listener);
|
|
};
|
|
}
|