Radish alpha
r
rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5
Radicle web interface
Radicle
Git
radicle-explorer src views repos Patches.svelte
<script lang="ts">
  import type { BaseUrl, Patch, PatchState, Repo } from "@http-client";

  import { HttpdClient } from "@http-client";

  import { PATCHES_PER_PAGE } from "./router";
  import { baseUrlToString } from "@app/lib/utils";

  import Button from "@app/components/Button.svelte";
  import ErrorMessage from "@app/components/ErrorMessage.svelte";
  import Icon from "@app/components/Icon.svelte";
  import Layout from "./Layout.svelte";
  import Link from "@app/components/Link.svelte";
  import List from "@app/components/List.svelte";
  import Loading from "@app/components/Loading.svelte";
  import PatchTeaser from "./Patch/PatchTeaser.svelte";
  import Placeholder from "@app/components/Placeholder.svelte";
  import Separator from "./Separator.svelte";

  export let baseUrl: BaseUrl;
  export let patches: Patch[];
  export let repo: Repo;
  export let status: PatchState["status"];
  export let nodeAvatarUrl: string | undefined;

  let loading = false;
  let page = 0;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  let error: any;
  let allPatches: Patch[];

  $: {
    allPatches = patches;
    page = 0;
  }

  const api = new HttpdClient(baseUrl);

  async function loadMore(status: PatchState["status"]): Promise<void> {
    loading = true;
    page += 1;
    try {
      const response = await api.repo.getAllPatches(repo.rid, {
        status,
        page,
        perPage: PATCHES_PER_PAGE,
      });
      allPatches = [...allPatches, ...response];
    } catch (e) {
      error = e;
    } finally {
      loading = false;
    }
  }

  $: showMoreButton =
    !loading &&
    !error &&
    allPatches.length <
      repo.payloads["xyz.radicle.project"].meta.patches[status];
</script>

<style>
  .header {
    display: flex;
    gap: 0.25rem;
    padding: 1rem;
    border-bottom: 1px solid var(--color-border-subtle);
  }
  .more {
    margin-top: 2rem;
    min-height: 3rem;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .counter {
    border-radius: var(--border-radius-sm);
    background-color: var(--color-surface-mid);
    color: var(--color-text-tertiary);
    padding: 0 0.25rem;
    min-width: 1.5rem;
    text-align: center;
  }
  .selected {
    background-color: var(--color-surface-alpha-subtle);
    color: var(--color-text-primary);
  }
  .title-counter {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }
  .placeholder {
    height: calc(100% - 4rem);
    display: flex;
    align-items: center;
    justify-content: center;
  }
  @media (max-width: 719.98px) {
    .placeholder {
      height: calc(100vh - 10rem);
    }
    .title-counter:not(.active) {
      display: none;
    }
  }
</style>

<Layout {nodeAvatarUrl} {baseUrl} {repo} activeTab="patches">
  <svelte:fragment slot="breadcrumb">
    <Separator />
    <Link
      route={{
        resource: "repo.patches",
        repo: repo.rid,
        node: baseUrl,
      }}>
      Patches
    </Link>
  </svelte:fragment>
  <div slot="header" class="header">
    <Link
      route={{
        resource: "repo.patches",
        repo: repo.rid,
        node: baseUrl,
        search: "status=open",
      }}>
      <Button variant={status === "open" ? "gray" : "background"}>
        <Icon name="patch" />
        <div class="title-counter" class:active={status === "open"}>
          Open
          <span class="counter" class:selected={status === "open"}>
            {repo.payloads["xyz.radicle.project"].meta.patches.open}
          </span>
        </div>
      </Button>
    </Link>
    <Link
      route={{
        resource: "repo.patches",
        repo: repo.rid,
        node: baseUrl,
        search: "status=draft",
      }}>
      <Button variant={status === "draft" ? "gray" : "background"}>
        <Icon name="patch-draft" />
        <div class="title-counter" class:active={status === "draft"}>
          Draft
          <span class="counter" class:selected={status === "draft"}>
            {repo.payloads["xyz.radicle.project"].meta.patches.draft}
          </span>
        </div>
      </Button>
    </Link>
    <Link
      route={{
        resource: "repo.patches",
        repo: repo.rid,
        node: baseUrl,
        search: "status=archived",
      }}>
      <Button variant={status === "archived" ? "gray" : "background"}>
        <Icon name="patch-archived" />
        <div class="title-counter" class:active={status === "archived"}>
          Archived
          <span class="counter" class:selected={status === "archived"}>
            {repo.payloads["xyz.radicle.project"].meta.patches.archived}
          </span>
        </div>
      </Button>
    </Link>
    <Link
      route={{
        resource: "repo.patches",
        repo: repo.rid,
        node: baseUrl,
        search: "status=merged",
      }}>
      <Button variant={status === "merged" ? "gray" : "background"}>
        <Icon name="patch-merged" />
        <div class="title-counter" class:active={status === "merged"}>
          Merged
          <span class="counter" class:selected={status === "merged"}>
            {repo.payloads["xyz.radicle.project"].meta.patches.merged}
          </span>
        </div>
      </Button>
    </Link>
  </div>

  <List items={allPatches}>
    <PatchTeaser
      slot="item"
      let:item
      {baseUrl}
      repoId={repo.rid}
      patch={item} />
  </List>

  {#if error}
    <ErrorMessage
      title="Couldn't load patches"
      description="Please make sure you are able to connect to the seed <code>{baseUrlToString(
        api.baseUrl,
      )}</code>"
      {error} />
  {/if}

  {#if repo.payloads["xyz.radicle.project"].meta.patches[status] === 0}
    <div class="placeholder">
      <Placeholder iconName="no-patches" caption={`No ${status} patches`} />
    </div>
  {/if}

  {#if loading || showMoreButton}
    <div class="more">
      {#if loading}
        <div style:margin-top={page === 0 ? "8rem" : ""}>
          <Loading noDelay small={page !== 0} center />
        </div>
      {/if}

      {#if showMoreButton}
        <Button
          size="large"
          variant="outline"
          on:click={() => loadMore(status)}>
          More
        </Button>
      {/if}
    </div>
  {/if}
</Layout>