fix: populate podcast dropdown from sources, filter via API
The podcast filter dropdown was built from loaded episodes only, so podcasts with older episodes didn't appear until scrolling. Now uses the full sources list and queries the API server-side when filtering by a specific podcast. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+34
-12
@@ -53,8 +53,8 @@ permalink: /podroll/
|
|||||||
class="w-full sm:w-auto appearance-none bg-surface-100 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg px-4 py-2 pr-10 text-sm text-surface-700 dark:text-surface-300 focus:outline-none focus:ring-2 focus:ring-primary-500"
|
class="w-full sm:w-auto appearance-none bg-surface-100 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg px-4 py-2 pr-10 text-sm text-surface-700 dark:text-surface-300 focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||||
>
|
>
|
||||||
<option value="all">All Podcasts</option>
|
<option value="all">All Podcasts</option>
|
||||||
<template x-for="podcast in uniquePodcasts" :key="podcast">
|
<template x-for="source in sortedSources" :key="source.title">
|
||||||
<option :value="podcast" x-text="podcast"></option>
|
<option :value="source.title" x-text="source.title"></option>
|
||||||
</template>
|
</template>
|
||||||
</select>
|
</select>
|
||||||
<svg class="absolute right-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-surface-500 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="absolute right-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-surface-500 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -248,6 +248,8 @@ function podrollApp() {
|
|||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
await this.fetchData();
|
await this.fetchData();
|
||||||
|
// Re-fetch from API when podcast filter changes
|
||||||
|
this.$watch('filterPodcast', () => this.fetchEpisodes());
|
||||||
// Auto-refresh every 5 minutes
|
// Auto-refresh every 5 minutes
|
||||||
this.refreshInterval = setInterval(() => this.fetchData(true), 5 * 60 * 1000);
|
this.refreshInterval = setInterval(() => this.fetchData(true), 5 * 60 * 1000);
|
||||||
},
|
},
|
||||||
@@ -265,7 +267,7 @@ function podrollApp() {
|
|||||||
|
|
||||||
this.episodes = episodesRes.items || [];
|
this.episodes = episodesRes.items || [];
|
||||||
this.hasMore = episodesRes.hasMore || false;
|
this.hasMore = episodesRes.hasMore || false;
|
||||||
this.offset = episodesRes.offset || 0;
|
this.offset = 0;
|
||||||
this.sources = sourcesRes.items || [];
|
this.sources = sourcesRes.items || [];
|
||||||
this.status = statusRes;
|
this.status = statusRes;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -276,12 +278,36 @@ function podrollApp() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async fetchEpisodes() {
|
||||||
|
this.loading = true;
|
||||||
|
this.error = null;
|
||||||
|
this.offset = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let url = `/podrollapi/api/episodes?limit=${this.limit}`;
|
||||||
|
if (this.filterPodcast !== 'all') {
|
||||||
|
url += `&source=${encodeURIComponent(this.filterPodcast)}`;
|
||||||
|
}
|
||||||
|
const res = await fetch(url).then(r => r.json());
|
||||||
|
this.episodes = res.items || [];
|
||||||
|
this.hasMore = res.hasMore || false;
|
||||||
|
} catch (err) {
|
||||||
|
this.error = 'Failed to load episodes: ' + err.message;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async loadMore() {
|
async loadMore() {
|
||||||
this.loadingMore = true;
|
this.loadingMore = true;
|
||||||
const newOffset = this.offset + this.limit;
|
const newOffset = this.offset + this.limit;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/podrollapi/api/episodes?limit=${this.limit}&offset=${newOffset}`).then(r => r.json());
|
let url = `/podrollapi/api/episodes?limit=${this.limit}&offset=${newOffset}`;
|
||||||
|
if (this.filterPodcast !== 'all') {
|
||||||
|
url += `&source=${encodeURIComponent(this.filterPodcast)}`;
|
||||||
|
}
|
||||||
|
const res = await fetch(url).then(r => r.json());
|
||||||
this.episodes = [...this.episodes, ...(res.items || [])];
|
this.episodes = [...this.episodes, ...(res.items || [])];
|
||||||
this.hasMore = res.hasMore || false;
|
this.hasMore = res.hasMore || false;
|
||||||
this.offset = newOffset;
|
this.offset = newOffset;
|
||||||
@@ -293,20 +319,16 @@ function podrollApp() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async refresh() {
|
async refresh() {
|
||||||
|
this.filterPodcast = 'all';
|
||||||
await this.fetchData();
|
await this.fetchData();
|
||||||
},
|
},
|
||||||
|
|
||||||
get filteredEpisodes() {
|
get filteredEpisodes() {
|
||||||
if (this.filterPodcast === 'all') return this.episodes;
|
return this.episodes;
|
||||||
return this.episodes.filter(ep => ep.podcast?.title === this.filterPodcast);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
get uniquePodcasts() {
|
get sortedSources() {
|
||||||
const podcasts = new Set();
|
return [...this.sources].sort((a, b) => a.title.localeCompare(b.title));
|
||||||
this.episodes.forEach(ep => {
|
|
||||||
if (ep.podcast?.title) podcasts.add(ep.podcast.title);
|
|
||||||
});
|
|
||||||
return Array.from(podcasts).sort();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
formatDate(dateStr, format = 'short') {
|
formatDate(dateStr, format = 'short') {
|
||||||
|
|||||||
Reference in New Issue
Block a user