mirror of
https://github.com/JuLi0n21/pwa-player.git
synced 2026-04-19 23:40:05 +00:00
fix alligment, remove scrollbars, clean up layout to support desktop properly
This commit is contained in:
@@ -3,6 +3,7 @@ import { RouterLink, RouterView } from 'vue-router'
|
|||||||
import NowPlaying from '@/components/NowPlaying.vue'
|
import NowPlaying from '@/components/NowPlaying.vue'
|
||||||
import NowPlayingView from '@/views/NowPlayingView.vue'
|
import NowPlayingView from '@/views/NowPlayingView.vue'
|
||||||
import MenuView from '@/views/MenuView.vue'
|
import MenuView from '@/views/MenuView.vue'
|
||||||
|
import HistoryView from '@/views/HistoryView.vue'
|
||||||
import Footer from '@/components/Footer.vue'
|
import Footer from '@/components/Footer.vue'
|
||||||
import { ref, onMounted, watch, onUnmounted } from 'vue'
|
import { ref, onMounted, watch, onUnmounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
@@ -37,8 +38,6 @@ function loadColors() {
|
|||||||
|
|
||||||
document.documentElement.style.setProperty('--information-color', localStorage.getItem('infoColor') || '#ec4899');
|
document.documentElement.style.setProperty('--information-color', localStorage.getItem('infoColor') || '#ec4899');
|
||||||
document.documentElement.style.setProperty('--border-color', localStorage.getItem('borderColor') || '#ec4899');
|
document.documentElement.style.setProperty('--border-color', localStorage.getItem('borderColor') || '#ec4899');
|
||||||
|
|
||||||
console.log(localStorage.getItem('bgColor'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loadColors();
|
loadColors();
|
||||||
@@ -71,17 +70,17 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="flex flex-col h-screen max-h-screen wrapper info text-xl">
|
<div v-else class="flex flex-col h-screen max-h-screen wrapper info text-xl">
|
||||||
<main class="flex flex-1 w-full h-full overflow-y-scroll">
|
<main class="flex flex-1 w-full h-full overflow-y-hidden">
|
||||||
|
|
||||||
<aside class="w-1/12 bg-primary p-4">
|
<aside class="w-1/12 bg-primary p-4 overflow-y-scroll">
|
||||||
<p>Sidebar content</p>
|
<HistoryView />
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<section class="flex-1 overflow-y-scroll p-4">
|
<section class="flex-1 overflow-y-hidden">
|
||||||
<RouterView />
|
<RouterView />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="w-1/5 p-4">
|
<section class="w-1/5 overflow-y-scroll flex flex-col">
|
||||||
<NowPlayingView />
|
<NowPlayingView />
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ onMounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="relative wrapper p-1 grow action">
|
<div class="relative wrapper p-1 action">
|
||||||
<img :src="encodeURI(bgimg + '?h=150&w=400')" class="w-full absolute top-0 left-0 right-0 h-full"
|
<img :src="encodeURI(bgimg + '?h=150&w=400')" class="w-full absolute top-0 left-0 right-0 h-full"
|
||||||
:style="{ 'filter': 'blur(2px)', 'opacity': '0.5' }" alt="Background Image" />
|
:style="{ 'filter': 'blur(2px)', 'opacity': '0.5' }" alt="Background Image" />
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ const audioStore = useAudio();
|
|||||||
function updateSong() {
|
function updateSong() {
|
||||||
|
|
||||||
let updated = props.song;
|
let updated = props.song;
|
||||||
console.log("updating song:", updated)
|
|
||||||
audioStore.setSong(updated)
|
audioStore.setSong(updated)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -21,19 +20,19 @@ function updateSong() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div @click="updateSong" :style="{ borderColor: border }" class="m-1 border bordercolor rounded-lg flex">
|
<div @click="updateSong" :style="{ borderColor: border }" class="m-1 md:text-xl border bordercolor rounded-lg flex">
|
||||||
<img class="h-14 w-14 m-1 rounded-lg"
|
<img class="h-14 w-14 md:w-24 md:h-24 m-1 rounded-lg"
|
||||||
:src="encodeURI(props.song?.previewimage ? props.song?.previewimage + '?h=56&w=56' : '/default-bg.png')"
|
:src="encodeURI(props.song?.previewimage ? props.song?.previewimage + '?h=56&w=56' : '/default-bg.png')"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
<div class="flex flex-col overflow-hidden">
|
<div class="flex flex-col overflow-hidden">
|
||||||
<p :style="{ color: info }" class="text-nowrap text-ellipsis overflow-hidden text-base info">
|
<p :style="{ color: info }" class="text-nowrap text-ellipsis overflow-hidden text-base info">
|
||||||
<slot name="songName">{{ props.song?.name ?? 'Title' }}</slot>
|
<slot name="songName">{{ props.song?.name ? props.song?.name : 'Unknown Title' }}</slot>
|
||||||
</p>
|
</p>
|
||||||
<h5 :style="{ color: action }" class="action text-sm text-nowrap text-ellipsis overflow-hidden text-base">
|
<h5 :style="{ color: action }" class="action text-sm text-nowrap text-ellipsis overflow-hidden text-base">
|
||||||
<slot name="artist">{{ props.song?.artist ?? 'Artist' }}</slot>
|
<slot name="artist">{{ props.song?.artist ? props.song.artist : 'Unknown Artist' }}</slot>
|
||||||
</h5>
|
</h5>
|
||||||
<h5 :style="{ color: action }" class="action text-sm">
|
<h5 :style="{ color: action }" class="action text-sm">
|
||||||
<slot name="length">{{ Math.floor(props.song?.length ?? 0 / 60000) }}:{{ Math.floor((props.song?.length ?? 0 /
|
<slot name="length">{{ Math.floor(props.song?.length / 60000 ?? 0) }}:{{ Math.floor((props.song?.length ?? 0 /
|
||||||
1000)
|
1000)
|
||||||
% 60).toString().padStart(2, '0') }}</slot>
|
% 60).toString().padStart(2, '0') }}</slot>
|
||||||
</h5>
|
</h5>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ function createAudio() {
|
|||||||
repeat: ref(false),
|
repeat: ref(false),
|
||||||
activeSongs: ref<Song[] | null>([]),
|
activeSongs: ref<Song[] | null>([]),
|
||||||
currentSong: ref<Song | null>(null),
|
currentSong: ref<Song | null>(null),
|
||||||
|
recentlyPlayed: ref(new Map<string, Song>()),
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveToLocalStorage(key: string, data: any) {
|
function saveToLocalStorage(key: string, data: any) {
|
||||||
@@ -34,10 +35,16 @@ function createAudio() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setSong(song: Song | null) {
|
function setSong(song: Song | null) {
|
||||||
console.log(song)
|
|
||||||
if (!song) return
|
if (!song) return
|
||||||
state.currentSong.value = song
|
state.currentSong.value = song
|
||||||
|
const map = state.recentlyPlayed.value;
|
||||||
|
|
||||||
|
|
||||||
saveToLocalStorage('lastPlayedSong', song)
|
saveToLocalStorage('lastPlayedSong', song)
|
||||||
|
if (map.has(song.hash)) {
|
||||||
|
map.delete(song.hash);
|
||||||
|
}
|
||||||
|
map.set(song.hash, song);
|
||||||
|
|
||||||
audioElement.pause()
|
audioElement.pause()
|
||||||
audioElement.src = song.url
|
audioElement.src = song.url
|
||||||
@@ -66,6 +73,11 @@ function toggleNext() {
|
|||||||
if (!state.activeSongs.value || !state.currentSong.value) return;
|
if (!state.activeSongs.value || !state.currentSong.value) return;
|
||||||
|
|
||||||
const songs = state.activeSongs.value;
|
const songs = state.activeSongs.value;
|
||||||
|
|
||||||
|
if(state.shuffle.value){
|
||||||
|
setSong(songs[Math.floor(Math.random() * songs.length)]);
|
||||||
|
}
|
||||||
|
|
||||||
const currentHash = state.currentSong.value.hash;
|
const currentHash = state.currentSong.value.hash;
|
||||||
const currentIndex = songs.findIndex(song => song.hash === currentHash);
|
const currentIndex = songs.findIndex(song => song.hash === currentHash);
|
||||||
|
|
||||||
@@ -99,8 +111,13 @@ function togglePrevious() {
|
|||||||
if (state.repeat.value) {
|
if (state.repeat.value) {
|
||||||
audioElement.currentTime = 0
|
audioElement.currentTime = 0
|
||||||
audioElement.play()
|
audioElement.play()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTime(seconds: number): string {
|
function formatTime(seconds: number): string {
|
||||||
@@ -109,11 +126,8 @@ function togglePrevious() {
|
|||||||
return `${min}:${sec}`
|
return `${min}:${sec}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTime() {
|
function updateTime(value: number) {
|
||||||
const slider = document.getElementById('audio-slider') as HTMLInputElement
|
if (value) audioElement.currentTime = (Number(value) / 100) * audioElement.duration
|
||||||
if (slider) {
|
|
||||||
audioElement.currentTime = (Number(slider.value) / 100) * audioElement.duration
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadInitialSong() {
|
async function loadInitialSong() {
|
||||||
|
|||||||
@@ -19,9 +19,7 @@ const fetchCollections = async () => {
|
|||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
|
|
||||||
const response = await api.musicBackendCollections(offset.value, limit.value);
|
const response = await api.musicBackendCollections(offset.value, limit.value);
|
||||||
console.log(response.data.songs.length)
|
|
||||||
let songs = mapApiToCollectionPreview(response.data.songs)
|
let songs = mapApiToCollectionPreview(response.data.songs)
|
||||||
console.log(songs)
|
|
||||||
collections.value = [...collections.value, ...songs];
|
collections.value = [...collections.value, ...songs];
|
||||||
offset.value += limit.value;
|
offset.value += limit.value;
|
||||||
|
|
||||||
@@ -48,7 +46,7 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="flex-1 text-center flex flex-col h-full overflow-scroll">
|
<main class="flex-1 text-center flex flex-col h-full overflow-y-scroll">
|
||||||
<div class="flex flex-col overflow-scroll collection-container">
|
<div class="flex flex-col overflow-scroll collection-container">
|
||||||
<CollectionListItem v-for="(collection, index) in collections" :key="index" :collection="collection" />
|
<CollectionListItem v-for="(collection, index) in collections" :key="index" :collection="collection" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import SongItem from '../components/SongItem.vue'
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="flex-1 flex-col overflow-scroll">
|
<main class="flex-1 flex-col overflow-y-scroll">
|
||||||
<p>Coming Soon...</p>
|
<p>Coming Soon...</p>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
21
frontend/src/views/HistoryView.vue
Normal file
21
frontend/src/views/HistoryView.vue
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<main class="flex-1 flex-col overflow-hidden">
|
||||||
|
<div class="flex-1 flex-col h-full overflow-y-scroll song-container">
|
||||||
|
<p class="p-1 rounded-full backdrop--light shadow-xl text-center">History</p>
|
||||||
|
<p v-if="songs.length === 0">No songs found</p>
|
||||||
|
<SongItem v-for="(song, index) in songs" :key="song.hash" :song="song" />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import SongItem from '../components/SongItem.vue'
|
||||||
|
|
||||||
|
import { useAudio } from '@/composables/useAudio';
|
||||||
|
const audioStore = useAudio();
|
||||||
|
|
||||||
|
const songs = computed(() => Array.from(audioStore.recentlyPlayed.value?.values()).reverse() || [])
|
||||||
|
|
||||||
|
</script>
|
||||||
@@ -32,8 +32,6 @@ function save(bg: string | null, main: string | null, info: string | null, borde
|
|||||||
localStorage.setItem('actionColor', main ?? actionColor.value);
|
localStorage.setItem('actionColor', main ?? actionColor.value);
|
||||||
localStorage.setItem('infoColor', info ?? infoColor.value);
|
localStorage.setItem('infoColor', info ?? infoColor.value);
|
||||||
localStorage.setItem('borderColor', border ?? borderColor.value);
|
localStorage.setItem('borderColor', border ?? borderColor.value);
|
||||||
|
|
||||||
console.log("bg", bgColor.value, "action:", actionColor.value, "info", infoColor.value, "border", borderColor.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getMe() {
|
async function getMe() {
|
||||||
@@ -44,12 +42,10 @@ async function getMe() {
|
|||||||
console.log("redirect detected");
|
console.log("redirect detected");
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(data)
|
|
||||||
if (data.id === null || data.id === undefined || Object.keys(data).length === 0) {
|
if (data.id === null || data.id === undefined || Object.keys(data).length === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("active user: ", data.name)
|
|
||||||
userStore.setUser(data);
|
userStore.setUser(data);
|
||||||
userStore.baseUrl.value(data.endpoint);
|
userStore.baseUrl.value(data.endpoint);
|
||||||
|
|
||||||
@@ -86,7 +82,7 @@ function reset() {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="flex-1 flex flex-col overflow-scroll">
|
<main class="flex-1 flex flex-col h-full overflow-y-scroll">
|
||||||
<input @change="update" type="text" id="url-input" :value="userStore.baseUrl.value" disabled />
|
<input @change="update" type="text" id="url-input" :value="userStore.baseUrl.value" disabled />
|
||||||
<br>
|
<br>
|
||||||
<button v-if="!userStore.user.value" @click="getMe" class="border bordercolor rounded-lg p-0.5">{{ loginStatus }}</button>
|
<button v-if="!userStore.user.value" @click="getMe" class="border bordercolor rounded-lg p-0.5">{{ loginStatus }}</button>
|
||||||
@@ -128,10 +124,9 @@ function reset() {
|
|||||||
class="appearance-none w-8 h-8 border-2 p-0 overflow-hidden cursor-pointer">
|
class="appearance-none w-8 h-8 border-2 p-0 overflow-hidden cursor-pointer">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full p-2">
|
<div class="w-full p-2">
|
||||||
<p>Current</p>
|
<p>Current</p>
|
||||||
<SongItem :song="audioStore.currentSong" />
|
<SongItem :song="audioStore.currentSong.value" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full p-2 bg-black">
|
<div class="w-full p-2 bg-black">
|
||||||
@@ -139,14 +134,14 @@ function reset() {
|
|||||||
class="border rounded-lg p-0.5" @click="save('#000000', '#5e2d8f', '#57db5d', '#b3002d')">Choose
|
class="border rounded-lg p-0.5" @click="save('#000000', '#5e2d8f', '#57db5d', '#b3002d')">Choose
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
<SongItem :song="audioStore.currentSong" :border="'#b3002d'" :action="'#5e2d8f'" :info="'#57db5d'" />
|
<SongItem :song="audioStore.currentSong.value" :border="'#b3002d'" :action="'#5e2d8f'" :info="'#57db5d'" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full p-2" style="background-color: #1c1719">
|
<div class="w-full p-2" style="background-color: #1c1719">
|
||||||
<p class="flex-1 flex justify-between" style=" color : #ec4889">Default<button style="border-color : #ec4889"
|
<p class="flex-1 flex justify-between" style=" color : #ec4889">Default<button style="border-color : #ec4889"
|
||||||
class="border rounded-lg p-0.5" @click="save('#1c1719', '#eab308', '#ec4889', '#ec4889')">Choose
|
class="border rounded-lg p-0.5" @click="save('#1c1719', '#eab308', '#ec4889', '#ec4889')">Choose
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
<SongItem :song="audioStore.currentSong" :border="'#ec4889'" :info="'#ec4889'" :action="'#eab308'" />
|
<SongItem :song="audioStore.currentSong.value" :border="'#ec4889'" :info="'#ec4889'" :action="'#eab308'" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full p-2" style="background-color: #ff4c4c">
|
<div class="w-full p-2" style="background-color: #ff4c4c">
|
||||||
@@ -155,7 +150,7 @@ function reset() {
|
|||||||
<button style="border-color: #ffffff" class="border rounded-lg p-0.5"
|
<button style="border-color: #ffffff" class="border rounded-lg p-0.5"
|
||||||
@click="save('#ff4c4c', '#ffcc00', '#ffffff', '#ffffff')">Choose</button>
|
@click="save('#ff4c4c', '#ffcc00', '#ffffff', '#ffffff')">Choose</button>
|
||||||
</p>
|
</p>
|
||||||
<SongItem :song="audioStore.currentSong" :border="'#ffffff'" :info="'#ffffff'" :action="'#ffcc00'" />
|
<SongItem :song="audioStore.currentSong.value" :border="'#ffffff'" :info="'#ffffff'" :action="'#ffcc00'" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full p-2" style="background-color: #003d00">
|
<div class="w-full p-2" style="background-color: #003d00">
|
||||||
@@ -164,7 +159,7 @@ function reset() {
|
|||||||
<button style="border-color: #e0f8d8" class="border rounded-lg p-0.5"
|
<button style="border-color: #e0f8d8" class="border rounded-lg p-0.5"
|
||||||
@click="save('#003d00', '#a8d5a2', '#e0f8d8', '#e0f8d8')">Choose</button>
|
@click="save('#003d00', '#a8d5a2', '#e0f8d8', '#e0f8d8')">Choose</button>
|
||||||
</p>
|
</p>
|
||||||
<SongItem :song="audioStore.currentSong" :border="'#e0f8d8'" :info="'#e0f8d8'" :action="'#a8d5a2'" />
|
<SongItem :song="audioStore.currentSong.value" :border="'#e0f8d8'" :info="'#e0f8d8'" :action="'#a8d5a2'" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full p-2" style="background-color: #00274d">
|
<div class="w-full p-2" style="background-color: #00274d">
|
||||||
@@ -173,7 +168,7 @@ function reset() {
|
|||||||
<button style="border-color: #00ffff" class="border rounded-lg p-0.5"
|
<button style="border-color: #00ffff" class="border rounded-lg p-0.5"
|
||||||
@click="save('#00274d', '#0099ff', '#00ffff', '#00ffff')">Choose</button>
|
@click="save('#00274d', '#0099ff', '#00ffff', '#00ffff')">Choose</button>
|
||||||
</p>
|
</p>
|
||||||
<SongItem :song="audioStore.currentSong" :border="'#00ffff'" :info="'#00ffff'" :action="'#0099ff'" />
|
<SongItem :song="audioStore.currentSong.value" :border="'#00ffff'" :info="'#00ffff'" :action="'#0099ff'" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ function isActive(path: string) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="flex-1 flex flex-col h-full overflow-y-scroll">
|
<main class="flex-1 flex flex-col h-full overflow-y-scroll md:overflow-hidden">
|
||||||
<header v-show="true">
|
<header v-show="true">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<nav class="flex justify-start my-2 mx-1 space-x-1 overflow-x-scroll flex-nowrap text-nowrap">
|
<nav class="flex justify-start my-2 mx-1 space-x-1 overflow-y-scroll flex-nowrap text-nowrap">
|
||||||
<RouterLink class="p-1 rounded-full backdrop--light shadow-xl" to="/"><i class="fa-solid fa-arrow-left"></i>
|
<RouterLink class="p-1 rounded-full backdrop--light shadow-xl" to="/"><i class="fa-solid fa-arrow-left"></i>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<RouterLink :class="`p-1 rounded-full backdrop--light shadow-xl ${isActive('/')}`" to="/menu/recent">Recently
|
<RouterLink :class="`p-1 rounded-full backdrop--light shadow-xl ${isActive('/')}`" to="/menu/recent">Recently
|
||||||
|
|||||||
@@ -74,8 +74,9 @@ const bgimg = computed(() => audioStore.currentSong.value?.previewimage || '/def
|
|||||||
<input
|
<input
|
||||||
class="w-full appearance-none h-2 rounded-full bg-yellow-200 bg-opacity-20 accent-yellow-600 outline-none"
|
class="w-full appearance-none h-2 rounded-full bg-yellow-200 bg-opacity-20 accent-yellow-600 outline-none"
|
||||||
type="range"
|
type="range"
|
||||||
@change="audioStore.updateTime"
|
@input="event => audioStore.updateTime(Number(event.target.value))"
|
||||||
:max="100"
|
:max="100"
|
||||||
|
step="0.001"
|
||||||
:value="audioStore.percentDone.value"
|
:value="audioStore.percentDone.value"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import SongItem from '../components/SongItem.vue'
|
import SongItem from '../components/SongItem.vue'
|
||||||
|
|
||||||
import { type Song, type CollectionPreview, mapApiToSongs } from '../script/types'
|
import { type Song, type CollectionPreview, mapApiToSongs } from '../script/types'
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted, nextTick } from 'vue'
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useAudio } from '@/composables/useAudio';
|
import { useAudio } from '@/composables/useAudio';
|
||||||
import { useUser } from '@/composables/useUser';
|
import { useUser } from '@/composables/useUser';
|
||||||
@@ -16,6 +16,7 @@ const api = musicApi()
|
|||||||
|
|
||||||
const songs = ref<Song[]>([]);
|
const songs = ref<Song[]>([]);
|
||||||
const name = ref('name');
|
const name = ref('name');
|
||||||
|
const containerRef = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
const limit = ref(100);
|
const limit = ref(100);
|
||||||
const offset = ref(0);
|
const offset = ref(0);
|
||||||
@@ -46,7 +47,9 @@ const fetchRecent = async () => {
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await fetchRecent();
|
await fetchRecent();
|
||||||
|
|
||||||
const container = document.querySelector('.song-container');
|
await nextTick();
|
||||||
|
|
||||||
|
const container = containerRef.value;
|
||||||
if (container) {
|
if (container) {
|
||||||
container.addEventListener('scroll', async () => {
|
container.addEventListener('scroll', async () => {
|
||||||
const scrollTop = container.scrollTop;
|
const scrollTop = container.scrollTop;
|
||||||
@@ -64,11 +67,10 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div
|
||||||
<main class="flex-1 flex-col overflow-scroll">
|
ref="containerRef"
|
||||||
<div class="flex-1 flex-col h-full overflow-scroll song-container">
|
class="flex-1 flex-col overflow-y-scroll song-container"
|
||||||
|
>
|
||||||
<SongItem v-for="(song, index) in songs" :key="index" :song="song" />
|
<SongItem v-for="(song, index) in songs" :key="index" :song="song" />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -108,19 +108,19 @@ watch(searchInput, async (val) => {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="flex flex-col flex-1 w-full h-full overflow-scroll">
|
<main class="flex flex-col flex-1 w-full h-full">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<input
|
<input
|
||||||
v-model="searchInput"
|
v-model="searchInput"
|
||||||
placeholder="Type to Search..."
|
placeholder="Type to Search..."
|
||||||
class="flex-1 max-h-12 search border bordercolor accent-pink-800 bg-yellow-300 bg-opacity-20 rounded-lg m-2 p-2"
|
class="w-full flex-1 max-h-12 search border bordercolor accent-pink-800 bg-yellow-300 bg-opacity-20 rounded-lg p-2"
|
||||||
/>
|
/>
|
||||||
<div class="absolute h-16 right-4 flex flex-col justify-center cursor-pointer" @click="emptySearch">
|
<div class="absolute top-4 right-4 flex flex-col justify-center cursor-pointer" @click="emptySearch">
|
||||||
<i class="far fa-times-circle opacity-50"></i>
|
<i class="far fa-times-circle opacity-50"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="relative flex flex-col w-full h-full overflow-scroll">
|
<div class="relative flex flex-col w-full h-full overflow-y-scroll">
|
||||||
<div v-if="showSearch" class="absolute w-full text-center search-recommendations z-20">
|
<div v-if="showSearch" class="absolute w-full text-center search-recommendations z-20">
|
||||||
<ActiveSearchList :songs="activesongs" :artist="artists" :search="searchTerm" />
|
<ActiveSearchList :songs="activesongs" :artist="artists" :search="searchTerm" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user