"It works" was a lie

dracoblue May 25, 2026
Source

My next #notai generated post is about another day of developing the kiesel app. This dev day was a bit sad. The happy path of each implementation worked, but as soon as I tried it in real usage: fail. Refresh mode The list view of kiesel has support for pull-to-refresh. The last changes somehow made the list after pull-to-refresh: empty. Each of the tabs: empty. That means everything needs to be reloaded and the cache didn't work. The reason was that the refresh mode was implemented in such a way that it replaced the array with: page 1. And if the cache filled many other pages - pull to refresh made it empty. My new setFeed implementation respects a mode now: setFeed((prev) => { if (mode === "initial") { // Merge fresh posts with cached: fresh posts win on overlap if (prev.length === 0) return allNewPosts; const freshKeys = new Set(allNewPosts.map((p) => ${p.protocol}:${p.uri})); const cachedOnly = prev.filter((p) => !freshKeys.has(${p.protocol}:${p.uri})); // Fresh first (newest), then remaining cached (older) const merged = [...allNewPosts, ...cachedOnly]; merged.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); return merged; }

    // Merge: deduplicate by URI
    const seen = new Set(prev.map((p) => `${p.protocol}:${p.uri}`));
    const unique = allNewPosts.filter(
      (p) => !seen.has(`${p.protocol}:${p.uri}`)
    );

    if (mode === "refresh") {
        // Prepend new posts
      return [...unique, ...prev];
    }
    // mode === "more": append
    return [...prev, ...unique];
  });

and this way it can take care of initial loading, refreshing when prepending (posts happening in the future) new posts or more when appending old posts (scroll further). Since I want to keep the cursors for pagination API valid, I need to update those. If this does not happen, the pagination starts again at page 1. oEmbed endpoint For the Apple Podcasts oEmbed endpoint I found one stating something like embed podcasts apple com (I won't post it here for you to not copy it wrong) and the result looked good. I started the app and embedded Apple Podcasts entry and it looked ugly. And the JSON was HTML, no JSON at all. I was confused. After some debugging and checking podcasts at e.g. https://podcasts.apple.com/de/podcast/sound-memes-der-k%C3%BCrzeste-comedy-podcast-der-welt/id1729757656 I found:

and noticed my url was completely wrong. It is just: https://podcasts.apple.com/api/oembed?url= and this one works. The one before looked promising, too. But it was wrong. 4x useEffect or one state The embed component has 4 async steps: detect provider check consent fetch oEmbed render player as webview My first implementation included 4 different useEffect calls, each of them checking for one variable for state change. That looks slim at the beginning. But I ran into race conditions in multiple ways. Duplicated entries and such things. To make it less complex, we had to introduce a new EmbedState which holds the overall state for the implementation of the embed component: type EmbedState = | { status: "loading" } | { status: "no-provider" } | { status: "needs-consent"; provider: ProviderConfig } | { status: "fetching"; provider: ProviderConfig } | { status: "oembed-ready"; provider: ProviderConfig; html: string; width?: number; height?: number } | { status: "direct-embed"; provider: ProviderConfig; embedUrl: string } | { status: "error"; provider: ProviderConfig | null }; and one useEffect, which knows what it's used for: useEffect(() => { const init = async () => { const provider = getProviderForUrl(uri); if (!provider || (!provider.oembedEndpoint && !provider.buildEmbedUrl)) { setState({ status: "no-provider" }); return; }
  const consented = await hasConsent(provider.id);
  if (!consented) {
    setState({ status: "needs-consent", provider });
    return;
  }

  loadEmbed(provider);
};
init();

}, [uri]); And it has just one dependency uri. Looks nice? ;) See you in the next dev day post!

Discussion in the ATmosphere

Loading comments...