feat(tracks): search

This commit is contained in:
2024-11-29 04:28:26 +01:00
parent 1287b71580
commit 1a4bde9618
5 changed files with 37 additions and 18 deletions

BIN
bun.lockb

Binary file not shown.

View File

@@ -38,7 +38,7 @@
"@protobuf-ts/grpcweb-transport": "^2.9.4",
"@protobuf-ts/plugin": "^2.9.4",
"dayjs": "^1.11.13",
"fzf": "^0.5.2",
"fuse.js": "^7.0.0",
"mode-watcher": "^0.5.0"
}
}

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { getCoverUrl } from '$lib/covers';
import type { Track } from '$lib/proto/library';
import AudioLines from 'virtual:icons/lucide/audio-lines';
// import AudioLines from 'virtual:icons/lucide/audio-lines';
interface Props {
track: Track;
@@ -12,16 +12,13 @@
let { track, currentlyPlaying = false, oncontextmenu }: Props = $props();
</script>
<div
class="flex flex-col gap-3"
data-track-hash={track.hash}
oncontextmenu={(e) => oncontextmenu?.(e, track.hash)}
>
<div class="flex flex-col gap-3" data-track-hash={track.hash}>
<div class="relative aspect-square w-full">
<button
type="submit"
class="relative aspect-square h-full w-full overflow-hidden rounded-lg"
formaction="/tracks/{track.hash}?/play"
oncontextmenu={(e) => oncontextmenu?.(e, track.hash)}
>
<img
class="aspect-square h-full w-full transform-gpu transition-all duration-150 hover:scale-105 hover:saturate-150"
@@ -31,9 +28,9 @@
height="640"
/>
</button>
<div class="absolute bottom-6 left-2 size-4 animate-pulse" class:hidden={!currentlyPlaying}>
<!-- <div class="absolute bottom-6 left-2 size-4 animate-pulse" class:hidden={!currentlyPlaying}>
<AudioLines class="text-primary" />
</div>
</div> -->
</div>
<div class="relative space-y-1 text-sm">
<h3 class="font-medium leading-none">{track.name}</h3>

View File

@@ -1,16 +1,29 @@
import { Fzf } from 'fzf';
import Fuse from 'fuse.js';
import type { Track } from './proto/library';
import { getContext, setContext } from 'svelte';
class SearchState {
input = $state('');
#fuse: Fuse<Track> | null = null;
filterTracks(tracks: Track[]) {
const fzf = new Fzf(tracks, {
selector: (t) => `${t.name} ${t.artistName}`,
sort: true
setTracks(tracks: Track[]) {
this.#fuse = new Fuse(tracks, {
keys: ['name', 'artistName'],
shouldSort: true,
includeScore: true,
threshold: 0.2,
findAllMatches: true
});
return fzf.find(this.input);
}
filterTracks() {
if (this.input.length < 1 || !this.#fuse) {
return;
}
const results = this.#fuse.search(this.input);
return results.map((r) => r.item);
}
}

View File

@@ -8,15 +8,22 @@
import Music2 from 'virtual:icons/lucide/music-2';
import ListMusic from 'virtual:icons/lucide/list-music';
import ListEnd from 'virtual:icons/lucide/list-end';
import { getSearchState } from '$lib/search.svelte';
interface Props {
data: PageServerData;
}
const player = getPlayerState();
let { data }: Props = $props();
const player = getPlayerState();
const search = getSearchState();
$effect(() => {
console.log('set tracks');
search.setTracks(data.tracks);
});
let contextMenuTarget = $state<string | null>(null);
const submitPlayTrack: SubmitFunction = async () => {
@@ -53,6 +60,8 @@
function contextMenuItemClicked(e: MouseEvent) {
e.stopPropagation();
}
let filteredTracks = $derived(search.filterTracks());
</script>
<form
@@ -60,7 +69,7 @@
class="grid grid-cols-2 gap-4 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6 xl:grid-cols-7 2xl:grid-cols-8"
use:enhance={submitPlayTrack}
>
{#each data.tracks as track}
{#each filteredTracks ?? data.tracks as track}
<TrackListing
{track}
currentlyPlaying={player.currentlyPlaying?.hash === track.hash}