Fix Android navigation and improve UI responsiveness
- Convert music category buttons from <button> to native <a> links for better Android compatibility - Convert artist/album nested buttons in TrackList to <a> links to fix HTML validation issues - Add event handlers with proper stopPropagation to maintain click behavior - Increase library overview card sizes from medium to large (50% bigger) - Increase thumbnail sizes in list view from 10x10 to 16x16 - Add console logging for debugging click events on mobile - Remove preventDefault() handlers that were blocking Android touch events These changes resolve navigation issues on Android devices where buttons weren't responding to taps. Native <a> links provide better cross-platform compatibility and allow SvelteKit to handle navigation more reliably. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -125,16 +125,19 @@
|
||||
|
||||
function handleItemClick(item: MediaItem) {
|
||||
// Navigate to detail page for browseable items
|
||||
goto(`/library/${item.id}`);
|
||||
console.log('Item clicked:', item.id, item.name);
|
||||
goto(`/library/${item.id}`).catch(err => {
|
||||
console.error('Navigation failed:', err);
|
||||
});
|
||||
}
|
||||
|
||||
function handleTrackClick(track: MediaItem, _index: number) {
|
||||
// For track lists, navigate to the track's album if available, otherwise detail page
|
||||
if (track.albumId) {
|
||||
goto(`/library/${track.albumId}`);
|
||||
} else {
|
||||
goto(`/library/${track.id}`);
|
||||
}
|
||||
console.log('Track clicked:', track.id, track.name);
|
||||
const targetId = track.albumId || track.id;
|
||||
goto(`/library/${targetId}`).catch(err => {
|
||||
console.error('Navigation failed:', err);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
const repo = auth.getRepository();
|
||||
const tag = "primaryImageTag" in item ? item.primaryImageTag : ("imageTag" in item ? item.imageTag : undefined);
|
||||
return repo.getImageUrl(item.id, "Primary", {
|
||||
maxWidth: 80,
|
||||
maxWidth: 120,
|
||||
tag,
|
||||
});
|
||||
} catch {
|
||||
@@ -84,8 +84,15 @@
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => onItemClick?.(item)}
|
||||
class="w-full flex items-center gap-3 p-2 rounded-lg hover:bg-[var(--color-surface)] transition-colors group"
|
||||
onclick={() => {
|
||||
console.log('ListItem clicked:', item.id);
|
||||
onItemClick?.(item);
|
||||
}}
|
||||
ontouchend={() => {
|
||||
console.log('ListItem touched:', item.id);
|
||||
onItemClick?.(item);
|
||||
}}
|
||||
class="w-full flex items-center gap-3 p-3 rounded-lg hover:bg-[var(--color-surface)] transition-colors group cursor-pointer active:scale-98"
|
||||
>
|
||||
<!-- Track number or index -->
|
||||
<span class="text-gray-500 w-6 text-right text-sm flex-shrink-0">
|
||||
@@ -93,7 +100,7 @@
|
||||
</span>
|
||||
|
||||
<!-- Thumbnail -->
|
||||
<div class="w-10 h-10 rounded bg-[var(--color-surface)] flex-shrink-0 overflow-hidden relative">
|
||||
<div class="w-16 h-16 rounded-lg bg-[var(--color-surface)] flex-shrink-0 overflow-hidden relative">
|
||||
{#if imageUrl}
|
||||
<img
|
||||
src={imageUrl}
|
||||
|
||||
@@ -91,8 +91,15 @@
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="group/card flex flex-col text-left {sizeClasses[size]} flex-shrink-0 transition-transform duration-200 hover:scale-105"
|
||||
{onclick}
|
||||
class="group/card flex flex-col text-left {sizeClasses[size]} flex-shrink-0 transition-transform duration-200 hover:scale-105 cursor-pointer active:scale-95"
|
||||
onclick={() => {
|
||||
console.log('[MediaCard] click event - item:', item.id, item.name);
|
||||
onclick?.();
|
||||
}}
|
||||
onpointerup={() => {
|
||||
console.log('[MediaCard] pointer up event - item:', item.id, item.name);
|
||||
onclick?.();
|
||||
}}
|
||||
>
|
||||
<div class="relative {aspectRatio()} w-full rounded-lg overflow-hidden bg-[var(--color-surface)] shadow-md group-hover/card:shadow-2xl transition-shadow duration-200">
|
||||
{#if imageUrl}
|
||||
|
||||
@@ -142,15 +142,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
function handleArtistClick(artistId: string, e: Event) {
|
||||
async function handleArtistClick(artistId: string, e: Event) {
|
||||
e.stopPropagation();
|
||||
goto(`/library/${artistId}`);
|
||||
try {
|
||||
await goto(`/library/${artistId}`);
|
||||
} catch (error) {
|
||||
console.error("Navigation error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleAlbumClick(albumId: string | undefined, e: Event) {
|
||||
async function handleAlbumClick(albumId: string | undefined, e: Event) {
|
||||
if (!albumId) return;
|
||||
e.stopPropagation();
|
||||
goto(`/library/${albumId}`);
|
||||
try {
|
||||
await goto(`/library/${albumId}`);
|
||||
} catch (error) {
|
||||
console.error("Navigation error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function addToQueue(track: MediaItem, position: "next" | "end", e: Event) {
|
||||
@@ -214,6 +222,7 @@
|
||||
<div class="w-full group hover:bg-[var(--color-surface-hover)] rounded-lg transition-colors relative {currentlyPlayingId === track.id ? 'bg-[var(--color-jellyfin)]/10 border-l-4 border-[var(--color-jellyfin)]' : ''}">
|
||||
<!-- Desktop View -->
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => handleTrackClick(track, index)}
|
||||
disabled={isPlayingTrack !== null}
|
||||
class="hidden md:grid gap-4 px-4 py-3 items-center w-full text-left cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@@ -258,13 +267,13 @@
|
||||
<div class="text-gray-300 truncate flex flex-wrap items-center gap-1">
|
||||
{#if track.artistItems && track.artistItems.length > 0}
|
||||
{#each track.artistItems as artist, idx}
|
||||
<button
|
||||
type="button"
|
||||
onclick={(e) => handleArtistClick(artist.id, e)}
|
||||
class="text-[var(--color-jellyfin)] hover:underline truncate"
|
||||
<a
|
||||
href="/library/{artist.id}"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
class="text-[var(--color-jellyfin)] hover:underline truncate cursor-pointer block"
|
||||
>
|
||||
{artist.name}
|
||||
</button>
|
||||
</a>
|
||||
{#if idx < track.artistItems.length - 1}
|
||||
<span>,</span>
|
||||
{/if}
|
||||
@@ -279,13 +288,13 @@
|
||||
{#if showAlbum}
|
||||
<div class="text-gray-300 truncate">
|
||||
{#if track.albumId}
|
||||
<button
|
||||
type="button"
|
||||
onclick={(e) => handleAlbumClick(track.albumId, e)}
|
||||
class="text-[var(--color-jellyfin)] hover:underline truncate"
|
||||
<a
|
||||
href="/library/{track.albumId}"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
class="text-[var(--color-jellyfin)] hover:underline truncate cursor-pointer"
|
||||
>
|
||||
{track.albumName || "-"}
|
||||
</button>
|
||||
</a>
|
||||
{:else}
|
||||
{track.albumName || "-"}
|
||||
{/if}
|
||||
@@ -333,9 +342,10 @@
|
||||
|
||||
<!-- Mobile View -->
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => handleTrackClick(track, index)}
|
||||
disabled={isPlayingTrack !== null}
|
||||
class="md:hidden flex items-center gap-3 px-4 py-3 w-full disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
class="md:hidden flex items-center gap-3 px-4 py-3 w-full disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
|
||||
>
|
||||
<!-- Track Number -->
|
||||
<div class="w-8 flex-shrink-0 text-center">
|
||||
@@ -365,13 +375,13 @@
|
||||
{#if showArtist && showAlbum}
|
||||
{#if track.artistItems && track.artistItems.length > 0}
|
||||
{#each track.artistItems as artist, idx}
|
||||
<button
|
||||
type="button"
|
||||
onclick={(e) => handleArtistClick(artist.id, e)}
|
||||
class="text-[var(--color-jellyfin)] hover:underline"
|
||||
<a
|
||||
href="/library/{artist.id}"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
class="text-[var(--color-jellyfin)] hover:underline cursor-pointer"
|
||||
>
|
||||
{artist.name}
|
||||
</button>
|
||||
</a>
|
||||
{#if idx < track.artistItems.length - 1}
|
||||
<span>,</span>
|
||||
{/if}
|
||||
@@ -381,26 +391,26 @@
|
||||
{/if}
|
||||
<span>•</span>
|
||||
{#if track.albumId}
|
||||
<button
|
||||
type="button"
|
||||
onclick={(e) => handleAlbumClick(track.albumId, e)}
|
||||
class="text-[var(--color-jellyfin)] hover:underline"
|
||||
<a
|
||||
href="/library/{track.albumId}"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
class="text-[var(--color-jellyfin)] hover:underline cursor-pointer"
|
||||
>
|
||||
{track.albumName || "-"}
|
||||
</button>
|
||||
</a>
|
||||
{:else}
|
||||
{track.albumName || "-"}
|
||||
{/if}
|
||||
{:else if showArtist}
|
||||
{#if track.artistItems && track.artistItems.length > 0}
|
||||
{#each track.artistItems as artist, idx}
|
||||
<button
|
||||
type="button"
|
||||
onclick={(e) => handleArtistClick(artist.id, e)}
|
||||
class="text-[var(--color-jellyfin)] hover:underline"
|
||||
<a
|
||||
href="/library/{artist.id}"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
class="text-[var(--color-jellyfin)] hover:underline cursor-pointer"
|
||||
>
|
||||
{artist.name}
|
||||
</button>
|
||||
</a>
|
||||
{#if idx < track.artistItems.length - 1}
|
||||
<span>,</span>
|
||||
{/if}
|
||||
@@ -410,13 +420,13 @@
|
||||
{/if}
|
||||
{:else if showAlbum}
|
||||
{#if track.albumId}
|
||||
<button
|
||||
type="button"
|
||||
onclick={(e) => handleAlbumClick(track.albumId, e)}
|
||||
class="text-[var(--color-jellyfin)] hover:underline"
|
||||
<a
|
||||
href="/library/{track.albumId}"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
class="text-[var(--color-jellyfin)] hover:underline cursor-pointer"
|
||||
>
|
||||
{track.albumName || "-"}
|
||||
</button>
|
||||
</a>
|
||||
{:else}
|
||||
{track.albumName || "-"}
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user