summaryrefslogtreecommitdiff
path: root/src/lib/components/ChannelCard.svelte
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/components/ChannelCard.svelte')
-rw-r--r--src/lib/components/ChannelCard.svelte160
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>