swap to generated api code and composables

This commit is contained in:
2025-05-21 16:44:23 +02:00
parent b9e865780a
commit a7a6a9b65d
29 changed files with 3307 additions and 517 deletions

View File

@@ -1,10 +1,10 @@
<script setup lang="ts">
import type { Song, CollectionPreview } from '../script/types'
import { ref, onMounted } from 'vue'
import { useUserStore } from '@/stores/userStore';
import CollectionListItem from '../components/CollectionListItem.vue'
import { useUser } from '@/composables/useUser';
const userStore = useUserStore();
const userStore = useUser();
const collections = ref<CollectionPreview[]>([]);
const limit = ref(10);

View File

@@ -1,11 +1,12 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useUserStore } from '@/stores/userStore';
import SongItem from '../components/SongItem.vue'
import { useAudioStore } from '@/stores/audioStore';
import { useAudio } from '@/composables/useAudio';
import { useUser } from '@/composables/useUser';
import type { Me } from '@/script/types';
const audioStore = useAudioStore();
const userStore = useUserStore();
const audioStore = useAudio();
const userStore = useUser();
const bgColor = ref('');
const actionColor = ref('');
@@ -16,7 +17,7 @@ const loginStatus = ref('Login');
function update() {
var input = document.getElementById("url-input") as HTMLInputElement;
userStore.baseUrl = input.value;
userStore.baseUrl.value = input.value;
}
@@ -50,7 +51,7 @@ async function getMe() {
console.log("active user: ", data.name)
userStore.setUser(data);
userStore.setBaseUrl(data.endpoint);
userStore.baseUrl.value(data.endpoint);
}
@@ -87,15 +88,15 @@ function reset() {
<main class="flex-1 flex flex-col overflow-scroll">
<h1> Meeeeee </h1>
<input @change="update" type="text" id="url-input" :value="userStore.baseUrl" disabled />
<input @change="update" type="text" id="url-input" :value="userStore.baseUrl.value" disabled />
<br>
<button v-if="!userStore.User" @click="getMe" class="border bordercolor rounded-lg p-0.5">{{ loginStatus }}</button>
<div v-if="userStore.User" class="flex p-5 justify-between">
<img :src="userStore.User.avatar_url" class="w-1/3">
<button v-if="!userStore.user.value" @click="getMe" class="border bordercolor rounded-lg p-0.5">{{ loginStatus }}</button>
<div v-if="userStore.user.value" class="flex p-5 justify-between">
<img :src="userStore.user.value.avatar_url" class="w-1/3">
<div>
<p>{{ userStore.User.name }}</p>
<p>{{ userStore.User.endpoint == "" ? 'Not Connected' : 'Connected' }}</p>
<p>Sharing: <button @click="share" class="border bordercolor rounded-lg p-0.5">{{ userStore.User.share
<p>{{ userStore.user.value.name }}</p>
<p>{{ userStore.user.value.endpoint == "" ? 'Not Connected' : 'Connected' }}</p>
<p>Sharing: <button @click="share" class="border bordercolor rounded-lg p-0.5">{{ userStore.user.value.share
}}</button></p>
<button @click="getMe" class="border bordercolor rounded-lg p-0.5"> Refresh
</button>

View File

@@ -1,7 +1,5 @@
<script setup lang="ts">
import { ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { useHeaderStore } from '@/stores/headerStore';
const route = useRoute();
@@ -9,7 +7,6 @@ function isActive(path: string) {
return route.path === path ? 'bg-blue-500 text-white' : '';
};
const headerStore = useHeaderStore();
</script>
<template>

View File

@@ -1,8 +1,7 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useAudioStore } from '@/stores/audioStore';
import { useAudio } from '@/composables/useAudio';
const audioStore = useAudioStore();
const audioStore = useAudio();
</script>
<template>
@@ -25,7 +24,7 @@ const audioStore = useAudioStore();
<i class="relative p-36 fa-solid fa-play">
<img class="absolute top-4 left-0 bottom-0 right-0 bg-center bg-cover rounded-lg"
:src="encodeURI(audioStore.bgimg + '?h=320&w=320')" :key="audioStore.bgimg" />
:src="encodeURI(audioStore.bgimg.value + '?h=320&w=320')" :key="audioStore.bgimg.value" />
</i>
</div>
@@ -34,25 +33,25 @@ const audioStore = useAudioStore();
<div>
<div class="flex w-full justify-around">
<i class="fa-solid fa-backward-step text-5xl self-center" @click="audioStore.togglePrev"></i>
<i :class="[audioStore.isPlaying ? 'fa-circle-play' : 'fa-circle-pause']" class="fa-regular text-7xl "
<i :class="[audioStore.isPlaying.value ? 'fa-circle-play' : 'fa-circle-pause']" class="fa-regular text-7xl "
@click="audioStore.togglePlay"></i>
<i class="fa-solid fa-forward-step text-5xl self-center" @click="audioStore.toggleNext"></i>
</div>
</div>
<div class="flex flex-1 justify-around ml-4">
<i @click="audioStore.toggleShuffle" :class="[audioStore.shuffle ? 'info' : '']"
<i @click="audioStore.toggleShuffle" :class="[audioStore.shuffle.value ? 'info' : '']"
class="fa-solid fa-shuffle"></i>
<div class="m-4 info flex-1 overflow-hidden">
<p>{{ audioStore.title }}</p>
<RouterLink :to="'search?a=' + audioStore.artist">
<p>{{ audioStore.title.value }}</p>
<RouterLink :to="'search?a=' + audioStore.artist.value">
{{ audioStore.artist }}
{{ audioStore.artist.value }}
</RouterLink>
</div>
<div class="flex flex-col justify-between mb-4 mr-4">
<i @click="audioStore.toggleRepeat" :class="[audioStore.repeat ? 'info' : '']"
<i @click="audioStore.toggleRepeat" :class="[audioStore.repeat.value ? 'info' : '']"
class="fa-solid fa-repeat"></i>
<i @click="$router.go(-1);" class="fa-solid fa-arrow-down"></i>
</div>
@@ -60,11 +59,11 @@ const audioStore = useAudioStore();
<div class="flex">
<input
class="appearance-none mx-4 flex-1 bg-yellow-200 bg-opacity-20 accent-yellow-600 rounded-lg outline-none slider "
type="range" id="audio-slider" @change="audioStore.updateTime" max="100" :value="audioStore.percentDone">
type="range" id="audio-slider" @change="audioStore.updateTime" max="100" :value="audioStore.percentDone.value">
</div>
<div class="flex justify-between mx-4">
<span id="current-time" class="time">{{ audioStore.currentTime }}</span>
<span id="duration" class="time ">{{ audioStore.duration }}</span>
<span id="current-time" class="time">{{ audioStore.currentTime.value }}</span>
<span id="duration" class="time ">{{ audioStore.duration.value }}</span>
</div>
</div>
</div>

View File

@@ -1,16 +1,18 @@
<script setup lang="ts">
import SongItem from '../components/SongItem.vue'
import type { Song, CollectionPreview } from '../script/types'
import { type Song, type CollectionPreview, mapApiToSongs } from '../script/types'
import { ref, onMounted } from 'vue'
import { useUserStore } from '@/stores/userStore';
import { useRoute } from 'vue-router';
import CollectionListItem from '../components/CollectionListItem.vue'
import { useAudioStore } from '@/stores/audioStore';
import { useAudio } from '@/composables/useAudio';
import { useUser } from '@/composables/useUser';
import { useApi } from '@/composables/useApi';
const userStore = useUser();
const audioStore = useAudio();
const { musicApi } = useApi();
const api = musicApi()
const route = useRoute();
const userStore = useUserStore();
const audioStore = useAudioStore();
const songs = ref<Song[]>([]);
const name = ref('name');
@@ -23,19 +25,23 @@ const fetchRecent = async () => {
if (isLoading.value) return;
isLoading.value = true;
const data = await userStore.fetchRecent(limit.value, offset.value);
data.forEach(song => {
song.previewimage = `${userStore.baseUrl}/api/v1/images/${song.previewimage}`;
song.url = `${userStore.baseUrl}/api/v1/audio/${song.url}`;
});
try {
const response = await api.musicBackendRecent(limit.value, offset.value);
if (response.data.songs) {
let newSongs = mapApiToSongs(response.data.songs);
offset.value += limit.value;
console.log(data)
songs.value = [...songs.value, ...data];
songs.value = [...songs.value, ...newSongs];
console.log(offset.value)
isLoading.value = false;
audioStore.setCollection(songs.value);
}
} catch (error) {
console.error('Failed to load songs:', error)
}
}

View File

@@ -1,124 +1,128 @@
<script setup lang="ts">
import type { Song, CollectionPreview } from '../script/types'
import { useUserStore } from '@/stores/userStore';
import { onMounted, ref, watch } from 'vue';
import { mapApiToSongs, type Song } from '../script/types'
import { ref, onMounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import ActiveSearchList from '../components/ActiveSearchList.vue'
import { useRoute, useRouter } from 'vue-router';
import SongItem from '../components/SongItem.vue'
import { useAudioStore } from '@/stores/audioStore';
import { useAudio } from '@/composables/useAudio'
import { useUser } from '@/composables/useUser'
import { MusicBackendApi } from '@/generated'
import { useApi } from '@/composables/useApi'
const router = useRouter();
const route = useRoute();
const audioStore = useAudioStore();
const userStore = useUserStore();
const activesongs = ref<Song[]>([]);
const songs = ref<Song[]>([]);
const artists = ref<string[]>([]);
const showSearch = ref(false);
const searchTerm = ref('');
const router = useRouter()
const route = useRoute()
const audioStore = useAudio()
const userStore = useUser()
const { musicApi } = useApi()
const api = musicApi()
const activesongs = ref<Song[]>([])
const songs = ref<Song[]>([])
const artists = ref<string[]>([])
const showSearch = ref(false)
const searchTerm = ref('')
async function fetchActiveSearch(term: string) {
const response = await api.musicBackendSearch(term);
const songData = mapApiToSongs(response.data.songs)
songData.forEach((song: Song) => {
song.previewimage = `${userStore.baseUrl.value}/api/v1/images/${song.previewimage}`
song.url = `${userStore.baseUrl.value}/api/v1/audio/${song.url}`
})
activesongs.value = songData
if (response.data.artist) artists.value = [response.data.artist]
audioStore.setCollection(songData)
showSearch.value = true
searchTerm.value = term
router.replace({ query: { s: term } })
}
async function fetchSearchArtist(artist: string) {
const data = await musicApi.MusicBackend_SearchArtists(artist)
data.forEach((song: Song) => {
song.previewimage = `${userStore.baseUrl.value}/api/v1/images/${song.previewimage}`
song.url = `${userStore.baseUrl.value}/api/v1/audio/${song.url}`
})
songs.value = data
showSearch.value = false
}
async function emptySearch() {
activesongs.value = []
artists.value = []
songs.value = []
showSearch.value = false
searchTerm.value = ''
router.replace({ query: {} })
}
onMounted(async () => {
await loadartistifexist();
const container = document.querySelector('.search') as HTMLInputElement;
if (container) {
container.addEventListener('input', async (event: Event) => {
showSearch.value = true;
const target = event.target as HTMLInputElement;
if (target.value != undefined && target.value != "") {
const data = await userStore.fetchActiveSearch(target.value)
router.push({ query: { s: target.value } });
searchTerm.value = target.value
data.songs.forEach(song => {
song.previewimage = `${userStore.baseUrl}/api/v1/images/${song.previewimage}`;
song.url = `${userStore.baseUrl}/api/v1/audio/${song.url}`;
});
activesongs.value = data.songs;
audioStore.setCollection(data.songs)
artists.value = data.artist;
} else {
activesongs.value = [];
artists.value = [];
showSearch.value = false;
router.push({ query: { s: target.value } });
}
});
container.addEventListener('enter', async (event: Event) => {
const target = event.target as HTMLInputElement;
if (target.value != undefined && target.value != "") {
showSearch.value = false
const data = await userStore.fetchActiveSearch(target.value)
}
})
if (route.query.a) {
await fetchSearchArtist(route.query.a as string)
}
const s = route.query.s as string;
if (s) { container.value = s; container.dispatchEvent(new Event('input')) }
});
async function loadartistifexist() {
const query = route.query.a as string;
if (query) {
showSearch.value = false;
const data = await userStore.fetchSearchArtist(query)
console.log(data);
data.forEach(song => {
song.previewimage = `${userStore.baseUrl}/api/v1/images/${song.previewimage}`;
song.url = `${userStore.baseUrl}/api/v1/audio/${song.url}`;
});
songs.value = data;
if (route.query.s) {
await fetchActiveSearch(route.query.s as string)
}
}
})
function emptySearch() {
const container = document.querySelector('.search') as HTMLInputElement;
// Watch for artist query changes
watch(() => route.query.a, async (newArtist) => {
if (newArtist) {
await fetchSearchArtist(newArtist as string)
} else {
songs.value = []
}
})
container.value = "";
container.dispatchEvent(new Event('input'))
songs.value = [];
artists.value = [];
}
watch(() => route.query.a, async (newQuery) => {
await loadartistifexist();
});
// Search input model
const searchInput = ref(searchTerm.value)
watch(searchInput, async (val) => {
if (val && val.trim() !== '') {
await fetchActiveSearch(val)
} else {
showSearch.value = false
activesongs.value = []
artists.value = []
router.replace({ query: {} })
}
})
</script>
<template>
<header>
<div class="wrapper">
<nav class="flex justify-start my-2 mx-1 space-x-1">
<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>
<h1 class="absolute left-0 right-0 text-center"> Search </h1>
<h1 class="absolute left-0 right-0 text-center">Search</h1>
</nav>
<hr>
<hr />
</div>
</header>
<main class="flex flex-col flex-1 flex-col w-full h-full overflow-scroll">
<input 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" />
<div class="absolute h-16 right-4 flex flex-col justify-center">
<i @click="emptySearch" class="far fa-times-circle opacity-50"></i>
<main class="flex flex-col flex-1 w-full h-full overflow-scroll">
<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"
/>
<div class="absolute h-16 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 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" />
</div>
<SongItem v-for="(song, index) in songs" :key="index" :song="song" />