Radish alpha
r
Radicle desktop app
Radicle
Git (anonymous pull)
Log in to clone via SSH
Filter releases list to delegate-authored by default
Daniel Norman committed 7 days ago
commit a74b5b4c9d139db7d391ce26edf99967287e0f0a
parent 6e84f2091d7ee66f546c5956bc9ebf48377298d5
1 file changed +74 -1
modified src/views/repo/Releases.svelte
@@ -26,6 +26,10 @@
  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");

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

@@ -44,10 +48,20 @@

  // Releases without artifacts are placeholders the user hasn't published
  // anything to yet — keep them off the list so it shows actual deliverables.
-
  const visibleReleases = $derived(
+
  const baseReleases = $derived(
    releases.filter(r => r.artifacts.length > 0 && !isFullyRedacted(r)),
  );

+
  // Pre-compute the delegate set so the counter badges stay in sync with
+
  // whatever the active filter is showing.
+
  const delegateReleases = $derived(
+
    baseReleases.filter(r => delegateDids.includes(r.creator.did)),
+
  );
+

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

  const searchable = $derived(
    visibleReleases.map(release => ({
      release,
@@ -84,12 +98,69 @@
    gap: 1px;
    min-height: 100%;
  }
+
  .filters {
+
    display: flex;
+
    align-items: center;
+
    gap: 0.25rem;
+
  }
+
  .filter {
+
    display: flex;
+
    align-items: center;
+
    gap: 0.25rem;
+
    font: var(--txt-body-m-regular);
+
    color: var(--color-text-secondary);
+
    padding: 0.25rem 0.5rem;
+
    border-radius: var(--border-radius-sm);
+
    background: none;
+
    border: none;
+
    cursor: pointer;
+
    white-space: nowrap;
+
  }
+
  .filter:hover {
+
    background-color: var(--color-surface-subtle);
+
    color: var(--color-text-primary);
+
  }
+
  .filter.active {
+
    background-color: var(--color-surface-subtle);
+
    color: var(--color-text-primary);
+
  }
+
  .filter-label {
+
    display: none;
+
  }
+
  .filter.active .filter-label {
+
    display: inline;
+
  }
+
  @media (min-width: 1011px) {
+
    .filter-label {
+
      display: inline;
+
    }
+
  }
</style>

<Layout selfScroll>
  <div class="page">
    <Topbar>
      <span class="topbar-title">Releases</span>
+
      <div class="filters">
+
        <button
+
          class="filter"
+
          class:active={trustFilter === "delegate"}
+
          onclick={() => (trustFilter = "delegate")}
+
          title="Only releases authored by repo delegates">
+
          <Icon name="badge" />
+
          <span class="filter-label">Delegates</span>
+
          <span class="global-counter-badge">{delegateReleases.length}</span>
+
        </button>
+
        <button
+
          class="filter"
+
          class:active={trustFilter === "all"}
+
          onclick={() => (trustFilter = "all")}
+
          title="Every release published to this repo">
+
          <Icon name="archive" />
+
          <span class="filter-label">All</span>
+
          <span class="global-counter-badge">{baseReleases.length}</span>
+
        </button>
+
      </div>
      <div class="global-flex" style:margin-left="auto" style:gap="0.5rem">
        <FuzzySearch
          hasItems={visibleReleases.length > 0}
@@ -130,6 +201,8 @@
            <div class="txt-missing txt-body-m-regular">
              {#if visibleReleases.length > 0}
                No matching releases
+
              {:else if trustFilter === "delegate" && baseReleases.length > 0}
+
                No releases from delegates yet
              {:else}
                No releases yet
              {/if}