Skip to content

Commit

Permalink
Refactor stores into reactive classes
Browse files Browse the repository at this point in the history
  • Loading branch information
Mystler committed Dec 20, 2024
1 parent 08869f6 commit ebbfb3f
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 114 deletions.
83 changes: 83 additions & 0 deletions src/lib/audioplayer.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type AudioPlayer from "$lib/components/AudioPlayer.svelte";
import { browser } from "$app/environment";

export interface PlaylistEntry {
id: number;
title: string;
url: string;
}

export class Playlist {
#plSeq = 0;
#entries: PlaylistEntry[] = $state([]);

addSong(url: string, title: string) {
this.#entries = [...this.#entries, { url, title, id: this.#plSeq++ }];
}

length() {
return this.#entries.length;
}

getNext() {
return this.#entries[0];
}

getList() {
return this.#entries;
}

advance() {
this.#entries = this.#entries.slice(1);
}

clear() {
this.#entries = [];
}

remove(index: number) {
this.#entries = [...this.#entries.slice(0, index), ...this.#entries.slice(index + 1)];
}

move(id: number, destIdx: number) {
const elIdx = this.#entries.findIndex((x) => x.id === id);
if (elIdx >= 0) {
const moveItem = this.#entries[elIdx];
const tempList = [...this.#entries.slice(0, elIdx), ...this.#entries.slice(elIdx + 1)];
this.#entries = [...tempList.slice(0, destIdx), moveItem, ...tempList.slice(destIdx)];
}
}

moveUp(index: number) {
if (index === 0) return;
[this.#entries[index - 1], this.#entries[index]] = [
this.#entries[index],
this.#entries[index - 1],
];
}

moveDown(index: number) {
if (index === this.#entries.length - 1) return;
[this.#entries[index + 1], this.#entries[index]] = [
this.#entries[index],
this.#entries[index + 1],
];
}
}

export const GlobalAudio = new (class {
#Volume = $state((browser && localStorage?.audioVolume) ?? 0.5);

Player = $state(null as AudioPlayer | null);
CurrentSong = $state(null as string | null);
Playlist = new Playlist();

set Volume(v: number) {
this.#Volume = v;
if (browser) localStorage.audioVolume = v;
}

get Volume() {
return this.#Volume;
}
})();
24 changes: 0 additions & 24 deletions src/lib/audioplayer.ts

This file was deleted.

17 changes: 5 additions & 12 deletions src/lib/components/AudioCard.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
<script lang="ts">
import {
addSong,
GlobalAudioCurrentSong,
GlobalAudioPlayer,
GlobalPlaylist,
type PlaylistEntry,
} from "$lib/audioplayer";
import { GlobalAudio, type Playlist } from "$lib/audioplayer.svelte";
import { getContext } from "svelte";
import type { Writable } from "svelte/store";
interface Props {
src: string;
Expand All @@ -18,17 +11,17 @@
let { src, title, genre = null }: Props = $props();
// Register our song into our parents list for "play all" tracking
addSong(getContext<Writable<PlaylistEntry[]>>("songs"), src, title);
getContext<Playlist>("songs")?.addSong(src, title);
function play() {
$GlobalAudioPlayer?.playSong(src, title);
GlobalAudio.Player?.playSong(src, title);
}
</script>

<div class="relative">
<button
type="button"
class="audio-card {$GlobalAudioCurrentSong === src ? 'current-song' : ''}"
class="audio-card {GlobalAudio.CurrentSong === src ? 'current-song' : ''}"
onclick={play}
>
<i class="fa fa-play text-xl"></i>
Expand All @@ -43,7 +36,7 @@
aria-label="Add to Queue"
class="absolute -bottom-1 -right-1 size-8 rounded-full bg-sky-900 hover:text-white hover:bg-sky-700"
onclick={(e) => {
addSong(GlobalPlaylist, src, title);
GlobalAudio.Playlist.addSong(src, title);
const button = e.currentTarget;
button.classList.add("animate-ping");
button.disabled = true;
Expand Down
9 changes: 4 additions & 5 deletions src/lib/components/AudioCardList.svelte
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
<script lang="ts">
import { addSong, GlobalPlaylist, type PlaylistEntry } from "$lib/audioplayer";
import { GlobalAudio, Playlist } from "$lib/audioplayer.svelte";
import { setContext } from "svelte";
import { writable } from "svelte/store";
interface Props {
children?: import("svelte").Snippet;
}
let { children }: Props = $props();
// Allow Audio Card elements to register with this context
const listedSongs = setContext("songs", writable<PlaylistEntry[]>([]));
const listedSongs = setContext("songs", new Playlist());
</script>

<button
type="button"
class="p-4 mb-4 rounded-xl bg-zinc-900 text-sky-400 hover:text-white hover:bg-zinc-700"
onclick={(e) => {
for (const song of $listedSongs) {
addSong(GlobalPlaylist, song.url, song.title);
for (const song of listedSongs.getList()) {
GlobalAudio.Playlist.addSong(song.url, song.title);
}
const button = e.currentTarget;
button.classList.add("animate-ping");
Expand Down
28 changes: 14 additions & 14 deletions src/lib/components/AudioPlayer.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
<script lang="ts">
import { GlobalAudioVolume, type PlaylistEntry } from "$lib/audioplayer";
import { writable, type Writable } from "svelte/store";
import { GlobalAudio, Playlist } from "$lib/audioplayer.svelte";
import PlaylistViewer from "./PlaylistViewer.svelte";
interface Props {
playlist?: Writable<PlaylistEntry[]>;
playlist?: Playlist;
onUrlChanged: (url: string | null) => void;
}
let { playlist = writable([]), onUrlChanged }: Props = $props();
let { playlist = new Playlist(), onUrlChanged }: Props = $props();
let url: string | null = $state(null);
let name: string | undefined = $state();
Expand All @@ -30,8 +29,8 @@
let volumeIcon: string = $state("");
$effect(() => {
if (muted) volumeIcon = "fa-volume-xmark";
else if ($GlobalAudioVolume >= 0.5) volumeIcon = "fa-volume-high";
else if ($GlobalAudioVolume >= 0.2) volumeIcon = "fa-volume-low";
else if (GlobalAudio.Volume >= 0.5) volumeIcon = "fa-volume-high";
else if (GlobalAudio.Volume >= 0.2) volumeIcon = "fa-volume-low";
else volumeIcon = "fa-volume-off";
});
Expand All @@ -58,15 +57,16 @@
}
function playNext(now: boolean) {
if ($playlist.length > 0) {
playSong($playlist[0].url, $playlist[0].title, now);
[, ...$playlist] = $playlist;
if (playlist.length() > 0) {
const nextSong = playlist.getNext();
playSong(nextSong.url, nextSong.title, now);
playlist.advance();
}
}
$effect(() => {
// Open player when no song is set but we add to queue.
if (!url && $playlist.length > 0) {
if (!url && playlist.length() > 0) {
playNext(false);
}
});
Expand Down Expand Up @@ -98,7 +98,7 @@
<audio
src={url}
bind:this={audio}
bind:volume={$GlobalAudioVolume}
bind:volume={GlobalAudio.Volume}
bind:muted
bind:currentTime={time}
bind:duration
Expand All @@ -119,7 +119,7 @@
<i class="fa {paused ? 'fa-play' : 'fa-pause'} text-2xl"></i>
</button>

{#if $playlist.length > 0}
{#if playlist.length() > 0}
<!-- Next button -->
<button
type="button"
Expand Down Expand Up @@ -168,7 +168,7 @@
class="slider w-16 sm:w-24"
type="range"
step="any"
bind:value={$GlobalAudioVolume}
bind:value={GlobalAudio.Volume}
min="0"
max="1"
/>
Expand All @@ -194,7 +194,7 @@
class="icon-button"
onclick={() => {
url = null;
playlist.set([]);
playlist.clear();
}}><i class="fa fa-xmark"></i></button
>
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/lib/components/MarkdownContent.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { page } from "$app/state";
import { setPageTitle, setPageDescription } from "$lib/page-meta.svelte";
import { Meta } from "$lib/page-meta.svelte";
interface Props {
title: string;
Expand All @@ -11,8 +11,8 @@
// Set our metadata if we are rendering inside md-pages.
if (page.route.id?.startsWith("/(md-pages)")) {
setPageTitle(title);
setPageDescription(description);
Meta.Title = title;
Meta.Description = description;
}
</script>

Expand Down
Loading

0 comments on commit ebbfb3f

Please sign in to comment.