feat: show search icon in header instead of input

+ fix: persistent updating text on update playlist modal
+ move router routes to a separate file
    + lazy import route components
+ remove loading: lazy from songcard
+ remove unused imports on navigation component
This commit is contained in:
geoffrey45
2022-09-18 10:07:58 +03:00
parent 5af3d9cfc3
commit 194a615b2d
14 changed files with 221 additions and 183 deletions
+2
View File
@@ -53,3 +53,5 @@ $track-btn-svg: $accent;
$side-nav-svg: $red; $side-nav-svg: $red;
$overshoot: cubic-bezier(0.68, -0.55, 0.265, 1.55);
+5 -3
View File
@@ -1,5 +1,5 @@
<template> <template>
<router-link :to="{ name: 'FolderView', params: { path: folder.path } }"> <router-link :to="{ name: Routes.folder, params: { path: folder.path } }">
<div class="f-item"> <div class="f-item">
<div class="icon"> <div class="icon">
<FolderSvg v-if="!folder.is_sym" /> <FolderSvg v-if="!folder.is_sym" />
@@ -16,8 +16,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { Folder } from "@/interfaces"; import { Folder } from "@/interfaces";
import FolderSvg from "../../assets/icons/folder.svg"; import { Routes } from "@/composables/enums";
import SymLinkSvg from "../../assets/icons/symlink.svg";
import FolderSvg from "@/assets/icons/folder.svg";
import SymLinkSvg from "@/assets/icons/symlink.svg";
defineProps<{ defineProps<{
folder: Folder; folder: Folder;
@@ -13,7 +13,6 @@
:src="imguri + track?.image" :src="imguri + track?.image"
alt="" alt=""
class="l-image rounded force-lm" class="l-image rounded force-lm"
loading="lazy"
/> />
<div id="bitrate" v-if="track?.bitrate"> <div id="bitrate" v-if="track?.bitrate">
{{ track.filetype }} {{ track.bitrate }} {{ track.filetype }} {{ track.bitrate }}
+1 -7
View File
@@ -22,18 +22,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from "@vue/reactivity";
import { Routes } from "@/composables/enums"; import { Routes } from "@/composables/enums";
import useSettingsStore from "@/stores/settings";
import PlaylistSvg from "../../assets/icons/playlist.svg"; import PlaylistSvg from "../../assets/icons/playlist.svg";
import FolderSvg from "../../assets/icons/folder.svg"; import FolderSvg from "../../assets/icons/folder.svg";
import SettingsSvg from "../../assets/icons/settings.svg"; import SettingsSvg from "../../assets/icons/settings.svg";
import SearchSvg from "../../assets/icons/search.svg"; import SearchSvg from "../../assets/icons/search.svg";
const settings = useSettingsStore();
const menus = [ const menus = [
{ {
name: "playlists", name: "playlists",
@@ -101,7 +95,7 @@ const menus = [
margin: 0 $small 0 $small; margin: 0 $small 0 $small;
border-radius: $small; border-radius: $small;
transform: scale(0.9); transform: scale(0.9);
opacity: .75; opacity: 0.75;
} }
} }
</style> </style>
@@ -5,8 +5,8 @@
> >
<img <img
:src="imguri + playlist.thumb" :src="imguri + playlist.thumb"
class="rounded border" class="rounded"
:class="{ noimg: !playlist.thumb }" :class="{ border: !playlist.thumb }"
/> />
<div class="overlay rounded"> <div class="overlay rounded">
<div class="p-name ellip">{{ playlist.name }}</div> <div class="p-name ellip">{{ playlist.name }}</div>
@@ -42,11 +42,6 @@ const props = defineProps<{
object-fit: cover; object-fit: cover;
transition: all 0.5s ease; transition: all 0.5s ease;
} }
img.noimg {
outline: 1px solid $gray2;
}
.overlay { .overlay {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
+19 -17
View File
@@ -1,6 +1,6 @@
<template> <template>
<form <form
@submit="update_playlist" @submit.prevent="update_playlist"
class="new-p-form" class="new-p-form"
enctype="multipart/form-data" enctype="multipart/form-data"
> >
@@ -34,7 +34,9 @@
}" }"
/> />
</div> </div>
<button type="submit" id="updateplaylistsubmit">Update</button> <button>
{{ clicked ? "Updating" : "Update" }}
</button>
</form> </form>
</template> </template>
@@ -42,7 +44,7 @@
import { updatePlaylist } from "@/composables/fetch/playlists"; import { updatePlaylist } from "@/composables/fetch/playlists";
import { Playlist } from "@/interfaces"; import { Playlist } from "@/interfaces";
import usePStore from "@/stores/pages/playlist"; import usePStore from "@/stores/pages/playlist";
import { onMounted } from "vue"; import { onMounted, ref } from "vue";
import { paths } from "@/config"; import { paths } from "@/config";
const pStore = usePStore(); const pStore = usePStore();
@@ -96,27 +98,27 @@ function handleFile(file: File) {
image = file; image = file;
} }
let clicked = false; let clicked = ref(false);
function update_playlist(e: Event) { function update_playlist(e: Event) {
e.preventDefault();
if (!clicked) {
clicked = true;
const elem = document.getElementById(
"updateplaylistsubmit"
) as HTMLButtonElement;
elem.innerText = "Updating";
} else {
return;
}
const form = e.target as HTMLFormElement; const form = e.target as HTMLFormElement;
const formData = new FormData(form); const formData = new FormData(form);
const name = formData.get("name") as string;
const nameChanged = name !== props.playlist.name;
const imgChanged = image !== undefined;
if (!nameChanged && !imgChanged) {
emit("hideModal");
return;
}
clicked.value = true;
formData.append("image", image); formData.append("image", image);
if (formData.get("name").toString().trim() !== "") { if (name && name.toString().trim() !== "") {
updatePlaylist(props.playlist.playlistid, formData, pStore).then(() => { updatePlaylist(props.playlist.playlistid, formData, pStore).then(() => {
emit("hideModal"); emit("hideModal");
}); });
+1 -1
View File
@@ -18,7 +18,7 @@
import { AlbumInfo } from "../../interfaces"; import { AlbumInfo } from "../../interfaces";
import { paths } from "../../config"; import { paths } from "../../config";
const imguri = paths.images.thumb; const imguri = paths.images.thumb.large;
defineProps<{ defineProps<{
album: AlbumInfo; album: AlbumInfo;
}>(); }>();
+4 -14
View File
@@ -1,13 +1,6 @@
<template> <template>
<div v-tooltip="returnArtists()" style="width: auto"> <div v-tooltip="returnArtists()" style="width: auto">
<div <div class="ellip" v-if="artists === null || artists.length === 0">
class="ellip"
v-if="
artists === null ||
artists.length === 0 ||
(artists[0] === '' && artists.length === 1)
"
>
<span>{{ albumartist }}</span> <span>{{ albumartist }}</span>
</div> </div>
<div class="ellip" v-else> <div class="ellip" v-else>
@@ -27,12 +20,9 @@ const props = defineProps<{
}>(); }>();
function returnArtists() { function returnArtists() {
if (props.artists === null) return props.albumartist; if (props.artists === null || props.artists.length === 0)
if (props.artists[0] !== "" && props.artists.length > 1) {
return props.artists.join(", ");
}
return props.albumartist; return props.albumartist;
return props.artists.join(", ");
} }
</script> </script>
+53 -4
View File
@@ -1,20 +1,31 @@
<template> <template>
<div class="header-input-wrapper rounded-sm" :class="{ showInput: clicked }">
<div class="search-svg" @click="clicked = !clicked">
<SearchSvg />
</div>
<input <input
type="search" type="search"
class="header-input rounded-sm pad-sm" class="header-input rounded-sm pad-sm"
:class="{ showInput: clicked }"
placeholder="Search here" placeholder="Search here"
v-model.trim="source" v-model.trim="source"
id="page-search" id="page-search"
/> />
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import usePStore from "@/stores/pages/playlist"; import { ref } from "vue";
import useFolderStore from "@/stores/pages/folder";
import useAlbumStore from "@/stores/pages/album";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import usePStore from "@/stores/pages/playlist";
import useAlbumStore from "@/stores/pages/album";
import useFolderStore from "@/stores/pages/folder";
import { Routes } from "@/composables/enums"; import { Routes } from "@/composables/enums";
import SearchSvg from "@/assets/icons/search.svg";
const clicked = ref(false);
const { query: playlistQuery } = storeToRefs(usePStore()); const { query: playlistQuery } = storeToRefs(usePStore());
const { query: folderQuery } = storeToRefs(useFolderStore()); const { query: folderQuery } = storeToRefs(useFolderStore());
@@ -44,6 +55,18 @@ const source = getRef();
</script> </script>
<style lang="scss"> <style lang="scss">
.header-input-wrapper {
display: flex;
flex-direction: row-reverse;
width: 1.5rem;
transition: all 0.25s;
&.showInput {
width: 15rem;
}
}
.header-input { .header-input {
background-color: $gray3; background-color: $gray3;
outline: none; outline: none;
@@ -51,9 +74,35 @@ const source = getRef();
color: inherit; color: inherit;
font-size: 1rem; font-size: 1rem;
z-index: 200; z-index: 200;
transition: all 0.25s $overshoot;
opacity: 0;
transform: translateY(-1rem);
&:focus { &:focus {
outline: solid; outline: solid;
} }
&.showInput {
opacity: 1;
transform: translateY(0);
transition-delay: .1s;
}
}
.search-svg {
// outline: solid;
margin-top: $smaller;
cursor: pointer;
// padding-left: ;
width: 2.25rem;
height: 2rem;
// aspect-ratio: 1;
z-index: 100;
svg {
display: block;
margin: 0 auto;
// outline: solid;
}
} }
</style> </style>
+1 -3
View File
@@ -1,5 +1,4 @@
import { paths } from "@/config"; import { paths } from "@/config";
import state from "../state";
import axios from "axios"; import axios from "axios";
import useAxios from "./useAxios"; import useAxios from "./useAxios";
@@ -81,5 +80,4 @@ export {
loadMoreArtists, loadMoreArtists,
}; };
// TODO: // TODO: Rewrite this module using `useAxios` hook
// Rewrite this module using `useAxios` hook
-2
View File
@@ -86,5 +86,3 @@ const paths = {
}; };
export { paths, toggleMode }; export { paths, toggleMode };
// TODO: Add setting to toggle between extending to edges or not
+3 -114
View File
@@ -1,121 +1,10 @@
import state from "@/composables/state"; import { routes } from "./routes";
import useAStore from "@/stores/pages/album"; import { createRouter, createWebHashHistory, RouterOptions } from "vue-router";
import useFStore from "@/stores/pages/folder";
import usePTrackStore from "@/stores/pages/playlist";
import usePStore from "@/stores/pages/playlists";
import AlbumsExplorer from "@/views/AlbumsExplorer.vue";
import AlbumView from "@/views/album/index.vue";
import ArtistsExplorer from "@/views/ArtistsExplorer.vue";
import FolderView from "@/views/FolderView.vue";
import Home from "@/views/Home.vue";
import PlaylistList from "@/views/PlaylistList.vue";
import PlaylistView from "@/views/playlist/index.vue";
import SettingsView from "@/views/SettingsView.vue";
import SearchView from "@/views/Search.vue";
import QueueView from "@/views/QueueView.vue";
import { createRouter, createWebHashHistory } from "vue-router";
const routes = [
{
path: "/",
name: "Home",
component: Home,
redirect: "/folder/$home",
},
{
path: "/folder/:path",
name: "FolderView",
component: FolderView,
beforeEnter: async (to: any) => {
state.loading.value = true;
await useFStore()
.fetchAll(to.params.path)
.then(() => {
state.loading.value = false;
});
},
},
{
path: "/folder/",
redirect: "/folder/home",
},
{
path: "/playlists",
name: "PlaylistList",
component: PlaylistList,
beforeEnter: async () => {
state.loading.value = true;
await usePStore()
.fetchAll()
.then(() => {
state.loading.value = false;
});
},
},
{
path: "/playlist/:pid",
name: "PlaylistView",
component: PlaylistView,
beforeEnter: async (to: any) => {
state.loading.value = true;
await usePTrackStore()
.fetchAll(to.params.pid)
.then(() => {
state.loading.value = false;
});
},
},
{
path: "/albums",
name: "AlbumsView",
component: AlbumsExplorer,
},
{
path: "/albums/:hash",
name: "AlbumView",
component: AlbumView,
beforeEnter: async (to: any) => {
state.loading.value = true;
const store = useAStore();
await store
.fetchTracksAndArtists(to.params.hash)
.then(() => {
state.loading.value = false;
});
},
},
{
path: "/artists",
name: "ArtistsView",
component: ArtistsExplorer,
},
{
path: "/settings",
name: "SettingsView",
component: SettingsView,
},
{
path: "/search",
name: "SearchView",
component: SearchView,
},
{
path: "/queue",
name: "QueueView",
component: QueueView,
},
{
path: "/:pathMatch(.*)",
component: () => import("../views/NotFound.vue"),
},
];
const router = createRouter({ const router = createRouter({
mode: "hash", mode: "hash",
history: createWebHashHistory(import.meta.env.BASE_URL), history: createWebHashHistory(import.meta.env.BASE_URL),
routes, routes,
}); } as RouterOptions);
export default router; export default router;
+121
View File
@@ -0,0 +1,121 @@
import state from "@/composables/state";
import useAStore from "@/stores/pages/album";
import useFStore from "@/stores/pages/folder";
import usePTrackStore from "@/stores/pages/playlist";
import usePStore from "@/stores/pages/playlists";
import Home from "@/views/Home.vue";
const routes = [
{
path: "/",
name: "Home",
component: Home,
redirect: "/folder/$home",
},
{
path: "/folder/:path",
name: "FolderView",
component: () => import("@/views/FolderView.vue"),
beforeEnter: async (to: any) => {
state.loading.value = true;
await useFStore()
.fetchAll(to.params.path)
.then(() => {
state.loading.value = false;
});
},
},
{
path: "/playlists",
name: "PlaylistList",
component: () => import("@/views/PlaylistList.vue"),
beforeEnter: async () => {
state.loading.value = true;
await usePStore()
.fetchAll()
.then(() => {
state.loading.value = false;
});
},
},
{
path: "/playlist/:pid",
name: "PlaylistView",
component: () => import("@/views/playlist/index.vue"),
beforeEnter: async (to: any) => {
state.loading.value = true;
await usePTrackStore()
.fetchAll(to.params.pid)
.then(() => {
state.loading.value = false;
});
},
},
{
path: "/albums",
name: "AlbumsView",
component: () => import("@/views/AlbumsExplorer.vue"),
},
{
path: "/albums/:hash",
name: "AlbumView",
component: () => import("@/views/album/index.vue"),
beforeEnter: async (to: any) => {
state.loading.value = true;
const store = useAStore();
await store.fetchTracksAndArtists(to.params.hash).then(() => {
state.loading.value = false;
});
},
},
{
path: "/artists",
name: "ArtistsView",
component: () => import("@/views/ArtistsExplorer.vue"),
},
{
path: "/settings",
name: "SettingsView",
component: () => import("@/views/SettingsView.vue"),
},
{
path: "/search",
name: "SearchView",
component: () => import("@/views/Search.vue"),
},
{
path: "/queue",
name: "QueueView",
component: () => import("@/views/QueueView.vue"),
},
{
name: "NotFound",
path: "/:pathMatch(.*)",
component: () => import("../views/NotFound.vue"),
},
];
const keys = [
"home",
"folder",
"playlists",
"playlist",
"albums",
"album",
"artists",
"settings",
"search",
"queue",
"notfound",
];
const routesList = routes.map((route, index) => {
const key = keys[index];
return { route: route.name };
});
// TODO: Use dynamic keys in routesList
export { routes, routesList };
-1
View File
@@ -19,7 +19,6 @@ export default defineStore("FolderDirs&Tracks", {
const { tracks, folders } = await fetchThem(path); const { tracks, folders } = await fetchThem(path);
[this.path, this.allDirs, this.allTracks] = [path, folders, tracks]; [this.path, this.allDirs, this.allTracks] = [path, folders, tracks];
}, },
resetQuery() { resetQuery() {
this.query = ""; this.query = "";