feat: playlist loading

This commit is contained in:
2026-09-07 16:15:31 +02:00
parent a5910df441
commit 867374fa47
19 changed files with 411 additions and 47 deletions
+4 -4
View File
@@ -1,11 +1,10 @@
<script setup lang="ts">
import AssociationView from '@/composables/AssociationView.vue';
import PlayerComponent from './PlayerComponent.vue';
import SmallPlayerComponent from './SmallPlayerComponent.vue';
import {
ref
} from 'vue';
const fullPlayer = ref( true );
fullPlayer
} from '@/ts/player/state';
const close = () => {
fullPlayer.value = false;
@@ -14,6 +13,7 @@
<template>
<div class="player-wrapper">
<AssociationView />
<SmallPlayerComponent v-model="fullPlayer" />
<div :class="['player-container', fullPlayer ? undefined : 'hidden']">
<i class="fa-solid fa-xmark" @click="close"></i>
@@ -1,5 +1,11 @@
<script setup lang="ts">
import UserPlaylists from '../playlists/UserPlaylists.vue';
</script>
<template>
<div>
<h1>Playlists</h1>
<UserPlaylists />
</div>
</template>
+4 -1
View File
@@ -18,6 +18,9 @@
import {
queueIdx
} from '@/ts/player/state';
import {
savePlaylist
} from '@/ts/userPlaylists/save';
const queue: WritableComputedRef<Song[]> = computed( {
get () {
@@ -56,7 +59,7 @@
<i class="fa-solid fa-xmark"></i>
Clear
</button>
<button>
<button @click="savePlaylist">
<i class="fa-solid fa-save"></i>
Save
</button>
@@ -0,0 +1,86 @@
<script setup lang="ts">
import PopupElement from '../popups/PopupElement.vue';
import {
ref
} from 'vue';
const showPopup = defineModel<boolean>( {
'required': true
} );
const addPlaylist = () => {
showPopup.value = false;
emit( 'add-playlist', playlistName.value );
};
const playlistName = ref( '' );
const emit = defineEmits<{
( e: 'add-playlist', name: string ): void;
}>();
</script>
<template>
<div>
<PopupElement v-model="showPopup" show-close>
<div class="title">
<h1>Add Playlist</h1>
</div>
<input v-model="playlistName" type="text" placeholder="Playlist name">
<br>
<button @click="addPlaylist">
<i class="fa-solid fa-plus"></i>
Add Playlist
</button>
</PopupElement>
</div>
</template>
<style lang="scss" scoped>
.title {
>h1 {
margin-bottom: 5px;
}
>p {
margin: 0;
margin-bottom: 10px;
}
}
.song-sources {
display: flex;
flex-wrap: wrap;
width: 50vw;
height: 40vh;
justify-content: center;
overflow-y: scroll;
overflow-x: hidden;
>div {
width: 45%;
margin: 0.5%;
height: 60%;
background-color: var(--accent-background);
border-radius: 20px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
flex-direction: column;
>div {
display: flex;
justify-content: center;
align-items: center;
>.fa-solid {
font-size: 1.5rem;
}
}
>.not-auth-notice {
font-size: 0.6rem;
margin: 0;
width: 70%;
}
}
}
</style>
@@ -0,0 +1,112 @@
<script setup lang="ts">
import {
addPlaylist,
removePlaylist
} from '@/ts/userPlaylists';
import {
editingPlaylists,
playlistIdx as playlistIdx,
playlists
} from '@/ts/userPlaylists/state';
import {
getPlaylists,
savePlaylists
} from '@/ts/userPlaylists/save';
import {
onMounted,
ref
} from 'vue';
import AddPlaylist from './AddPlaylist.vue';
import {
UnownedError
} from '@/ts/request';
import player from '@/ts/player';
import router from '@/router';
const checkingStatus = ref( true );
const dots = ref( 0 );
const showAddPlaylist = ref( false );
let interval = -1;
const loadPlaylists = async () => {
if ( interval < 0 ) {
interval = setInterval( () => {
dots.value = ( dots.value + 1 ) % 4;
}, 500 );
}
try {
await getPlaylists();
} catch ( e ) {
if ( e instanceof UnownedError ) {
router.push( '/get' );
}
}
checkingStatus.value = false;
try {
clearInterval( interval );
interval = -1;
} catch { /* Empty */ }
};
onMounted( () => {
loadPlaylists();
} );
onMounted( () => {} );
const openAddPlaylistPopup = () => {
showAddPlaylist.value = true;
};
const togglePlaylistEditing = ( idx: number ) => {
editingPlaylists.value[ idx ] = !editingPlaylists.value[ idx ];
};
const selectPlaylist = ( idx: number ) => {
playlistIdx.value = idx;
player.clearQueue();
player.loadPlaylist( playlists.value[ idx ]!.songs );
};
</script>
<template>
<div class="playlists">
<AddPlaylist v-model="showAddPlaylist" @add-playlist="addPlaylist" />
<div v-if="checkingStatus">
Loading{{ '.'.repeat( dots ) }}
</div>
<div v-else-if="playlists.length === 0">
No playlists
<button @click="openAddPlaylistPopup">
<i class="fa-solid fa-plus"></i>
Add one
</button>
</div>
<div v-else>
<button @click="openAddPlaylistPopup">
<i class="fa-solid fa-plus"></i>
Add Playlist
</button>
<button @click="savePlaylists">
<i class="fa-solid fa-floppy-disk"></i>
Save Changes
</button>
<button @click="loadPlaylists">
<i class="fa-solid fa-rotate"></i>
Undo unsaved changes
</button>
<div v-for="(playlist, index) in playlists" :key="index">
<input v-if="editingPlaylists[ index ]" v-model="playlist.name" type="text">
<h2 v-else @click="() => selectPlaylist( index )">
{{ playlist.name }}
</h2>
<i class="fa-solid fa-pen-to-square" @click="() => togglePlaylistEditing( index )"></i>
<i class="fa-solid fa-trash" @click="() => removePlaylist( index )"></i>
</div>
</div>
</div>
</template>