Radish alpha
r
rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5
Radicle web interface
Radicle
Git
Don't query the /profile endpoint on every project load
Merged rudolfs opened 2 years ago

We load the preferred seed config value on demand when the user clicks the share button.

check check-visual check-unit-test check-httpd-api-unit-test check-e2e check-build

👉 Preview
👉 Workflow runs
👉 Branch on GitHub

11 files changed +89 -139 7ba9429a 09065938
modified src/views/projects/Commit.svelte
@@ -12,8 +12,6 @@
  export let baseUrl: BaseUrl;
  export let commit: Commit;
  export let project: Project;
-
  export let preferredSeeds: string[];
-
  export let publicExplorer: string;

  $: header = commit.commit;
</script>
@@ -48,7 +46,7 @@
            stripEmphasizedStyling
            fontSize="large"
            content={header.summary} />
-
          <Share {preferredSeeds} {publicExplorer} {baseUrl} />
+
          <Share {baseUrl} />
        </span>
        <CommitAuthorship {header}>
          <span class="global-commit">{formatCommit(header.id)}</span>
modified src/views/projects/Header/ShareButton.svelte
@@ -1,10 +1,13 @@
<script lang="ts">
  import type { ProjectRoute } from "@app/views/projects/router";

-
  import { queryProject } from "@app/lib/projects";
+
  import { onMount } from "svelte";
+

+
  import { activeUnloadedRouteStore, routeToPath } from "@app/lib/router";
+
  import { api } from "@app/lib/httpd";
  import { config } from "@app/lib/config";
  import { formatPublicExplorer } from "@app/lib/utils";
-
  import { activeUnloadedRouteStore, routeToPath } from "@app/lib/router";
+
  import { queryProject } from "@app/lib/projects";

  import Clipboard from "@app/components/Clipboard.svelte";
  import Command from "@app/components/Command.svelte";
@@ -12,37 +15,42 @@
  import Loading from "@app/components/Loading.svelte";
  import IconButton from "@app/components/IconButton.svelte";

-
  export let preferredSeeds: string[];
-
  export let publicExplorer: string;
-

  let usedFallbackSeed = false;

-
  const route = $activeUnloadedRouteStore as ProjectRoute;
-
  const seedRoutes = preferredSeeds.reduce<ProjectRoute[]>((acc, seed) => {
-
    const [, address] = seed.split("@");
-
    acc.push({
-
      ...route,
-
      node: {
-
        hostname: address.split(":")[0],
-
        port: config.nodes.defaultHttpdPort,
-
        scheme: config.nodes.defaultHttpdScheme,
-
      },
-
    });
-
    return acc;
-
  }, []);
+
  let publicExplorer: string;
+
  let seedRoutes: ProjectRoute[] = [];

-
  // Set seed.radicle.garden as fallback seed.
-
  $: if (preferredSeeds.length === 0) {
-
    usedFallbackSeed = true;
-
    seedRoutes.push({
-
      ...route,
-
      node: {
-
        hostname: config.nodes.defaultHttpdHostname,
-
        port: config.nodes.defaultHttpdPort,
-
        scheme: config.nodes.defaultHttpdScheme,
-
      },
+
  onMount(async () => {
+
    const profile = await api.profile.getProfile().catch(() => undefined);
+
    const route = $activeUnloadedRouteStore as ProjectRoute;
+
    publicExplorer =
+
      profile?.config.publicExplorer || config.nodes.fallbackPublicExplorer;
+
    const preferredSeeds = profile?.config.preferredSeeds || [];
+

+
    seedRoutes = preferredSeeds.map(seed => {
+
      const [, address] = seed.split("@");
+
      return {
+
        ...route,
+
        node: {
+
          hostname: address.split(":")[0],
+
          port: config.nodes.defaultHttpdPort,
+
          scheme: config.nodes.defaultHttpdScheme,
+
        },
+
      };
    });
-
  }
+

+
    if (preferredSeeds.length === 0) {
+
      usedFallbackSeed = true;
+
      seedRoutes.push({
+
        ...route,
+
        node: {
+
          hostname: config.nodes.defaultHttpdHostname,
+
          port: config.nodes.defaultHttpdPort,
+
          scheme: config.nodes.defaultHttpdScheme,
+
        },
+
      });
+
    }
+
  });
</script>

<style>
@@ -137,7 +145,12 @@
              </IconButton>
              <IconButton>
                <a
-
                  href={path}
+
                  href={formatPublicExplorer(
+
                    publicExplorer,
+
                    seed.node.hostname,
+
                    seed.project,
+
                    path,
+
                  )}
                  target="_blank"
                  style=" width: 1.5rem;
                height: 1.5rem; display: flex; align-items: center; justify-content: center;">
modified src/views/projects/History.svelte
@@ -32,8 +32,6 @@
  export let totalCommitCount: number;
  export let tree: Tree;
  export let seeding: boolean;
-
  export let preferredSeeds: string[];
-
  export let publicExplorer: string;

  const api = new HttpdClient(baseUrl);

@@ -112,13 +110,7 @@
</style>

<Layout {baseUrl} {project} activeTab="source">
-
  <ProjectNameHeader
-
    {project}
-
    {baseUrl}
-
    {seeding}
-
    {preferredSeeds}
-
    {publicExplorer}
-
    slot="header" />
+
  <ProjectNameHeader {project} {baseUrl} {seeding} slot="header" />

  <div style:margin="1rem 0 1rem 1rem" slot="subheader">
    <Header
modified src/views/projects/Issue.svelte
@@ -46,8 +46,6 @@
  export let baseUrl: BaseUrl;
  export let issue: Issue;
  export let project: Project;
-
  export let preferredSeeds: string[];
-
  export let publicExplorer: string;
  export let rawPath: (commit?: string) => string;

  const api = new HttpdClient(baseUrl);
@@ -512,7 +510,7 @@
              </Button>
            {/if}
            {#if issueState === "read"}
-
              <Share {preferredSeeds} {publicExplorer} {baseUrl} />
+
              <Share {baseUrl} />
              {#if session && role.isDelegateOrAuthor(session.publicKey, project.delegates, issue.author.id)}
                <CobStateButton
                  items={items.filter(
modified src/views/projects/Issues.svelte
@@ -26,8 +26,6 @@
  export let issues: Issue[];
  export let project: Project;
  export let state: IssueState["status"];
-
  export let preferredSeeds: string[];
-
  export let publicExplorer: string;

  let loading = false;
  let page = 0;
@@ -147,7 +145,7 @@
    </Popover>

    <div style="margin-left: auto; display: flex; gap: 1rem;">
-
      <Share {preferredSeeds} {publicExplorer} {baseUrl} />
+
      <Share {baseUrl} />
      {#if $httpdStore.state === "authenticated" && isLocal(baseUrl.hostname)}
        <Link
          route={{
modified src/views/projects/Patch.svelte
@@ -86,8 +86,6 @@
  export let rawPath: (commit?: string) => string;
  export let project: Project;
  export let view: PatchView;
-
  export let preferredSeeds: string[];
-
  export let publicExplorer: string;

  $: api = new HttpdClient(baseUrl);

@@ -748,7 +746,7 @@
            </Button>
          {/if}
          {#if patchState === "read"}
-
            <Share {preferredSeeds} {publicExplorer} {baseUrl} />
+
            <Share {baseUrl} />
            {#if session && role.isDelegateOrAuthor(session.publicKey, project.delegates, patch.author.id)}
              <CobStateButton
                items={items.filter(
modified src/views/projects/Patches.svelte
@@ -27,8 +27,6 @@
  export let patches: Patch[];
  export let project: Project;
  export let state: PatchState["status"];
-
  export let preferredSeeds: string[];
-
  export let publicExplorer: string;

  let loading = false;
  let page = 0;
@@ -161,7 +159,7 @@
    </Popover>

    <div style="margin-left: auto; display: flex; gap: 1rem;">
-
      <Share {preferredSeeds} {publicExplorer} {baseUrl} />
+
      <Share {baseUrl} />
      {#if $httpdStore.state === "authenticated" && isLocal(baseUrl.hostname)}
        <Popover popoverPositionTop="2.5rem" popoverPositionRight="0">
          <Button
modified src/views/projects/Share.svelte
@@ -2,31 +2,43 @@
  import type { BaseUrl } from "@httpd-client";

  import debounce from "lodash/debounce";
-
  import { httpdStore } from "@app/lib/httpd";
+
  import { api, httpdStore } from "@app/lib/httpd";
  import { isLocal, toClipboard } from "@app/lib/utils";
+
  import { config } from "@app/lib/config";

  import Button from "@app/components/Button.svelte";
  import IconSmall from "@app/components/IconSmall.svelte";
  import Popover from "@app/components/Popover.svelte";
  import ShareButton from "./Header/ShareButton.svelte";
+
  import Loading from "@app/components/Loading.svelte";

-
  export let preferredSeeds: string[];
-
  export let publicExplorer: string;
  export let baseUrl: BaseUrl;

  const caption = "Link to seed";
-
  let linkIcon: "link" | "checkmark" = "link";
  let shareIcon: "share" | "checkmark" = "share";
+
  let loading = false;

  const restoreIcon = debounce(() => {
-
    linkIcon = "link";
    shareIcon = "share";
-
  }, 800);
+
  }, 1000);

-
  async function copy(text: string) {
-
    await toClipboard(text);
-
    linkIcon = "checkmark";
-
    shareIcon = "checkmark";
+
  async function copy() {
+
    if (loading) {
+
      return;
+
    }
+
    try {
+
      loading = true;
+
      const profile = await api.profile.getProfile().catch(() => undefined);
+
      const publicExplorer =
+
        profile?.config.publicExplorer ?? config.nodes.fallbackPublicExplorer;
+
      const text = new URL(publicExplorer).origin.concat(
+
        window.location.pathname,
+
      );
+
      await toClipboard(text);
+
      shareIcon = "checkmark";
+
    } finally {
+
      loading = false;
+
    }
    restoreIcon();
  }
</script>
@@ -42,20 +54,18 @@
      slot="toggle"
      let:toggle
      on:click={toggle}>
-
      <IconSmall name={linkIcon} />
+
      <IconSmall name="link" />
      {caption}
    </Button>
-
    <ShareButton {publicExplorer} {preferredSeeds} slot="popover" />
+
    <ShareButton slot="popover" />
  </Popover>
{:else}
-
  <Button
-
    variant="outline"
-
    size="regular"
-
    on:click={() =>
-
      void copy(
-
        new URL(publicExplorer).origin.concat(window.location.pathname),
-
      )}>
-
    <IconSmall name={shareIcon} />
-
    {shareIcon === "share" ? "Share" : "Link copied"}
+
  <Button variant="outline" size="regular" on:click={copy}>
+
    {#if loading}
+
      <Loading small noDelay />
+
    {:else}
+
      <IconSmall name={shareIcon} />
+
      {shareIcon === "share" ? "Share" : "Link copied"}
+
    {/if}
  </Button>
{/if}
modified src/views/projects/Source.svelte
@@ -25,8 +25,6 @@
  export let revision: string | undefined;
  export let tree: Tree;
  export let seeding: boolean;
-
  export let preferredSeeds: string[];
-
  export let publicExplorer: string;

  let mobileFileTree = false;

@@ -113,13 +111,7 @@
</style>

<Layout {baseUrl} {project} activeTab="source">
-
  <ProjectNameHeader
-
    {project}
-
    {baseUrl}
-
    {seeding}
-
    {preferredSeeds}
-
    {publicExplorer}
-
    slot="header" />
+
  <ProjectNameHeader {project} {baseUrl} {seeding} slot="header" />

  <div style:margin="1rem 0 1rem 1rem" slot="subheader">
    <Header
modified src/views/projects/Source/ProjectNameHeader.svelte
@@ -15,8 +15,6 @@
  export let project: Project;
  export let baseUrl: BaseUrl;
  export let seeding: boolean;
-
  export let preferredSeeds: string[];
-
  export let publicExplorer: string;
</script>

<style>
@@ -77,7 +75,7 @@
    <div
      class="global-hide-on-mobile"
      style="margin-left: auto; display: flex; gap: 0.5rem;">
-
      <Share {preferredSeeds} {publicExplorer} {baseUrl} />
+
      <Share {baseUrl} />
      <CloneButton {baseUrl} id={project.id} name={project.name} />
      <SeedButton
        {seeding}
modified src/views/projects/router.ts
@@ -20,7 +20,6 @@ import * as Syntax from "@app/lib/syntax";
import * as httpd from "@app/lib/httpd";
import { HttpdClient } from "@httpd-client";
import { ResponseError } from "@httpd-client/lib/fetcher";
-
import { config } from "@app/lib/config";
import { handleError } from "@app/views/projects/error";
import { nodePath } from "@app/views/nodes/router";
import { unreachable } from "@app/lib/utils";
@@ -116,8 +115,6 @@ export type ProjectLoadedRoute =
        rawPath: (commit?: string) => string;
        blobResult: BlobResult;
        seeding: boolean;
-
        preferredSeeds: string[];
-
        publicExplorer: string;
      };
    }
  | {
@@ -133,8 +130,6 @@ export type ProjectLoadedRoute =
        commitHeaders: CommitHeader[];
        totalCommitCount: number;
        seeding: boolean;
-
        preferredSeeds: string[];
-
        publicExplorer: string;
      };
    }
  | {
@@ -143,8 +138,6 @@ export type ProjectLoadedRoute =
        baseUrl: BaseUrl;
        project: Project;
        commit: Commit;
-
        preferredSeeds: string[];
-
        publicExplorer: string;
      };
    }
  | {
@@ -154,8 +147,6 @@ export type ProjectLoadedRoute =
        project: Project;
        rawPath: (commit?: string) => string;
        issue: Issue;
-
        preferredSeeds: string[];
-
        publicExplorer: string;
      };
    }
  | {
@@ -165,8 +156,6 @@ export type ProjectLoadedRoute =
        project: Project;
        issues: Issue[];
        state: IssueState["status"];
-
        preferredSeeds: string[];
-
        publicExplorer: string;
      };
    }
  | {
@@ -184,8 +173,6 @@ export type ProjectLoadedRoute =
        project: Project;
        patches: Patch[];
        state: PatchState["status"];
-
        preferredSeeds: string[];
-
        publicExplorer: string;
      };
    }
  | {
@@ -196,8 +183,6 @@ export type ProjectLoadedRoute =
        rawPath: (commit?: string) => string;
        patch: Patch;
        view: PatchView;
-
        preferredSeeds: string[];
-
        publicExplorer: string;
      };
    };

@@ -282,8 +267,7 @@ export async function loadProjectRoute(
    } else if (route.resource === "project.history") {
      return await loadHistoryView(route);
    } else if (route.resource === "project.commit") {
-
      const [profile, project, commit] = await Promise.all([
-
        api.profile.getProfile().catch(() => undefined),
+
      const [project, commit] = await Promise.all([
        api.project.getById(route.project),
        api.project.getCommitBySha(route.project, route.commit),
      ]);
@@ -294,15 +278,10 @@ export async function loadProjectRoute(
          baseUrl: route.node,
          project,
          commit,
-
          preferredSeeds: profile?.config.preferredSeeds || [],
-
          publicExplorer:
-
            profile?.config.publicExplorer ||
-
            config.nodes.fallbackPublicExplorer,
        },
      };
    } else if (route.resource === "project.issue") {
-
      const [profile, project, issue] = await Promise.all([
-
        api.profile.getProfile().catch(() => undefined),
+
      const [project, issue] = await Promise.all([
        api.project.getById(route.project),
        api.project.getIssueById(route.project, route.issue),
      ]);
@@ -313,10 +292,6 @@ export async function loadProjectRoute(
          project,
          rawPath,
          issue,
-
          preferredSeeds: profile?.config.preferredSeeds || [],
-
          publicExplorer:
-
            profile?.config.publicExplorer ||
-
            config.nodes.fallbackPublicExplorer,
        },
      };
    } else if (route.resource === "project.patch") {
@@ -350,8 +325,7 @@ async function loadPatchesView(
  const searchParams = new URLSearchParams(route.search || "");
  const state = (searchParams.get("state") as PatchState["status"]) || "open";

-
  const [profile, project, patches] = await Promise.all([
-
    api.profile.getProfile().catch(() => undefined),
+
  const [project, patches] = await Promise.all([
    api.project.getById(route.project),
    api.project.getAllPatches(route.project, {
      state,
@@ -367,9 +341,6 @@ async function loadPatchesView(
      patches,
      state,
      project,
-
      preferredSeeds: profile?.config.preferredSeeds || [],
-
      publicExplorer:
-
        profile?.config.publicExplorer || config.nodes.fallbackPublicExplorer,
    },
  };
}
@@ -380,8 +351,7 @@ async function loadIssuesView(
  const api = new HttpdClient(route.node);
  const state = route.state || "open";

-
  const [profile, project, issues] = await Promise.all([
-
    api.profile.getProfile().catch(() => undefined),
+
  const [project, issues] = await Promise.all([
    api.project.getById(route.project),
    api.project.getAllIssues(route.project, {
      state,
@@ -398,9 +368,6 @@ async function loadIssuesView(
      issues,
      state,
      project,
-
      preferredSeeds: profile?.config.preferredSeeds || [],
-
      publicExplorer:
-
        profile?.config.publicExplorer || config.nodes.fallbackPublicExplorer,
    },
  };
}
@@ -414,8 +381,7 @@ async function loadTreeView(
      route.project
    }${commit ? `/${commit}` : ""}`;

-
  const [profile, project, peers, branchMap, seeding] = await Promise.all([
-
    api.profile.getProfile().catch(() => undefined),
+
  const [project, peers, branchMap, seeding] = await Promise.all([
    api.project.getById(route.project),
    api.project.getAllRemotes(route.project),
    getPeerBranches(api, route.project, route.peer),
@@ -457,9 +423,6 @@ async function loadTreeView(
      path,
      blobResult,
      seeding,
-
      preferredSeeds: profile?.config.preferredSeeds || [],
-
      publicExplorer:
-
        profile?.config.publicExplorer || config.nodes.fallbackPublicExplorer,
    },
  };
}
@@ -509,8 +472,7 @@ async function loadHistoryView(
): Promise<ProjectLoadedRoute> {
  const api = new HttpdClient(route.node);

-
  const [profile, project, peers, branchMap] = await Promise.all([
-
    api.profile.getProfile().catch(() => undefined),
+
  const [project, peers, branchMap] = await Promise.all([
    api.project.getById(route.project),
    api.project.getAllRemotes(route.project),
    getPeerBranches(api, route.project, route.peer),
@@ -554,9 +516,6 @@ async function loadHistoryView(
      commitHeaders: commitsResponse.commits.map(c => c.commit),
      totalCommitCount: commitsResponse.stats.commits,
      seeding,
-
      preferredSeeds: profile?.config.preferredSeeds || [],
-
      publicExplorer:
-
        profile?.config.publicExplorer || config.nodes.fallbackPublicExplorer,
    },
  };
}
@@ -569,8 +528,7 @@ async function loadPatchView(
    `${route.node.scheme}://${route.node.hostname}:${route.node.port}/raw/${
      route.project
    }${commit ? `/${commit}` : ""}`;
-
  const [profile, project, patch] = await Promise.all([
-
    api.profile.getProfile().catch(() => undefined),
+
  const [project, patch] = await Promise.all([
    api.project.getById(route.project),
    api.project.getPatchById(route.project, route.patch),
  ]);
@@ -627,9 +585,6 @@ async function loadPatchView(
      rawPath,
      patch,
      view,
-
      preferredSeeds: profile?.config.preferredSeeds || [],
-
      publicExplorer:
-
        profile?.config.publicExplorer || config.nodes.fallbackPublicExplorer,
    },
  };
}