fix alligment, remove scrollbars, clean up layout to support desktop properly

This commit is contained in:
2025-05-25 03:14:30 +02:00
parent 8e5adaec96
commit 6058a96258
12 changed files with 80 additions and 51 deletions

View File

@@ -3,6 +3,7 @@ import { RouterLink, RouterView } from 'vue-router'
import NowPlaying from '@/components/NowPlaying.vue'
import NowPlayingView from '@/views/NowPlayingView.vue'
import MenuView from '@/views/MenuView.vue'
import HistoryView from '@/views/HistoryView.vue'
import Footer from '@/components/Footer.vue'
import { ref, onMounted, watch, onUnmounted } from 'vue'
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('--border-color', localStorage.getItem('borderColor') || '#ec4899');
console.log(localStorage.getItem('bgColor'));
}
loadColors();
@@ -71,17 +70,17 @@ onUnmounted(() => {
</div>
<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">
<p>Sidebar content</p>
<aside class="w-1/12 bg-primary p-4 overflow-y-scroll">
<HistoryView />
</aside>
<section class="flex-1 overflow-y-scroll p-4">
<section class="flex-1 overflow-y-hidden">
<RouterView />
</section>
<section class="w-1/5 p-4">
<section class="w-1/5 overflow-y-scroll flex flex-col">
<NowPlayingView />
</section>
</main>

View File

@@ -16,7 +16,7 @@ onMounted(() => {
<template>
<div>
<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"
:style="{ 'filter': 'blur(2px)', 'opacity': '0.5' }" alt="Background Image" />

View File

@@ -13,7 +13,6 @@ const audioStore = useAudio();
function updateSong() {
let updated = props.song;
console.log("updating song:", updated)
audioStore.setSong(updated)
}
</script>
@@ -21,19 +20,19 @@ function updateSong() {
<template>
<div @click="updateSong" :style="{ borderColor: border }" class="m-1 border bordercolor rounded-lg flex">
<img class="h-14 w-14 m-1 rounded-lg"
<div @click="updateSong" :style="{ borderColor: border }" class="m-1 md:text-xl border bordercolor rounded-lg flex">
<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')"
loading="lazy" />
<div class="flex flex-col overflow-hidden">
<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>
<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 :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)
% 60).toString().padStart(2, '0') }}</slot>
</h5>

View File

@@ -22,6 +22,7 @@ function createAudio() {
repeat: ref(false),
activeSongs: ref<Song[] | null>([]),
currentSong: ref<Song | null>(null),
recentlyPlayed: ref(new Map<string, Song>()),
}
function saveToLocalStorage(key: string, data: any) {
@@ -34,10 +35,16 @@ function createAudio() {
}
function setSong(song: Song | null) {
console.log(song)
if (!song) return
state.currentSong.value = song
const map = state.recentlyPlayed.value;
saveToLocalStorage('lastPlayedSong', song)
if (map.has(song.hash)) {
map.delete(song.hash);
}
map.set(song.hash, song);
audioElement.pause()
audioElement.src = song.url
@@ -66,6 +73,11 @@ function toggleNext() {
if (!state.activeSongs.value || !state.currentSong.value) return;
const songs = state.activeSongs.value;
if(state.shuffle.value){
setSong(songs[Math.floor(Math.random() * songs.length)]);
}
const currentHash = state.currentSong.value.hash;
const currentIndex = songs.findIndex(song => song.hash === currentHash);
@@ -99,8 +111,13 @@ function togglePrevious() {
if (state.repeat.value) {
audioElement.currentTime = 0
audioElement.play()
return
}
toggleNext();
}
}
function formatTime(seconds: number): string {
@@ -109,11 +126,8 @@ function togglePrevious() {
return `${min}:${sec}`
}
function updateTime() {
const slider = document.getElementById('audio-slider') as HTMLInputElement
if (slider) {
audioElement.currentTime = (Number(slider.value) / 100) * audioElement.duration
}
function updateTime(value: number) {
if (value) audioElement.currentTime = (Number(value) / 100) * audioElement.duration
}
async function loadInitialSong() {

View File

@@ -19,9 +19,7 @@ const fetchCollections = async () => {
isLoading.value = true;
const response = await api.musicBackendCollections(offset.value, limit.value);
console.log(response.data.songs.length)
let songs = mapApiToCollectionPreview(response.data.songs)
console.log(songs)
collections.value = [...collections.value, ...songs];
offset.value += limit.value;
@@ -48,7 +46,7 @@ onMounted(async () => {
</script>
<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">
<CollectionListItem v-for="(collection, index) in collections" :key="index" :collection="collection" />
</div>

View File

@@ -3,7 +3,7 @@ import SongItem from '../components/SongItem.vue'
</script>
<template>
<main class="flex-1 flex-col overflow-scroll">
<main class="flex-1 flex-col overflow-y-scroll">
<p>Coming Soon...</p>
</main>
</template>

View 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>

View File

@@ -32,8 +32,6 @@ function save(bg: string | null, main: string | null, info: string | null, borde
localStorage.setItem('actionColor', main ?? actionColor.value);
localStorage.setItem('infoColor', info ?? infoColor.value);
localStorage.setItem('borderColor', border ?? borderColor.value);
console.log("bg", bgColor.value, "action:", actionColor.value, "info", infoColor.value, "border", borderColor.value)
}
async function getMe() {
@@ -44,12 +42,10 @@ async function getMe() {
console.log("redirect detected");
}
console.log(data)
if (data.id === null || data.id === undefined || Object.keys(data).length === 0) {
return
}
console.log("active user: ", data.name)
userStore.setUser(data);
userStore.baseUrl.value(data.endpoint);
@@ -86,7 +82,7 @@ function reset() {
</div>
</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 />
<br>
<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">
</div>
</div>
<div class="w-full p-2">
<p>Current</p>
<SongItem :song="audioStore.currentSong" />
<SongItem :song="audioStore.currentSong.value" />
</div>
<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
</button>
</p>
<SongItem :song="audioStore.currentSong" :border="'#b3002d'" :action="'#5e2d8f'" :info="'#57db5d'" />
<SongItem :song="audioStore.currentSong.value" :border="'#b3002d'" :action="'#5e2d8f'" :info="'#57db5d'" />
</div>
<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"
class="border rounded-lg p-0.5" @click="save('#1c1719', '#eab308', '#ec4889', '#ec4889')">Choose
</button>
</p>
<SongItem :song="audioStore.currentSong" :border="'#ec4889'" :info="'#ec4889'" :action="'#eab308'" />
<SongItem :song="audioStore.currentSong.value" :border="'#ec4889'" :info="'#ec4889'" :action="'#eab308'" />
</div>
<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"
@click="save('#ff4c4c', '#ffcc00', '#ffffff', '#ffffff')">Choose</button>
</p>
<SongItem :song="audioStore.currentSong" :border="'#ffffff'" :info="'#ffffff'" :action="'#ffcc00'" />
<SongItem :song="audioStore.currentSong.value" :border="'#ffffff'" :info="'#ffffff'" :action="'#ffcc00'" />
</div>
<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"
@click="save('#003d00', '#a8d5a2', '#e0f8d8', '#e0f8d8')">Choose</button>
</p>
<SongItem :song="audioStore.currentSong" :border="'#e0f8d8'" :info="'#e0f8d8'" :action="'#a8d5a2'" />
<SongItem :song="audioStore.currentSong.value" :border="'#e0f8d8'" :info="'#e0f8d8'" :action="'#a8d5a2'" />
</div>
<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"
@click="save('#00274d', '#0099ff', '#00ffff', '#00ffff')">Choose</button>
</p>
<SongItem :song="audioStore.currentSong" :border="'#00ffff'" :info="'#00ffff'" :action="'#0099ff'" />
<SongItem :song="audioStore.currentSong.value" :border="'#00ffff'" :info="'#00ffff'" :action="'#0099ff'" />
</div>
</main>

View File

@@ -10,10 +10,10 @@ function isActive(path: string) {
</script>
<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">
<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>
<RouterLink :class="`p-1 rounded-full backdrop--light shadow-xl ${isActive('/')}`" to="/menu/recent">Recently

View File

@@ -74,8 +74,9 @@ const bgimg = computed(() => audioStore.currentSong.value?.previewimage || '/def
<input
class="w-full appearance-none h-2 rounded-full bg-yellow-200 bg-opacity-20 accent-yellow-600 outline-none"
type="range"
@change="audioStore.updateTime"
@input="event => audioStore.updateTime(Number(event.target.value))"
:max="100"
step="0.001"
:value="audioStore.percentDone.value"
/>
</div>

View File

@@ -2,7 +2,7 @@
import SongItem from '../components/SongItem.vue'
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 { useAudio } from '@/composables/useAudio';
import { useUser } from '@/composables/useUser';
@@ -16,6 +16,7 @@ const api = musicApi()
const songs = ref<Song[]>([]);
const name = ref('name');
const containerRef = ref<HTMLElement | null>(null);
const limit = ref(100);
const offset = ref(0);
@@ -46,7 +47,9 @@ const fetchRecent = async () => {
onMounted(async () => {
await fetchRecent();
const container = document.querySelector('.song-container');
await nextTick();
const container = containerRef.value;
if (container) {
container.addEventListener('scroll', async () => {
const scrollTop = container.scrollTop;
@@ -64,11 +67,10 @@ onMounted(async () => {
</script>
<template>
<main class="flex-1 flex-col overflow-scroll">
<div class="flex-1 flex-col h-full overflow-scroll song-container">
<div
ref="containerRef"
class="flex-1 flex-col overflow-y-scroll song-container"
>
<SongItem v-for="(song, index) in songs" :key="index" :song="song" />
</div>
</main>
</template>

View File

@@ -108,19 +108,19 @@ watch(searchInput, async (val) => {
</div>
</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">
<input
v-model="searchInput"
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>
</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">
<ActiveSearchList :songs="activesongs" :artist="artists" :search="searchTerm" />
</div>