add darkmode and progress bar

master
adb-sh 2 years ago
parent 27ab4c870d
commit 4f7b922428

@ -1,9 +1,13 @@
<script setup lang="ts">
import DarkModeToggle from "@/components/DarkmodeToggle.vue";
</script>
<template>
<div v-if="$route.meta.allowEmbed && $route.query.embed === 'true'" class="p-2">
<router-view />
</div>
<div v-else>
<div class="bg-secondary shadow">
<div class="bg-darkmode-dark bg-light shadow sticky-top">
<nav class="navbar px-2 container">
<router-link class="d-flex btn" to="/">
<div class="d-flex header-title flex-column justify-content-center">
@ -11,14 +15,18 @@
<div>music connects</div>
</div>
</router-link>
<div>
<div class="d-flex align-items-center">
<router-link
v-if="!$api.isAuthorized()"
to="/auth"
class="btn btn-outline-dark"
class="btn btn-outline"
>
login
</router-link>
<DarkModeToggle class="mx-2" v-slot="{ state }">
<i v-if="state" class="bi-moon"></i>
<i v-else class="bi-sun"></i>
</DarkModeToggle>
</div>
</nav>
</div>
@ -31,8 +39,6 @@
</template>
<style lang="scss">
@import "main.scss";
nav {
a {
font-weight: bold;

@ -1,8 +1,9 @@
<script setup lang="ts">
import { defineProps } from "vue";
import TrackProgressBar from "@/components/TrackProgressBar.vue";
defineProps({
currentlyPlaying: Object,
currentlyPlaying: Object
});
</script>
@ -11,19 +12,23 @@ defineProps({
<div class="card-header">
<b>{{ currentlyPlaying?.item.name }}</b>
<div>
{{ currentlyPlaying?.item.artists.map(artist => artist.name).join(', ') }}
{{ currentlyPlaying?.item.artists.map(artist => artist.name).join(", ") }}
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md">
<div class="row justify-content-center">
<div class="col coverGroup">
<img :src="currentlyPlaying.item.album.images[0].url" alt="album cover" class="card-img">
<p v-if="currentlyPlaying.context">
listening from {{ currentlyPlaying?.context.type }}
</p>
<TrackProgressBar
:duration="currentlyPlaying?.item.duration"
:progress="currentlyPlaying.progress"
:isPlaying="currentlyPlaying.isPlaying"
/>
</div>
<div class="col-md">
<p class="my-2">
</div>
</div>
<div class="card-footer">
<div class="">
<a
:href="currentlyPlaying?.item.externalURL.spotify"
target="_blank"
@ -41,8 +46,6 @@ defineProps({
>
view {{ currentlyPlaying?.context.type }} on Spotify
</a>
</p>
</div>
</div>
</div>
</div>
@ -50,7 +53,7 @@ defineProps({
<style scoped lang="scss">
.currentlyPlaying {
.card-img {
.coverGroup {
max-width: 20rem;
}
}

@ -1,5 +1,6 @@
<script setup lang="ts">
import { defineProps } from "vue";
import TrackProgressBar from "@/components/TrackProgressBar.vue";
defineProps({
currentlyPlaying: Object,
@ -18,6 +19,11 @@ defineProps({
<div>
{{ currentlyPlaying?.item.artists.map(artist => artist.name).join(', ') }}
</div>
<TrackProgressBar
:duration="currentlyPlaying?.item.duration"
:progress="currentlyPlaying.progress"
:isPlaying="currentlyPlaying.isPlaying"
/>
</div>
</div>
</div>

@ -0,0 +1,23 @@
<template>
<div class="form-check form-switch">
<label class="custom-control-label" for="darkSwitch">
<slot :state="sliderState" />
</label>
<input v-model="sliderState" type="checkbox" class="form-check-input" />
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import { themeConfig } from '@/main';
const sliderState = ref(themeConfig.getTheme() === 'dark');
watch(sliderState, (state) => {
themeConfig.setTheme(state ? 'dark' : 'light');
});
themeConfig.themeChangeHandlers.push((newTheme) => {
sliderState.value = newTheme === 'dark';
});
</script>

@ -52,14 +52,13 @@ const update = async (promise: Promise | unknown) => {
loading.value = true;
if (props.throbber) setTimeout(() => {
if (loading.value) showThrobber.value = true;
}, 250);
}, 500);
try {
data.value = await (promise.isPromiseList
? Promise.all(promise.promises)
: promise);
} catch (e) {
error.value = e;
console.error('PR', e);
} finally {
loading.value = false;
showThrobber.value = false;

@ -0,0 +1,30 @@
<script setup lang="ts">
import { defineProps, watch, ref } from "vue";
const props = defineProps({
seconds: Number,
});
const getHmsFromSeconds = (d: number) => ({
hours: Math.floor(d / 3600),
minutes: Math.floor(d % 3600 / 60),
seconds: Math.floor(d % 3600 % 60),
});
const getLeadingZero = (a: number, digits: number) =>
('0'.repeat(digits) + a.toString())
.substr(- digits);
const hms = ref(getHmsFromSeconds(props.seconds as number));
watch(() => props.seconds, (to) => {
hms.value = getHmsFromSeconds(to as number);
});
</script>
<template>
<span v-if="hms.hours" class="hours">{{ getLeadingZero(hms.hours, 2) }}:</span>
<span class="minutes">{{ getLeadingZero(hms.minutes, 2) }}:</span>
<span class="seconds">{{ getLeadingZero(hms.seconds, 2) }}</span>
</template>

@ -0,0 +1,52 @@
<script setup lang="ts">
import { defineProps, ref, watch, onBeforeUnmount } from "vue";
import TimeFormatter from "@/components/TimeFormatter.vue";
const props = defineProps({
duration: Number,
progress: Number,
isPlaying: Boolean,
});
const estimatedProgress = ref(props.progress as number);
const updateInterval = setInterval(() => {
if (props.isPlaying) estimatedProgress.value += 1000;
}, 1000);
watch(() => props.progress, (to) => {
estimatedProgress.value = to as number;
});
onBeforeUnmount(() => {
clearInterval(updateInterval);
});
</script>
<template>
<div>
<div class="progress my-2">
<div
class="progress-bar"
role="progressbar"
:style="{
width: `${ estimatedProgress / duration * 100 }%`
}"
/>
</div>
<div class="row my-2">
<div class="col">
<TimeFormatter :seconds="estimatedProgress / 1000"/>
</div>
<div class="col-auto">
<TimeFormatter :seconds="duration / 1000"/>
</div>
</div>
</div>
</template>
<style scoped>
.progress {
height: 0.5rem;
}
</style>

@ -1,8 +1,7 @@
@import "bootstrap/scss/bootstrap.scss";
//@import "bootstrap-darkmode/scss/darktheme.scss";
@import "../node_modules/bootstrap/scss/bootstrap.scss";
@import "../node_modules/bootstrap-darkmode/scss/darktheme.scss";
html, body {
//background-color: $dark-body-bg;
line-break: loose;
word-break: break-word;
}

@ -3,6 +3,12 @@ import App from "./App.vue";
import "./registerServiceWorker";
import router from "./router";
import { createApi } from "@/Api";
import 'bootstrap-icons/font/bootstrap-icons.scss';
import './main.scss';
import { ThemeConfig } from 'bootstrap-darkmode';
export const themeConfig = new ThemeConfig();
themeConfig.initTheme();
createApp(App)
.use(router)

@ -13,7 +13,7 @@ const userInfo = ref(api?.getUserInfo(route.params.id as string));
let refreshUserInfo = setInterval(() => {
userInfo.value = api?.getUserInfo(route.params.id as string);
}, 20000);
}, 10000);
onBeforeUnmount(() => {
clearInterval(refreshUserInfo);
@ -43,10 +43,7 @@ onBeforeUnmount(() => {
v-slot="{ data: { user, currentlyPlaying } }"
class="row"
>
<div v-if="$route.query.embed === 'true'">
</div>
<div v-else class="col-md-4">
<div class="col-md-4">
<div class="card">
<div class="card-header">
{{ user.displayName }}
@ -80,6 +77,9 @@ onBeforeUnmount(() => {
<div class="col">
<h2>Currently listening to:</h2>
<CurrentlyPlaying v-if="currentlyPlaying?.item" :currently-playing="currentlyPlaying" />
<p v-else class="alert alert-info">
{{ user.displayName }} is not listening to music.
</p>
</div>
</PromiseResolver>
</div>

Loading…
Cancel
Save