diff --git a/src/ui/PodcastView/EpisodeList.svelte b/src/ui/PodcastView/EpisodeList.svelte index ffaa9ea..16a418c 100644 --- a/src/ui/PodcastView/EpisodeList.svelte +++ b/src/ui/PodcastView/EpisodeList.svelte @@ -5,10 +5,12 @@ import { hidePlayedEpisodes, playedEpisodes } from "src/store"; import Icon from "../obsidian/Icon.svelte"; import Text from "../obsidian/Text.svelte"; + import Loading from "./Loading.svelte"; export let episodes: Episode[] = []; export let showThumbnails: boolean = false; export let showListMenu: boolean = true; + export let isLoading: boolean = false; let searchInputQuery: string = ""; const dispatch = createEventDispatcher(); @@ -63,7 +65,13 @@ {/if}
No episodes found.
{/if} {#each episodes as episode (episode.url || episode.streamUrl || `${episode.title}-${episode.episodeDate ?? ""}`)} @@ -115,4 +123,13 @@ width: 100%; margin-bottom: 0.5rem; } + + .episode-list-loading { + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; + padding: 1rem 0; + color: var(--text-muted); + } diff --git a/src/ui/PodcastView/PodcastView.integration.test.ts b/src/ui/PodcastView/PodcastView.integration.test.ts index 5d7895a..0341fc0 100644 --- a/src/ui/PodcastView/PodcastView.integration.test.ts +++ b/src/ui/PodcastView/PodcastView.integration.test.ts @@ -1,4 +1,4 @@ -import { fireEvent, render, screen } from "@testing-library/svelte"; +import { fireEvent, render, screen, waitFor } from "@testing-library/svelte"; import { get } from "svelte/store"; import { afterEach, @@ -144,4 +144,89 @@ describe("PodcastView integration flow", () => { expect.objectContaining({ path: expectedPath }), ); }); + + test("shows loading state while fetching and streams episodes per feed", async () => { + const secondFeed: PodcastFeed = { + title: "Second Podcast", + url: "https://pod.example.com/feed-two.xml", + artworkUrl: "https://pod.example.com/art-two.jpg", + }; + + const firstEpisode: Episode = { + title: "Episode A", + streamUrl: "https://pod.example.com/a.mp3", + url: "https://pod.example.com/a", + description: "Episode A description", + content: "Episode A content
", + podcastName: testFeed.title, + artworkUrl: testFeed.artworkUrl, + episodeDate: new Date("2024-02-01T00:00:00.000Z"), + }; + + const secondEpisode: Episode = { + title: "Episode B", + streamUrl: "https://pod.example.com/b.mp3", + url: "https://pod.example.com/b", + description: "Episode B description", + content: "Episode B content
", + podcastName: secondFeed.title, + artworkUrl: secondFeed.artworkUrl, + episodeDate: new Date("2024-01-15T00:00:00.000Z"), + }; + + let resolveFirstFeed!: (value: Episode[]) => void; + let resolveSecondFeed!: (value: Episode[]) => void; + + mockGetEpisodes + .mockImplementationOnce( + () => + new Promise