add now playing card settings

+ move left sidebar to separate component
This commit is contained in:
geoffrey45 2022-08-19 21:28:46 +03:00
parent 44bb30fe9f
commit ade8edcba2
18 changed files with 135 additions and 288 deletions

View File

@ -3,15 +3,7 @@
<Modal />
<Notification />
<div id="app-grid">
<div class="l-sidebar rounded">
<div class="withlogo">
<Logo />
<Navigation />
</div>
<nowPlaying />
<!-- <Playlists /> -->
</div>
<LeftSidebar />
<NavBar />
<div id="acontent" class="rounded">
<router-view />
@ -33,20 +25,18 @@ import useModalStore from "@/stores/modal";
import useContextStore from "@/stores/context";
import handleShortcuts from "@/composables/useKeyboard";
import Logo from "@/components/Logo.vue";
import Modal from "@/components/modal.vue";
import NavBar from "@/components/nav/NavBar.vue";
// import Tabs from "@/components/RightSideBar/Tabs.vue";
import ContextMenu from "@/components/contextMenu.vue";
import Notification from "@/components/Notification.vue";
import Navigation from "@/components/LeftSidebar/Navigation.vue";
import nowPlaying from "@/components/LeftSidebar/nowPlaying.vue";
import RightSideBar from "@/components/RightSideBar/Main.vue";
import SearchInput from "@/components/RightSideBar/SearchInput.vue";
import BottomBar from "@/components/BottomBar/BottomBar.vue";
import LeftSidebar from "./components/LeftSidebar/index.vue";
import { readLocalStorage, writeLocalStorage } from "@/utils";
import Playlists from "./components/LeftSidebar/Playlists.vue";
const queue = useQStore();
const router = useRouter();
@ -71,6 +61,7 @@ onStartTyping(() => {
const elem = document.getElementById("globalsearch") as HTMLInputElement;
elem.focus();
elem.value = "";
document.getElementById("ginner")?.classList.add("search-focused");
});
function handleWelcomeModal() {
@ -97,7 +88,6 @@ onMounted(() => {
.l-sidebar {
position: relative;
.withlogo {
padding: 1rem;
}

View File

@ -1,5 +1,5 @@
<template>
<div class="b-bar bg-black pad-medium rounded">
<div class="b-bar bg-black pad-medium rounded" v-if="settings.use_right_np">
<div class="info">
<img
:src="paths.images.thumb + queue.currenttrack?.image"
@ -22,15 +22,10 @@
</div>
</div>
<Progress />
<!-- <div class="ex-hotkeys">
<HotKeys />
</div> -->
<div class="time">
<span class="current">{{ formatSeconds(queue.currentTime) }}</span>
<span class="current">{{ formatSeconds(queue.duration.current) }}</span>
<HotKeys />
<span class="full">{{
formatSeconds(queue.fullTime || queue.currenttrack.length)
}}</span>
<span class="full">{{ formatSeconds(queue.currenttrack.length) }}</span>
</div>
</div>
</template>
@ -40,11 +35,13 @@ import "@/assets/scss/BottomBar/BottomBar.scss";
import { formatSeconds, putCommas } from "@/utils";
import HotKeys from "../LeftSidebar/NP/HotKeys.vue";
import Progress from "../LeftSidebar/NP/Progress.vue";
import useSettingsStore from "@/stores/settings";
import { paths } from "@/config";
import useQStore from "@/stores/queue";
const queue = useQStore();
const settings = useSettingsStore();
</script>
<style lang="scss">
@ -66,14 +63,6 @@ const queue = useQStore();
}
}
.ex-hotkeys {
position: absolute;
width: 10rem;
right: 1rem;
top: 1rem;
border-radius: $small;
}
.info {
display: grid;
grid-template-columns: max-content 1fr;

View File

@ -7,6 +7,11 @@
:max="q.duration.full"
step="0.1"
@change="seek()"
:style="{
backgroundSize: `${
(q.duration.current / (q.currenttrack.length || 0)) * 100
}% 100%`,
}"
/>
</template>
@ -15,9 +20,9 @@ import useQStore from "../../../stores/queue";
const q = useQStore();
const seek = () => {
const elem = <HTMLFormElement>document.getElementById("progress");
const elem = document.getElementById("progress") as HTMLInputElement;
const value = elem.value;
q.seek(value);
q.seek(value as unknown as number);
};
</script>

View File

@ -0,0 +1,24 @@
<template>
<div class="l-sidebar rounded">
<div class="withlogo">
<Logo />
<Navigation />
</div>
<nowPlaying v-if="settings.use_side_np" />
<!-- <Playlists /> -->
</div>
</template>
<script setup lang="ts">
import Logo from "@/components/Logo.vue";
import Navigation from "@/components/LeftSidebar/Navigation.vue";
import nowPlaying from "@/components/LeftSidebar/nowPlaying.vue";
// import Playlists from "./components/LeftSidebar/Playlists.vue";
import useSettingsStore from "@/stores/settings";
const settings = useSettingsStore();
</script>
<style lang="scss"></style>

View File

@ -1,161 +0,0 @@
<template>
<div class="now-playing bg-black shadow-lg">
<div class="art-tags">
<div class="duration">{{ formatSeconds(current.length) }}</div>
<div
:style="{
backgroundImage: `url(&quot;${current.image}&quot;)`,
}"
class="album-art image bg-black"
></div>
<div class="t-a">
<p id="title" class="ellipsis">{{ current.title }}</p>
<div class="separator no-bg-black"></div>
<div v-if="current.artists[0] !== ''" id="artist" class="ellip">
<span v-for="artist in putCommas(current.artists)" :key="artist">{{
artist
}}</span>
</div>
<div v-else id="artist">
<span>{{ current.albumartist }}</span>
</div>
<div id="type">
<span v-if="current.bitrate > 330"
>FLAC {{ current.bitrate }} Kbps</span
>
<span v-else>MP3 | {{ current.bitrate }} Kbps</span>
</div>
</div>
</div>
<div class="progress">
<div class="prog">
<Progress />
</div>
</div>
<div class="c-wrapper rounded">
<div class="controls">
<div class="shuffle">
<div class="image"></div>
<div class="image"></div>
</div>
<HotKeys />
<div class="fav">
<div class="image"></div>
<div class="image"></div>
</div>
</div>
</div>
</div>
</template>
<script>
import { ref } from "@vue/reactivity";
import playAudio from "@/composables/playAudio.js";
import { formatSeconds, putCommas } from "@/utils";
import HotKeys from "../shared/HotKeys.vue";
import Progress from "../shared/Progress.vue";
export default {
setup() {
const current = ref(perks.current);
const putCommas = perks.putCommas;
const { playNext } = playAudio;
const { playPrev } = playAudio;
const { playPause } = playAudio;
const isPlaying = playAudio.playing;
const seek = () => {
playAudio.seek(document.getElementById("progress").value);
};
return {
current,
putCommas,
playNext,
playPrev,
playPause,
seek,
isPlaying,
formatSeconds: perks.formatSeconds,
};
},
components: { Progress, HotKeys },
};
</script>
<style lang="scss">
.now-playing {
border-radius: 0.5rem;
height: 13.5rem;
padding: 0.5rem;
// background: rgba(255, 255, 255, 0.055);
display: grid;
grid-template-rows: 3fr 1fr;
.progress {
display: flex;
.prog {
width: 100%;
display: grid;
align-items: center;
}
}
.art-tags {
display: flex;
align-items: center;
position: relative;
.t-a {
#title {
margin: 0;
width: 20rem;
color: #fff;
}
#artist {
font-size: 0.8rem;
width: 20rem;
color: $highlight-blue;
}
}
.duration {
position: absolute;
bottom: $small;
right: 0;
font-size: 0.9rem;
}
#type {
font-size: $medium;
color: $red;
padding: $smaller;
border-radius: $smaller;
position: absolute;
bottom: 0.1rem;
border: solid 1px $red;
}
.album-art {
width: 7rem;
height: 7rem;
border-radius: 0.5rem;
margin-right: 0.5rem;
background-image: url("../../assets/images/null.webp");
}
}
.c-wrapper {
background-color: $bbb;
height: 3.5rem;
padding: 0 $small;
display: grid;
align-items: center;
}
}
</style>

View File

@ -4,15 +4,12 @@
id="ginner"
tabindex="0"
class="bg-black rounded"
:class="{ 'search-focused': focused }"
>
<input
id="globalsearch"
v-model="search.query"
placeholder="Search your library"
type="search"
@focus="focused = true"
@blur="focused = false"
/>
<SearchSvg />
</div>
@ -24,7 +21,6 @@
</template>
<script setup lang="ts">
import { ref } from "vue";
import useSearchStore from "../../stores/search";
import SearchSvg from "../../assets/icons/search.svg";
import QueueSvg from "../../assets/icons/queue.svg";
@ -32,7 +28,6 @@ import useTabStore from "../../stores/tabs";
const search = useSearchStore();
const tabs = useTabStore();
const focused = ref(false);
</script>
<style lang="scss">

View File

@ -1,9 +1,5 @@
<template>
<div
class="switch rounded"
@click="toggled = !toggled"
:class="{ toggled: toggled }"
>
<div class="switch rounded" :class="{ toggled: state }">
<div class="circle circular"></div>
</div>
</template>
@ -11,7 +7,9 @@
<script setup lang="ts">
import { ref } from "vue";
const toggled = ref(false);
defineProps<{
state: boolean;
}>();
</script>
<style lang="scss">

View File

@ -4,46 +4,6 @@
v-for="(group, index) in settingGroups[current].groups"
:key="index"
:group="group"
/><Group
v-for="(group, index) in settingGroups[current].groups"
:key="index"
:group="group"
/><Group
v-for="(group, index) in settingGroups[current].groups"
:key="index"
:group="group"
/><Group
v-for="(group, index) in settingGroups[current].groups"
:key="index"
:group="group"
/><Group
v-for="(group, index) in settingGroups[current].groups"
:key="index"
:group="group"
/><Group
v-for="(group, index) in settingGroups[current].groups"
:key="index"
:group="group"
/><Group
v-for="(group, index) in settingGroups[current].groups"
:key="index"
:group="group"
/><Group
v-for="(group, index) in settingGroups[current].groups"
:key="index"
:group="group"
/><Group
v-for="(group, index) in settingGroups[current].groups"
:key="index"
:group="group"
/><Group
v-for="(group, index) in settingGroups[current].groups"
:key="index"
:group="group"
/><Group
v-for="(group, index) in settingGroups[current].groups"
:key="index"
:group="group"
/>
</div>
</template>
@ -61,9 +21,6 @@ defineProps<{
.settingscontent {
width: 100%;
max-width: 40rem;
padding-right: 1rem;
margin: 0 auto;
}
</style>

View File

@ -9,14 +9,20 @@
<div class="title">
{{ setting.title }}
</div>
<div class="options"><Switch /></div>
<div class="options">
<Switch
v-if="setting.type == SettingType.switch"
@click="setting.action()"
:state="setting.source()"
/>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { SettingGroup } from "@/interfaces/settings";
import { SettingGroup, SettingType } from "@/interfaces/settings";
import Switch from "./Components/Switch.vue";
defineProps<{
@ -30,6 +36,10 @@ defineProps<{
gap: $small;
margin-top: 2rem;
&:first-child {
margin-top: 0;
}
h4 {
margin: $small auto;
}
@ -39,6 +49,11 @@ defineProps<{
font-size: 0.9rem;
}
.setting {
display: grid;
gap: 1rem;
}
.setting > * {
display: grid;
grid-template-columns: 1fr max-content;

View File

@ -1,44 +1,43 @@
import { Track } from "../interfaces.js";
import { paths } from "../config";
export default (
track: Track,
playPause: () => void,
playNext: () => void,
playPrev: () => void
) => {
import useQueueStore from "../stores/queue";
export default () => {
if ("mediaSession" in navigator) {
const queue = useQueueStore();
navigator.mediaSession.metadata = new window.MediaMetadata({
title: track.title,
artist: track.artists.join(", "),
title: queue.currenttrack.title,
artist: queue.currenttrack.artists.join(", "),
artwork: [
{
src: paths.images.thumb + track.image,
src: paths.images.thumb + queue.currenttrack.image,
sizes: "96x96",
type: "image/jpeg",
},
{
src: paths.images.thumb + track.image,
src: paths.images.thumb + queue.currenttrack.image,
sizes: "128x128",
type: "image/webp",
},
{
src: paths.images.thumb + track.image,
src: paths.images.thumb + queue.currenttrack.image,
sizes: "192x192",
type: "image/webp",
},
{
src: paths.images.thumb + track.image,
src: paths.images.thumb + queue.currenttrack.image,
sizes: "256x256",
type: "image/webp",
},
{
src: paths.images.thumb + track.image,
src: paths.images.thumb + queue.currenttrack.image,
sizes: "384x384",
type: "image/webp",
},
{
src: paths.images.thumb + track.image,
src: paths.images.thumb + queue.currenttrack.image,
sizes: "512x512",
type: "image/webp",
},
@ -46,16 +45,16 @@ export default (
});
navigator.mediaSession.setActionHandler("play", function () {
playPause();
queue.playPause();
});
navigator.mediaSession.setActionHandler("pause", function () {
playPause();
queue.playPause();
});
navigator.mediaSession.setActionHandler("previoustrack", function () {
playPrev();
queue.playPrev();
});
navigator.mediaSession.setActionHandler("nexttrack", function () {
playNext();
queue.playNext();
});
}
};

View File

@ -13,8 +13,9 @@ export interface SettingOption {
export interface Setting {
title: string;
type: SettingType;
options: SettingOption[];
action: (arg0?: SettingOption) => void;
options?: SettingOption[];
action: (arg0?: any) => void;
source: () => any;
}
export interface SettingGroup {

View File

@ -1,15 +1,11 @@
import { SettingCategory } from "@/interfaces/settings";
import setNowPlayingComponent from "./now-playing";
console.log(setNowPlayingComponent);
import nowPlaying from "./now-playing";
export default {
title: "General",
groups: [
{
title: "Repeat queue",
desc: "Do you want to do that?",
settings: [setNowPlayingComponent()],
settings: [...nowPlaying],
},
],
} as SettingCategory;

View File

@ -1,7 +1,19 @@
import { SettingType } from "@/interfaces/settings";
import useSettingsStore from "@/stores/settings";
export default () => ({
title: "Use Sidebar now playing card",
const settings = useSettingsStore;
export default [
{
title: "Use left now playing card",
type: SettingType.switch,
action: () => console.log("should toggle something"),
});
source: () => settings().use_side_np,
action: () => settings().toggleUseSideNP(),
},
{
title: "Use right bottom now playing card",
type: SettingType.switch,
source: () => settings().use_right_np,
action: () => settings().toggleUseRightNP(),
},
];

View File

@ -3,7 +3,8 @@ import state from "../composables/state";
import { NotifType, useNotifStore } from "./notification";
import { FromOptions } from "../composables/enums";
import notif from "../composables/mediaNotification";
import updateMediaNotif from "../composables/mediaNotification";
import {
fromAlbum,
fromFolder,
@ -24,10 +25,10 @@ function shuffle(tracks: Track[]) {
type From = fromFolder | fromAlbum | fromPlaylist | fromSearch;
let audio = new Audio();
let elem: HTMLElement;
export default defineStore("Queue", {
state: () => ({
progressElem: HTMLElement,
duration: {
current: 0,
full: 0,
@ -42,14 +43,17 @@ export default defineStore("Queue", {
tracklist: [] as Track[],
}),
actions: {
bindProgressElem() {
elem = document.getElementById("progress");
},
play(index: number = 0) {
if (this.tracklist.length === 0) return;
this.current = index;
const track = this.tracklist[index];
this.currentid = track.trackid;
const uri = state.settings.uri + "/file/" + track.hash;
const elem = document.getElementById("progress") as HTMLElement;
this.updateCurrent(index);
this.bindProgressElem();
new Promise((resolve, reject) => {
audio.autoplay = true;
@ -61,12 +65,10 @@ export default defineStore("Queue", {
this.duration.full = audio.duration;
audio.play().then(() => {
this.playing = true;
notif(track, this.playPause, this.playNext, this.playPrev);
updateMediaNotif();
audio.ontimeupdate = () => {
this.duration.current = audio.currentTime;
const bg_size = (audio.currentTime / audio.duration) * 100;
elem.style.backgroundSize = `${bg_size}% 100%`;
};
audio.onended = () => {

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,24 @@
import { defineStore } from "pinia";
import useQueueStore from "../queue";
export default defineStore("settings", {
state: () => ({
use_side_np: false,
use_right_np: true,
}),
actions: {
toggleNPs() {
this.use_side_np = !this.use_side_np;
this.use_right_np = !this.use_right_np;
useQueueStore().bindProgressElem();
},
toggleUseSideNP() {
this.toggleNPs();
},
toggleUseRightNP() {
this.toggleNPs();
},
},
getters: {},
persist: true,
});

View File

@ -4,7 +4,7 @@
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
// "strict": true,
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,