Files
Psychotoxical-psysonic/src/utils/folderBrowserHelpers.ts
T
Frank Stellmacher d4d3b0e53f refactor(folder-browser): G.87 — extract column component + 3 hooks (cluster, multi-commit) (#654)
* refactor(folder-browser): G.87.1 — extract helpers + types

Move ColumnKind / NavPos / Column types + entryToAlbumIfPresent /
entryToTrack mappers + isFolderBrowserArrowKey /
folderBrowserHasKeyModifiers key-event helpers into
src/utils/folderBrowserHelpers.ts. Pure code move.

* refactor(folder-browser): G.87.2 — extract FolderBrowserColumn component

* refactor(folder-browser): G.87.3 — extract useFolderBrowserNowPlayingPath hook

* refactor(folder-browser): G.87.4 — extract useFolderBrowserScrolling hook

* refactor(folder-browser): G.87.5 — extract useFolderBrowserKeyboardNav hook
2026-05-13 18:06:47 +02:00

86 lines
2.3 KiB
TypeScript

import type React from 'react';
import type { SubsonicAlbum, SubsonicDirectoryEntry } from '../api/subsonicTypes';
import type { Track } from '../store/playerStoreTypes';
export type ColumnKind = 'roots' | 'indexes' | 'directory';
export type NavPos = { colIndex: number; rowIndex: number };
export type Column = {
id: string;
name: string;
items: SubsonicDirectoryEntry[];
selectedId: string | null;
loading: boolean;
error: boolean;
kind: ColumnKind;
};
/** getMusicDirectory: `albumId` or `album` + row `id` (Navidrome). */
export function entryToAlbumIfPresent(item: SubsonicDirectoryEntry): SubsonicAlbum | null {
if (!item.isDir) return null;
const albumId = item.albumId ?? (item.album ? item.id : undefined);
if (!albumId) return null;
return {
id: albumId,
name: item.album ?? item.title,
artist: item.artist ?? '',
artistId: item.artistId ?? '',
coverArt: item.coverArt,
year: item.year,
genre: item.genre,
starred: item.starred,
userRating: item.userRating,
songCount: 0,
duration: 0,
};
}
export function entryToTrack(e: SubsonicDirectoryEntry): Track {
return {
id: e.id,
title: e.title,
artist: e.artist ?? '',
album: e.album ?? '',
albumId: e.albumId ?? '',
artistId: e.artistId,
coverArt: e.coverArt,
duration: e.duration ?? 0,
track: e.track,
year: e.year,
bitRate: e.bitRate,
suffix: e.suffix,
genre: e.genre,
starred: e.starred,
userRating: e.userRating,
};
}
export function isFolderBrowserArrowKey(e: React.KeyboardEvent): boolean {
return (
e.key === 'ArrowUp' ||
e.key === 'ArrowDown' ||
e.key === 'ArrowLeft' ||
e.key === 'ArrowRight' ||
e.code === 'ArrowUp' ||
e.code === 'ArrowDown' ||
e.code === 'ArrowLeft' ||
e.code === 'ArrowRight'
);
}
/** Modifiers from native event + getModifierState (WebKit/WebView can miss flags on the synthetic event). */
export function folderBrowserHasKeyModifiers(e: React.KeyboardEvent): boolean {
const n = e.nativeEvent;
if (n.ctrlKey || n.altKey || n.shiftKey || n.metaKey) return true;
if (typeof n.getModifierState === 'function') {
return (
n.getModifierState('Control') ||
n.getModifierState('Alt') ||
n.getModifierState('Shift') ||
n.getModifierState('Meta') ||
n.getModifierState('OS')
);
}
return false;
}