diff options
| author | Thomas Grothe <grothe.tr@gmail.com> | 2026-03-07 23:32:05 -0500 |
|---|---|---|
| committer | Thomas Grothe <grothe.tr@gmail.com> | 2026-03-07 23:32:05 -0500 |
| commit | dbd1386a43ae9e7013809be2e0bd0e1c049059fc (patch) | |
| tree | 22588cb21dfa1cc941e13031e73cb85cdfb7f402 /src/lib/components/ChannelCard.svelte | |
Diffstat (limited to 'src/lib/components/ChannelCard.svelte')
| -rw-r--r-- | src/lib/components/ChannelCard.svelte | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/src/lib/components/ChannelCard.svelte b/src/lib/components/ChannelCard.svelte new file mode 100644 index 0000000..8747efa --- /dev/null +++ b/src/lib/components/ChannelCard.svelte @@ -0,0 +1,160 @@ +<script lang="ts"> + import type { VideoThumbnail } from '$lib/api/youtube'; + import { subscriptions } from '$lib/stores/subscriptions'; + + interface Props { + channelId: string; + channelName: string; + thumbnails: VideoThumbnail[]; + subCount?: number; + videoCount?: number; + showSubscribeButton?: boolean; + } + + let { + channelId, + channelName, + thumbnails, + subCount, + videoCount, + showSubscribeButton = true + }: Props = $props(); + + const thumbnail = $derived(thumbnails[0]?.url || ''); + + const isSubscribed = $derived(subscriptions.isSubscribed($subscriptions, channelId)); + + function formatSubCount(count: number | undefined): string { + if (!count) return ''; + if (count >= 1000000) return `${(count / 1000000).toFixed(1)}M subscribers`; + if (count >= 1000) return `${(count / 1000).toFixed(1)}K subscribers`; + return `${count} subscribers`; + } + + function handleSubscribe(e: Event) { + e.preventDefault(); + e.stopPropagation(); + if (isSubscribed) { + subscriptions.remove(channelId); + } else { + subscriptions.add(channelId, channelName, thumbnails); + } + } +</script> + +<a href="/channel/{channelId}" class="channel-card"> + <div class="thumbnail"> + {#if thumbnail} + <img src={thumbnail} alt={channelName} loading="lazy" /> + {:else} + <div class="placeholder">{channelName[0]}</div> + {/if} + </div> + <div class="info"> + <h3 class="name">{channelName}</h3> + <div class="meta"> + {#if subCount} + <span>{formatSubCount(subCount)}</span> + {/if} + {#if videoCount} + <span class="separator">•</span> + <span>{videoCount} videos</span> + {/if} + </div> + </div> + {#if showSubscribeButton} + <button + class="subscribe-btn" + class:subscribed={isSubscribed} + onclick={handleSubscribe} + > + {isSubscribed ? 'Subscribed' : 'Subscribe'} + </button> + {/if} +</a> + +<style> + .channel-card { + display: flex; + align-items: center; + gap: 1rem; + padding: 1rem; + text-decoration: none; + color: inherit; + border-radius: 8px; + transition: background 0.15s ease; + } + + .channel-card:hover { + background: var(--bg-hover); + } + + .thumbnail { + width: 80px; + height: 80px; + border-radius: 50%; + overflow: hidden; + background: var(--bg-secondary); + flex-shrink: 0; + } + + .thumbnail img { + width: 100%; + height: 100%; + object-fit: cover; + } + + .placeholder { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + font-size: 2rem; + font-weight: 600; + color: var(--text-muted); + text-transform: uppercase; + } + + .info { + flex: 1; + min-width: 0; + } + + .name { + font-size: 1rem; + font-weight: 500; + margin: 0 0 0.25rem; + color: var(--text-primary); + } + + .meta { + font-size: 0.85rem; + color: var(--text-muted); + } + + .separator { + margin: 0 0.25rem; + } + + .subscribe-btn { + padding: 0.5rem 1rem; + border-radius: 20px; + border: none; + font-size: 0.9rem; + font-weight: 500; + cursor: pointer; + background: var(--accent-color); + color: #fff; + transition: opacity 0.15s ease; + } + + .subscribe-btn:hover { + opacity: 0.9; + } + + .subscribe-btn.subscribed { + background: var(--bg-secondary); + color: var(--text-secondary); + } +</style> |
