All files / src/lib browse-state.ts

100% Statements 24/24
100% Branches 12/12
100% Functions 6/6
100% Lines 20/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60                    6x     6x                 6x 550x 550x   2x       6x 375x 375x       178x   139x     6x 53x               6x 125x 125x 125x 8x 8x   125x    
// SPDX-License-Identifier: GPL-3.0-only
// Copyright (C) 2026 Max Boksem. See NOTICE for additional terms under GPLv3 section 7.
 
/**
 * Channel-list state that survives the list unmounting while the player is
 * open: the selected tab, the search text, and how much has been watched
 * since the list was last shown (used to decide whether to keep the search
 * when coming back).
 */
 
const KEY = "mvp_browseState";
 
/** Watching for at least this long clears the search when returning to the list. */
export const SEARCH_RESET_AFTER_MS = 60_000;
 
interface StoredBrowseState {
	tab?: string;
	search?: string;
	/** Playback time since the list was last shown; paused and buffering time not included. */
	watchedMs?: number;
}
 
const read = (): StoredBrowseState => {
	try {
		return JSON.parse(sessionStorage.getItem(KEY) ?? "{}") as StoredBrowseState;
	} catch {
		return {};
	}
};
 
const write = (patch: Partial<StoredBrowseState>): void => {
	try {
		sessionStorage.setItem(KEY, JSON.stringify({ ...read(), ...patch }));
	} catch {}
};
 
export const saveBrowseTab = (tab: string): void => write({ tab });
 
export const saveBrowseSearch = (search: string): void => write({ search });
 
/** Called by the player as playback advances. */
export const addWatchedTime = (ms: number): void => {
	if (ms > 0) write({ watchedMs: (read().watchedMs ?? 0) + ms });
};
 
/**
 * Read the state to restore when the channel list opens, and reset the watch
 * time. If at least SEARCH_RESET_AFTER_MS was watched the search is cleared,
 * otherwise it's kept so the user can pick another result.
 */
export const consumeBrowseState = (): { tab?: string; search: string } => {
	const s = read();
	let search = s.search ?? "";
	if (s.watchedMs !== undefined) {
		if (s.watchedMs >= SEARCH_RESET_AFTER_MS) search = "";
		write({ search, watchedMs: undefined });
	}
	return { tab: s.tab, search };
};