Radish alpha
r
Radicle desktop app
Radicle
Git (anonymous pull)
Log in to clone via SSH
Preserve releases filter through navigation
Daniel Norman committed 7 days ago
commit a3f03afe9cafc599f556cfae203553fa923e551b
parent be27955f9072fbf4762c50ba50e4f4eff200b3d6
5 files changed +64 -21
modified src/components/ReleaseTeaser.svelte
@@ -1,4 +1,5 @@
<script lang="ts">
+
  import type { ReleaseFilter } from "@app/views/repo/router";
  import type { Release } from "@bindings/cob/release/Release";

  import { push } from "@app/lib/router";
@@ -11,9 +12,10 @@
  interface Props {
    release: Release;
    rid: string;
+
    filter: ReleaseFilter;
  }

-
  const { release, rid }: Props = $props();
+
  const { release, rid, filter }: Props = $props();
</script>

<style>
@@ -103,6 +105,7 @@
      resource: "repo.release",
      rid,
      release: release.id,
+
      filter,
    });
  }}>
  <div class="left">
modified src/lib/router/definitions.ts
@@ -131,7 +131,11 @@ export async function loadRoute(
      return loaded;
    }
    // Fall back to the list when a release id no longer exists.
-
    return loadReleases({ resource: "repo.releases", rid: route.rid });
+
    return loadReleases({
+
      resource: "repo.releases",
+
      rid: route.rid,
+
      filter: route.filter,
+
    });
  }
  return route;
}
modified src/views/repo/Release.svelte
@@ -1,4 +1,5 @@
<script lang="ts">
+
  import type { ReleaseFilter } from "@app/views/repo/router";
  import type { Artifact } from "@bindings/cob/release/Artifact";
  import type { Release } from "@bindings/cob/release/Release";
  import type { Config } from "@bindings/config/Config";
@@ -30,9 +31,10 @@
    repo: RepoInfo;
    release: Release;
    config: Config;
+
    filter: ReleaseFilter;
  }

-
  const { repo, release: releaseProp, config }: Props = $props();
+
  const { repo, release: releaseProp, config, filter }: Props = $props();

  let release = $state(releaseProp);
  $effect(() => {
@@ -727,8 +729,9 @@
            router.push({
              resource: "repo.releases",
              rid: repo.rid,
+
              filter,
            })}>
-
          Releases
+
          {filter === "delegate" ? "Delegate releases" : "All releases"}
        </button>
        <Icon name="chevron-right" />
        <Id id={release.id} clipboard={release.id} placement="bottom-start" />
modified src/views/repo/Releases.svelte
@@ -1,9 +1,11 @@
<script lang="ts">
+
  import type { ReleaseFilter } from "@app/views/repo/router";
  import type { Release } from "@bindings/cob/release/Release";
  import type { RepoInfo } from "@bindings/repo/RepoInfo";

  import fuzzysort from "fuzzysort";

+
  import * as router from "@app/lib/router";
  import { modifierKey } from "@app/lib/utils";

  import Button from "@app/components/Button.svelte";
@@ -19,17 +21,22 @@
  interface Props {
    repo: RepoInfo;
    releases: Release[];
+
    filter: ReleaseFilter;
  }

-
  const { repo, releases }: Props = $props();
+
  const { repo, releases, filter }: Props = $props();

  let showNew = $state(false);
  let searchInput = $state("");
  let showSearch = $state(false);
-
  // Default to delegate releases so users don't see arbitrary peer
-
  // releases unless they opt in. State is local — filter doesn't
-
  // round-trip through the URL.
-
  let trustFilter = $state<"delegate" | "all">("delegate");
+

+
  async function setFilter(next: ReleaseFilter) {
+
    await router.push({
+
      resource: "repo.releases",
+
      rid: repo.rid,
+
      filter: next,
+
    });
+
  }

  const delegateDids = $derived(repo.delegates.map(d => d.did));

@@ -59,7 +66,7 @@
  );

  const visibleReleases = $derived(
-
    trustFilter === "delegate" ? delegateReleases : baseReleases,
+
    filter === "delegate" ? delegateReleases : baseReleases,
  );

  const searchable = $derived(
@@ -144,8 +151,8 @@
      <div class="filters">
        <button
          class="filter"
-
          class:active={trustFilter === "delegate"}
-
          onclick={() => (trustFilter = "delegate")}
+
          class:active={filter === "delegate"}
+
          onclick={() => void setFilter("delegate")}
          title="Only releases authored by repo delegates">
          <Icon name="badge" />
          <span class="filter-label">Delegates</span>
@@ -153,8 +160,8 @@
        </button>
        <button
          class="filter"
-
          class:active={trustFilter === "all"}
-
          onclick={() => (trustFilter = "all")}
+
          class:active={filter === "all"}
+
          onclick={() => void setFilter("all")}
          title="Every release published to this repo">
          <Icon name="archive" />
          <span class="filter-label">All</span>
@@ -189,7 +196,7 @@
    <ScrollArea style="height: 100%; min-width: 0;">
      <div class="list">
        {#each searchResults as result (result.obj.release.id)}
-
          <ReleaseTeaser release={result.obj.release} rid={repo.rid} />
+
          <ReleaseTeaser release={result.obj.release} rid={repo.rid} {filter} />
        {/each}

        {#if searchResults.length === 0}
@@ -201,7 +208,7 @@
            <div class="txt-missing txt-body-m-regular">
              {#if visibleReleases.length > 0}
                No matching releases
-
              {:else if trustFilter === "delegate" && baseReleases.length > 0}
+
              {:else if filter === "delegate" && baseReleases.length > 0}
                No releases from delegates yet
              {:else}
                No releases yet
modified src/views/repo/router.ts
@@ -152,9 +152,15 @@ export interface LoadedRepoPatchesRoute {
  };
}

+
// Releases list filter. `delegate` is the default; threaded through to
+
// the release detail route so the breadcrumb can navigate back to the
+
// same filtered list.
+
export type ReleaseFilter = "delegate" | "all";
+

export interface RepoReleasesRoute {
  resource: "repo.releases";
  rid: string;
+
  filter?: ReleaseFilter;
}

export interface LoadedRepoReleasesRoute {
@@ -162,6 +168,7 @@ export interface LoadedRepoReleasesRoute {
  params: {
    repo: RepoInfo;
    releases: Release[];
+
    filter: ReleaseFilter;
    sidebarData: SidebarData;
  };
}
@@ -170,6 +177,7 @@ export interface RepoReleaseRoute {
  resource: "repo.release";
  rid: string;
  release: string;
+
  filter?: ReleaseFilter;
}

export interface LoadedRepoReleaseRoute {
@@ -179,6 +187,7 @@ export interface LoadedRepoReleaseRoute {
    config: Config;
    releases: Release[];
    release: Release;
+
    filter: ReleaseFilter;
    sidebarData: SidebarData;
  };
}
@@ -403,7 +412,7 @@ export async function loadReleases(

  return {
    resource: "repo.releases",
-
    params: { sidebarData, repo, releases },
+
    params: { sidebarData, repo, releases, filter: route.filter ?? "delegate" },
  };
}

@@ -432,6 +441,7 @@ export async function loadRelease(
      config: sidebarData.config,
      release,
      releases,
+
      filter: route.filter ?? "delegate",
    },
  };
}
@@ -497,9 +507,20 @@ export function repoRouteToPath(route: RepoRoute): string {
    }
    return url;
  } else if (route.resource === "repo.releases") {
-
    return [...pathSegments, "releases"].join("/");
+
    let url = [...pathSegments, "releases"].join("/");
+
    // Only serialize a non-default filter so the canonical URL stays clean.
+
    if (route.filter && route.filter !== "delegate") {
+
      searchParams.set("filter", route.filter);
+
      url += `?${searchParams}`;
+
    }
+
    return url;
  } else if (route.resource === "repo.release") {
-
    return [...pathSegments, "releases", route.release].join("/");
+
    let url = [...pathSegments, "releases", route.release].join("/");
+
    if (route.filter && route.filter !== "delegate") {
+
      searchParams.set("filter", route.filter);
+
      url += `?${searchParams}`;
+
    }
+
    return url;
  } else {
    return unreachable(route);
  }
@@ -563,10 +584,15 @@ export function repoUrlToRoute(
      }
    } else if (resource === "releases") {
      const id = segments.shift();
+
      const filterParam = searchParams.get("filter");
+
      const filter: ReleaseFilter | undefined =
+
        filterParam === "all" || filterParam === "delegate"
+
          ? filterParam
+
          : undefined;
      if (id) {
-
        return { resource: "repo.release", rid, release: id };
+
        return { resource: "repo.release", rid, release: id, filter };
      }
-
      return { resource: "repo.releases", rid };
+
      return { resource: "repo.releases", rid, filter };
    } else {
      return null;
    }