mirror of
https://github.com/maciejpedzich/spotifyplaylistarchive.com.git
synced 2024-11-14 01:03:03 +01:00
166 lines
4.4 KiB
Vue
166 lines
4.4 KiB
Vue
<!-- eslint-disable vue/multi-word-component-names -->
|
|
<script setup lang="ts">
|
|
import ProgressSpinner from 'primevue/progressspinner';
|
|
import Datepicker from '@vuepic/vue-datepicker';
|
|
|
|
import { CalendarEntry } from '~~/models/calendar-entry';
|
|
|
|
// Moving this interfaces to separate files makes TypeScript scream at you
|
|
interface DateChangePayload {
|
|
month: number;
|
|
year: number;
|
|
}
|
|
|
|
defineProps<{ page: 'snapshot-pick' | 'compare' }>();
|
|
|
|
const route = useRoute();
|
|
const playlistId = route.params.playlistId;
|
|
|
|
const now = new Date();
|
|
const queryMonth = useState(`queryMonth${playlistId}`, () => now.getMonth());
|
|
const queryYear = useState(`queryYear${playlistId}`, () => now.getFullYear());
|
|
|
|
const displayDate = new Date(queryYear.value, queryMonth.value);
|
|
const minDate = new Date('2021-12-01');
|
|
|
|
const hoursOffset = -(displayDate.getTimezoneOffset() / 60);
|
|
const sinceDateParam = computed(() =>
|
|
new Date(queryYear.value, queryMonth.value, 1, hoursOffset)
|
|
.toISOString()
|
|
.substring(0, 10)
|
|
);
|
|
const untilDateParam = computed(() =>
|
|
new Date(queryYear.value, queryMonth.value + 1, 1, hoursOffset)
|
|
.toISOString()
|
|
.substring(0, 10)
|
|
);
|
|
|
|
const queryString = computed(() => {
|
|
const queryParamsObj = new URLSearchParams();
|
|
|
|
queryParamsObj.set('sinceDate', sinceDateParam.value);
|
|
queryParamsObj.set('untilDate', untilDateParam.value);
|
|
|
|
return queryParamsObj.toString();
|
|
});
|
|
|
|
const {
|
|
pending: loadingCalendarEntries,
|
|
error: calendarEntriesLoadError,
|
|
data: calendarEntries,
|
|
refresh: reloadCalendarEntries
|
|
} = useFetch<CalendarEntry[]>(
|
|
() => `/api/playlists/${playlistId}/snapshots?${queryString.value}`,
|
|
{
|
|
key: `snapshots-calendar-of-${playlistId}`,
|
|
server: false
|
|
}
|
|
);
|
|
|
|
const snapshotLinkMap = computed<Record<string, string>>(() =>
|
|
(calendarEntries.value || []).reduce((map, entry) => {
|
|
const dayCapturedKey = entry.dateCaptured.substring(8, 10);
|
|
|
|
map[dayCapturedKey] = `./snapshots/show/${entry.commitSha}`;
|
|
|
|
return map;
|
|
}, {})
|
|
);
|
|
const allowedDates = computed(() =>
|
|
Object.keys(JSON.parse(JSON.stringify(snapshotLinkMap.value))).map(
|
|
(dayKey) =>
|
|
new Date(queryYear.value, queryMonth.value, Number(dayKey), hoursOffset)
|
|
)
|
|
);
|
|
|
|
const updateQueryAndReload = async ({ month, year }: DateChangePayload) => {
|
|
queryMonth.value = month;
|
|
queryYear.value = year;
|
|
|
|
await reloadCalendarEntries();
|
|
};
|
|
|
|
const getSnapshotLinkFromDate = (day: number) =>
|
|
snapshotLinkMap.value[day.toString().padStart(2, '0')];
|
|
|
|
const isDayCaptured = (day: number) => !!getSnapshotLinkFromDate(day);
|
|
|
|
const isQueryMonth = (date: Date) => date.getMonth() === queryMonth.value;
|
|
</script>
|
|
|
|
<template>
|
|
<NuxtLayout name="centered-content">
|
|
<ClientOnly>
|
|
<ProgressSpinner
|
|
v-if="loadingCalendarEntries"
|
|
class="abosulte top-0 right-0"
|
|
/>
|
|
<p
|
|
v-else-if="!loadingCalendarEntries && calendarEntriesLoadError"
|
|
class="text-2xl"
|
|
>
|
|
Something went wrong while fetching archive entries
|
|
</p>
|
|
<Datepicker
|
|
v-show="!(loadingCalendarEntries || calendarEntriesLoadError)"
|
|
v-model="displayDate"
|
|
class="md:px-0 px-4"
|
|
:min-date="minDate"
|
|
:max-date="now"
|
|
:allowed-dates="allowedDates"
|
|
:enable-time-picker="false"
|
|
:month-change-on-arrows="false"
|
|
:month-change-on-scroll="false"
|
|
no-swipe
|
|
no-today
|
|
prevent-min-max-navigation
|
|
inline
|
|
dark
|
|
@update-month-year="updateQueryAndReload"
|
|
>
|
|
<template #day="{ day, date }">
|
|
<NuxtLink
|
|
v-if="isQueryMonth(date) && isDayCaptured(day)"
|
|
class="w-full h-full"
|
|
:to="getSnapshotLinkFromDate(day)"
|
|
>
|
|
<div
|
|
class="w-full h-full flex justify-content-center align-items-center bg-primary hover:bg-green-400 text-0 border-round"
|
|
>
|
|
{{ day }}
|
|
</div>
|
|
</NuxtLink>
|
|
<div v-else class="m-2">
|
|
{{ day }}
|
|
</div>
|
|
</template>
|
|
</Datepicker>
|
|
</ClientOnly>
|
|
</NuxtLayout>
|
|
</template>
|
|
|
|
<style scoped>
|
|
:deep(div.dp__calendar_header) {
|
|
width: 100%;
|
|
padding: 0.4rem 0.2rem;
|
|
}
|
|
|
|
:deep(div.dp__cell_inner) {
|
|
margin: 0.5rem;
|
|
padding: 0;
|
|
}
|
|
|
|
:deep(div.dp__active_date) {
|
|
background-color: transparent;
|
|
}
|
|
|
|
:deep(div.dp__overlay_cell_active) {
|
|
background-color: var(--primary-color);
|
|
color: var(--surface-0);
|
|
}
|
|
|
|
:deep(div.dp__action_row:last-of-type) {
|
|
display: none;
|
|
}
|
|
</style>
|