Add loading and "no playlists found" states

This commit is contained in:
Maciej Pędzich 2023-01-12 20:42:33 +01:00
parent 29117560c1
commit 1839ec9609
2 changed files with 59 additions and 27 deletions

View File

@ -4,9 +4,9 @@ import { onMounted, ref, watch } from 'vue';
import { search } from 'fast-fuzzy';
import { debounce } from 'debounce';
import type { PlaylistSnapshot } from '../../models/playlist-snapshot';
import type { SearchSuggestion } from '../../models/search-suggestion';
import { getPlaylistIdFromUrl } from '../../utils/getPlaylistIdFromUrl';
import type { PlaylistSnapshot } from '@/models/playlist-snapshot';
import type { SearchSuggestion } from '@/models/search-suggestion';
import { getPlaylistIdFromUrl } from '@/utils/getPlaylistIdFromUrl';
const searchTerm = ref('');
const searchHistory = ref<SearchSuggestion[]>(
@ -17,19 +17,24 @@ const playlistRegistry = ref<SearchSuggestion[]>([]);
const searchSuggestions = ref<SearchSuggestion[]>(searchHistory.value);
const isFetchingPlaylists = ref(true);
const errorOccurred = ref(false);
const registryFetchErrorOccurred = ref(false);
const isPreparingSuggestions = ref(false);
const canShowSuggestions = ref(false);
const fetchAvailablePlaylists = async () => {
try {
isFetchingPlaylists.value = true;
errorOccurred.value = false;
registryFetchErrorOccurred.value = false;
const playlistMetadata: Record<string, PlaylistSnapshot> = await (
await fetch(
'https://raw.githubusercontent.com/mackorone/spotify-playlist-archive/main/playlists/metadata.json'
)
).json();
const githubResponse = await fetch(
'https://raw.githubusercontent.com/mackorone/spotify-playlist-archive/main/playlists/metadata.json'
);
if (!githubResponse.ok) throw new Error(githubResponse.statusText);
const playlistMetadata: Record<string, PlaylistSnapshot> =
await githubResponse.json();
const playlistEntries = Object.entries(playlistMetadata).map(
([id, { original_name }]) => ({ id, name: original_name })
@ -38,7 +43,7 @@ const fetchAvailablePlaylists = async () => {
playlistRegistry.value = playlistEntries;
} catch (error) {
console.error(error);
errorOccurred.value = true;
registryFetchErrorOccurred.value = true;
} finally {
isFetchingPlaylists.value = false;
}
@ -48,6 +53,8 @@ const showSearchHistory = () => (searchSuggestions.value = searchHistory.value);
const findMatchingPlaylists = debounce(async (name: string) => {
try {
isPreparingSuggestions.value = true;
if (name.length === 0) return showSearchHistory();
else if (name.length < 3) return (searchSuggestions.value = []);
@ -56,24 +63,26 @@ const findMatchingPlaylists = debounce(async (name: string) => {
`https://raw.githubusercontent.com/mackorone/spotify-playlist-archive/main/playlists/pretty/${playlistId}.json`
);
if (!githubResponse.ok) throw new Error('Entry does not exist');
if (!githubResponse.ok)
throw new Error(`GitHub ${githubResponse.status.toString()}`);
const playlist: PlaylistSnapshot = await githubResponse.json();
const suggestion = { id: playlistId, name: playlist.original_name };
return (searchSuggestions.value = [suggestion]);
searchSuggestions.value = [suggestion];
} catch (error) {
const noopErrorMessages = ['Invalid playlist URL', 'Entry does not exist'];
const errorMessage = (error as Error).message;
if (noopErrorMessages.includes((error as Error).message)) {
return (searchSuggestions.value = []);
}
if (errorMessage === 'GitHub 404') return (searchSuggestions.value = []);
const suggestions = search(name, playlistRegistry.value, {
keySelector: (obj) => obj.name
});
searchSuggestions.value = suggestions.slice(0, 5);
} finally {
isPreparingSuggestions.value = false;
canShowSuggestions.value = true;
}
}, 250);
@ -98,7 +107,10 @@ watch(searchTerm, (newSearchTerm) => findMatchingPlaylists(newSearchTerm));
v-if="isFetchingPlaylists"
class="fa-solid fa-spinner fa-spin text-5xl"
></i>
<div v-else-if="errorOccurred" class="alert alert-error shadow-lg">
<div
v-else-if="registryFetchErrorOccurred"
class="alert alert-error shadow-lg"
>
<div>
<span>Failed to load playlist registry</span>
</div>
@ -121,16 +133,32 @@ watch(searchTerm, (newSearchTerm) => findMatchingPlaylists(newSearchTerm));
v-show="canShowSuggestions"
class="w-full absolute z-10 top-full left-0 right-0 bg-base-100 text-left border-l-[1px] border-r-[1px] border-solid border-base-content border-opacity-20"
>
<a
v-for="suggestion in searchSuggestions"
class="block p-3 text-inherit border-b-[1px] hover:bg-primary hover:text-primary-content focus:bg-primary focus:text-primary-content border-solid border-base-content border-opacity-20"
:href="`/playlists/${suggestion.id}/snapshots`"
@mousedown.prevent="saveMatchToHistory(suggestion)"
<div
v-if="isPreparingSuggestions"
class="search-suggestion text-xl text-center"
>
<!-- Changing the above to click will cause blur to fire first, -->
<!-- thus removing the anchor before navigation is triggered -->
{{ suggestion.name }}
</a>
<i class="fa-solid fa-spinner fa-spin"></i>
</div>
<template v-else>
<div
v-if="searchTerm.length > 3 && searchSuggestions.length === 0"
class="search-suggestion text-center"
>
<em>No archived playlists were found</em>
</div>
<template v-else>
<a
v-for="suggestion in searchSuggestions"
class="search-suggestion hover:bg-primary hover:text-primary-content focus:bg-primary focus:text-primary-content"
:href="`/playlists/${suggestion.id}/snapshots`"
@mousedown.prevent="saveMatchToHistory(suggestion)"
>
<!-- Changing the above to click will cause blur to fire first, -->
<!-- thus removing the anchor before navigation is triggered -->
{{ suggestion.name }}
</a>
</template>
</template>
</div>
</div>
</template>

View File

@ -17,3 +17,7 @@ a {
li > a {
@apply hover:text-inherit focus:text-primary-content focus:bg-primary;
}
.search-suggestion {
@apply block p-3 text-inherit border-b-[1px] border-solid border-base-content border-opacity-20;
}