First working POC

This commit is contained in:
2026-01-26 22:21:54 +01:00
commit cfddc1edea
255 changed files with 77606 additions and 0 deletions
+189
View File
@@ -0,0 +1,189 @@
// Queue state store - event-driven view of Rust player queue
//
// This store listens for queue_changed events from the Rust backend
// and provides reactive state for the frontend. All business logic
// (shuffle order, next/previous calculations, etc.) is handled by Rust.
import { writable, derived, get } from "svelte/store";
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import type { MediaItem } from "$lib/api/types";
import { auth } from "$lib/stores/auth";
export type RepeatMode = "off" | "all" | "one";
interface QueueState {
items: MediaItem[];
currentIndex: number | null;
shuffle: boolean;
repeat: RepeatMode;
hasNext: boolean;
hasPrevious: boolean;
}
interface QueueChangedEvent {
items: MediaItem[];
currentIndex: number | null;
shuffle: boolean;
repeat: RepeatMode;
hasNext: boolean;
hasPrevious: boolean;
}
function createQueueStore() {
const initialState: QueueState = {
items: [],
currentIndex: null,
shuffle: false,
repeat: "off",
hasNext: false,
hasPrevious: false,
};
const { subscribe, set } = writable<QueueState>(initialState);
// Listen for queue changed events from Rust backend
let unlisten: (() => void) | null = null;
async function init() {
// Initial sync from backend
await syncFromRust();
// Listen for queue changed events
unlisten = await listen<QueueChangedEvent>("player-event", (event) => {
if ((event.payload as any).type === "queue_changed") {
const queueEvent = event.payload as any;
set({
items: queueEvent.items,
currentIndex: queueEvent.current_index,
shuffle: queueEvent.shuffle,
repeat: queueEvent.repeat,
hasNext: queueEvent.has_next,
hasPrevious: queueEvent.has_previous,
});
}
});
}
/**
* Sync queue state from Rust backend (for initial load)
*/
async function syncFromRust(): Promise<void> {
try {
const rustQueue = await invoke<QueueChangedEvent>("player_get_queue");
console.log("[Queue] Synced from Rust - items:", rustQueue.items.length);
set({
items: rustQueue.items,
currentIndex: rustQueue.currentIndex,
shuffle: rustQueue.shuffle,
repeat: rustQueue.repeat,
hasNext: rustQueue.hasNext,
hasPrevious: rustQueue.hasPrevious,
});
} catch (error) {
console.error("[Queue] Failed to sync from Rust:", error);
}
}
/**
* Clean up event listener
*/
function cleanup() {
if (unlisten) {
unlisten();
unlisten = null;
}
}
// Initialize on creation
init();
// All queue operations now invoke backend commands
// Backend handles all business logic and emits events
async function next() {
await invoke("player_next");
}
async function previous() {
await invoke("player_previous");
}
async function skipTo(index: number) {
await invoke("player_skip_to", { index });
}
async function toggleShuffle() {
await invoke("player_toggle_shuffle");
}
async function cycleRepeat() {
await invoke("player_cycle_repeat");
}
async function removeFromQueue(index: number) {
await invoke("player_remove_from_queue", { index });
}
async function moveInQueue(fromIndex: number, toIndex: number) {
await invoke("player_move_in_queue", { fromIndex, toIndex });
}
async function addToQueue(items: MediaItem | MediaItem[], position: "next" | "end" = "end") {
const toAdd = Array.isArray(items) ? items : [items];
const trackIds = toAdd.map((item) => item.id);
// Get repository handle from auth store
const authState = get(auth);
if (!authState.isAuthenticated || !authState.repository) {
throw new Error("User not authenticated");
}
const repositoryHandle = authState.repository.getHandle();
// Use new Rust commands that accept IDs only
if (trackIds.length === 1) {
await invoke("player_add_track_by_id", {
repositoryHandle,
request: {
trackId: trackIds[0],
position,
},
});
} else {
await invoke("player_add_tracks_by_ids", {
repositoryHandle,
request: {
trackIds,
position,
},
});
}
}
return {
subscribe,
next,
previous,
skipTo,
toggleShuffle,
cycleRepeat,
addToQueue,
removeFromQueue,
moveInQueue,
syncFromRust,
cleanup,
};
}
export const queue = createQueueStore();
// Derived stores for convenience
export const queueItems = derived(queue, ($q) => $q.items);
export const currentQueueIndex = derived(queue, ($q) => $q.currentIndex);
export const currentQueueItem = derived(queue, ($q) =>
$q.currentIndex !== null ? $q.items[$q.currentIndex] : null
);
export const isShuffle = derived(queue, ($q) => $q.shuffle);
export const repeatMode = derived(queue, ($q) => $q.repeat);
export const hasNext = derived(queue, ($q) => $q.hasNext);
export const hasPrevious = derived(queue, ($q) => $q.hasPrevious);