fix: address reader-UI feedback (shorts, search, tags, login, UX)
- Shorts: confirm via youtube.com/shorts/<id> probe (SOCS cookie bypasses the consent redirect) instead of a 60s heuristic; concurrent probing, shorts_probed flag, scheduled refinement (migration 0005) - Search: match title + channel name only (descriptions caused noisy results) - Faceted tag filtering: AND across categories (language AND topic narrows), OR within a category; any/all toggle applies to topics - Language detection: majority vote over individual titles (fixes misdetections like multipoleguy -> English; drops bogus Polish/Romanian) - Login: drop forced consent so returning sign-in is quick (select_account) - Feed cards: clickable channel name (opens channel), persistent saved badge, undo toast on hide, Hidden view to restore; tag chips show counts in tooltip
This commit is contained in:
parent
e56502789f
commit
8c245e986f
17 changed files with 306 additions and 35 deletions
|
|
@ -22,6 +22,7 @@ export interface Video {
|
|||
channel_id: string;
|
||||
channel_title: string | null;
|
||||
channel_thumbnail: string | null;
|
||||
channel_url: string;
|
||||
published_at: string | null;
|
||||
thumbnail_url: string | null;
|
||||
duration_seconds: number | null;
|
||||
|
|
|
|||
41
frontend/src/lib/toast.ts
Normal file
41
frontend/src/lib/toast.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
export interface ToastAction {
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
}
|
||||
export interface ToastItem {
|
||||
id: number;
|
||||
message: string;
|
||||
action?: ToastAction;
|
||||
}
|
||||
|
||||
let items: ToastItem[] = [];
|
||||
let listeners: Array<() => void> = [];
|
||||
let counter = 1;
|
||||
|
||||
function emit() {
|
||||
listeners.forEach((l) => l());
|
||||
}
|
||||
|
||||
export function toast(message: string, action?: ToastAction): number {
|
||||
const id = counter++;
|
||||
items = [...items, { id, message, action }];
|
||||
emit();
|
||||
setTimeout(() => dismiss(id), 6000);
|
||||
return id;
|
||||
}
|
||||
|
||||
export function dismiss(id: number) {
|
||||
items = items.filter((i) => i.id !== id);
|
||||
emit();
|
||||
}
|
||||
|
||||
export function getToasts(): ToastItem[] {
|
||||
return items;
|
||||
}
|
||||
|
||||
export function subscribe(listener: () => void): () => void {
|
||||
listeners.push(listener);
|
||||
return () => {
|
||||
listeners = listeners.filter((l) => l !== listener);
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue