Implement copying track URLs and JSON export

This commit is contained in:
Maciej Pędzich 2023-01-23 22:32:02 +01:00
parent db14f68aa1
commit 7260c771a2
3 changed files with 115 additions and 1 deletions

View File

@ -0,0 +1,36 @@
---
import type { PlaylistSnapshot } from '@/models/playlist-snapshot';
interface Props {
snapshot: PlaylistSnapshot;
}
const { snapshot } = Astro.props;
---
<script>
import type { PlaylistSnapshot } from '@/models/playlist-snapshot';
class ExportToJsonLink extends HTMLElement {
constructor() {
super();
const snapshot = JSON.parse(this.dataset.snapshot!) as PlaylistSnapshot;
const anchor = this.querySelector('a')!;
anchor.href = URL.createObjectURL(
new Blob([JSON.stringify(snapshot, null, '\t')])
);
anchor.download = `${snapshot.original_name}.json`;
}
}
customElements.define('export-to-json-link', ExportToJsonLink);
</script>
<export-to-json-link data-snapshot={JSON.stringify(snapshot)}>
<a class="btn btn-ghost text-base normal-case">
<i class="fa-solid fa-file-arrow-down mr-3"></i>
Export to JSON
</a>
</export-to-json-link>

View File

@ -0,0 +1,69 @@
<script setup lang="ts">
import { ref } from 'vue';
const props = defineProps<{ urls: string[] }>();
const textToCopy = ref('');
const copySuccess = ref<boolean | null>(null);
const copyTrackUrls = async () => {
try {
copySuccess.value = null;
textToCopy.value = props.urls.join('\n');
if (!navigator.clipboard) {
throw new Error('Clipboard API is not supported');
}
const { state: clipboardWritePerm } = await navigator.permissions.query({
name: 'clipboard-write' as PermissionName
});
if (clipboardWritePerm === 'denied') {
throw new Error('Permission to write to clipboard has been denied');
}
await navigator.clipboard.writeText(textToCopy.value);
copySuccess.value = true;
setTimeout(() => (copySuccess.value = null), 5000);
} catch (error) {
copySuccess.value = false;
}
};
</script>
<template>
<button
class="btn btn-ghost hover:text-primary-focus text-base normal-case"
@click="copyTrackUrls()"
>
<i class="fa-solid fa-clipboard-list mr-3"></i>
Copy Track URLs
</button>
<Teleport to="body">
<template v-if="copySuccess !== null">
<div v-if="copySuccess === true" class="toast toast-center">
<div class="alert alert-success min-w-max">
<div>
<span>URLs have been copied!</span>
</div>
</div>
</div>
<div v-else class="modal modal-open">
<div class="modal-box md:max-w-xl max-w-xs text-center">
<h3 class="font-bold text-2xl">Error</h3>
<p class="py-4 text-md">
Failed to copy track URLs! Select and manually copy the text below:
</p>
<div class="w-full max-h-72 overflow-y-auto">
<pre>{{ textToCopy }}</pre>
</div>
<div class="modal-action justify-center">
<button class="btn" @click="copySuccess = null">Done</button>
</div>
</div>
</div>
</template>
</Teleport>
</template>

View File

@ -6,11 +6,15 @@ import PlaylistPageTab from '@/layouts/PlaylistPageTab.astro';
import { getPlaylistLayoutProps } from '@/utils/getPlaylistLayoutProps';
import { formatDate } from '@/utils/formatDate';
import CopyTrackUrlsButton from '@/components/vue/CopyTrackUrlsButton.vue';
import ExportToJsonLink from '@/components/ExportToJsonLink.astro';
import type { PlaylistSnapshot } from '@/models/playlist-snapshot';
const layoutProps = await getPlaylistLayoutProps(Astro);
let snapshot: PlaylistSnapshot | null = null;
let trackUrls: string[] = [];
if (layoutProps.playlist) {
try {
@ -24,6 +28,7 @@ if (layoutProps.playlist) {
}
snapshot = (await githubResponse.json()) as PlaylistSnapshot;
trackUrls = snapshot.tracks.map(({ url }) => url);
layoutProps.description = decode(snapshot.description, { level: 'html5' });
} catch (error) {
const isNotFoundError = (error as Error).message === 'GitHub 404';
@ -42,9 +47,13 @@ if (layoutProps.playlist) {
<PlaylistPageTab {...layoutProps}>
<p class="text-lg">{layoutProps.description}</p>
<div class="w-full flex justify-center content-center gap-1">
<CopyTrackUrlsButton urls={trackUrls} client:only="vue" />
<ExportToJsonLink snapshot={snapshot!} />
</div>
<div class="lg:max-w-full md:max-w-2xl max-w-sm px-5 overflow-x-auto">
<table
class="table datatable mt-4 mb-8 border-[1px] border-b-0 border-r-0 border-base-content border-opacity-20"
class="table datatable mb-10 border-[1px] border-b-0 border-r-0 border-base-content border-opacity-20"
>
<thead>
<tr>