summaryrefslogtreecommitdiff
path: root/src/routes/subscriptions/+page.svelte
diff options
context:
space:
mode:
authorThomas Grothe <grothe.tr@gmail.com>2026-03-07 23:32:05 -0500
committerThomas Grothe <grothe.tr@gmail.com>2026-03-07 23:32:05 -0500
commitdbd1386a43ae9e7013809be2e0bd0e1c049059fc (patch)
tree22588cb21dfa1cc941e13031e73cb85cdfb7f402 /src/routes/subscriptions/+page.svelte
good startHEADmain
Diffstat (limited to 'src/routes/subscriptions/+page.svelte')
-rw-r--r--src/routes/subscriptions/+page.svelte151
1 files changed, 151 insertions, 0 deletions
diff --git a/src/routes/subscriptions/+page.svelte b/src/routes/subscriptions/+page.svelte
new file mode 100644
index 0000000..9f318ac
--- /dev/null
+++ b/src/routes/subscriptions/+page.svelte
@@ -0,0 +1,151 @@
+<script lang="ts">
+ import { subscriptions } from '$lib/stores/subscriptions';
+ import { getChannel, type VideoInfo } from '$lib/api/youtube';
+ import VideoCard from '$lib/components/VideoCard.svelte';
+ import ChannelCard from '$lib/components/ChannelCard.svelte';
+
+ let videos = $state<VideoInfo[]>([]);
+ let loading = $state(true);
+ let error = $state('');
+ let activeTab = $state<'feed' | 'channels'>('feed');
+
+ $effect(() => {
+ if ($subscriptions.length > 0 && activeTab === 'feed') {
+ loadFeed();
+ } else if ($subscriptions.length === 0) {
+ loading = false;
+ }
+ });
+
+ async function loadFeed() {
+ loading = true;
+ error = '';
+
+ try {
+ const results = await Promise.all(
+ $subscriptions.map(sub =>
+ getChannel(sub.channelId).catch(() => ({ videos: [] }))
+ )
+ );
+
+ const allVideos = results.flatMap(r => r.videos);
+ videos = allVideos.slice(0, 50);
+ } catch (e) {
+ error = e instanceof Error ? e.message : 'Failed to load feed';
+ } finally {
+ loading = false;
+ }
+ }
+</script>
+
+<svelte:head>
+ <title>Subscriptions - ActualYT</title>
+</svelte:head>
+
+<div class="container">
+ <div class="header">
+ <h1 class="section-title">Subscriptions</h1>
+ <div class="tabs">
+ <button
+ class="tab"
+ class:active={activeTab === 'feed'}
+ onclick={() => activeTab = 'feed'}
+ >
+ Feed
+ </button>
+ <button
+ class="tab"
+ class:active={activeTab === 'channels'}
+ onclick={() => activeTab = 'channels'}
+ >
+ Channels ({$subscriptions.length})
+ </button>
+ </div>
+ </div>
+
+ {#if $subscriptions.length === 0}
+ <div class="empty">
+ <p>You haven't subscribed to any channels yet.</p>
+ <p>Find channels you like and click Subscribe to see their videos here.</p>
+ </div>
+ {:else if activeTab === 'feed'}
+ {#if loading}
+ <div class="loading">Loading your feed</div>
+ {:else if error}
+ <div class="error">{error}</div>
+ {:else if videos.length === 0}
+ <div class="empty">No recent videos from your subscriptions</div>
+ {:else}
+ <div class="video-grid">
+ {#each videos as video (video.videoId)}
+ <VideoCard
+ videoId={video.videoId}
+ title={video.title}
+ author={video.author}
+ authorId={video.authorId}
+ thumbnails={video.videoThumbnails}
+ viewCount={video.viewCount}
+ publishedText={video.publishedText}
+ lengthSeconds={video.lengthSeconds}
+ />
+ {/each}
+ </div>
+ {/if}
+ {:else}
+ <div class="channel-list">
+ {#each $subscriptions as sub (sub.channelId)}
+ <ChannelCard
+ channelId={sub.channelId}
+ channelName={sub.channelName}
+ thumbnails={[{ url: sub.thumbnail, width: 88, height: 88 }]}
+ />
+ {/each}
+ </div>
+ {/if}
+</div>
+
+<style>
+ .header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: 1rem;
+ margin-bottom: 1.5rem;
+ }
+
+ .section-title {
+ margin-bottom: 0;
+ }
+
+ .tabs {
+ display: flex;
+ gap: 0.5rem;
+ }
+
+ .tab {
+ padding: 0.5rem 1rem;
+ border: none;
+ border-radius: 20px;
+ background: var(--bg-secondary);
+ color: var(--text-secondary);
+ font-size: 0.9rem;
+ cursor: pointer;
+ transition: background 0.15s ease, color 0.15s ease;
+ }
+
+ .tab:hover {
+ background: var(--bg-hover);
+ }
+
+ .tab.active {
+ background: var(--text-primary);
+ color: var(--bg-primary);
+ }
+
+ .channel-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ }
+</style>