Radish alpha
r
rad:z4D5UCArafTzTQpDZNQRuqswh3ury
Radicle desktop app
Radicle
Git
Persist sidebar across navigations to preserve scroll position
Rūdolfs Ošiņš committed 1 month ago
commit 7b396ff72daf5ea0e4e266ea51d3f64a5ad8ca9d
parent d0763b3
11 files changed +88 -93
modified src/App.svelte
@@ -1,6 +1,7 @@
<script lang="ts">
  import type { Config } from "@bindings/config/Config";
  import type { ErrorWrapper } from "@bindings/error/ErrorWrapper";
+
  import type { RepoInfo } from "@bindings/repo/RepoInfo";

  import { listen } from "@tauri-apps/api/event";
  import { getCurrentWindow } from "@tauri-apps/api/window";
@@ -20,12 +21,14 @@
  import { invoke } from "@app/lib/invoke";
  import { hide } from "@app/lib/modal";
  import * as router from "@app/lib/router";
+
  import { isLoadedRepoRoute } from "@app/lib/router/definitions";
  import {
    setUnlistenNodeEvents,
    unlistenNodeEvents,
  } from "@app/lib/startup.svelte";
  import { isMac, unreachable } from "@app/lib/utils";

+
  import AppSidebar from "@app/components/AppSidebar.svelte";
  import { codeFont } from "@app/components/CodeFontSwitch.svelte";
  import {
    followSystemTheme,
@@ -50,6 +53,11 @@

  const activeRouteStore = router.activeRouteStore;

+
  const activeRepo = $derived.by((): RepoInfo | undefined => {
+
    const route = $activeRouteStore;
+
    return isLoadedRepoRoute(route) ? route.params.repo : undefined;
+
  });
+

  const DRAG_REGION_HEIGHT = 32;
  const INTERACTIVE_TAGS = new Set([
    "a",
@@ -144,6 +152,13 @@
    align-items: center;
    height: 100%;
  }
+
  .layout {
+
    display: grid;
+
    grid-template-columns: auto 1fr;
+
    grid-template-rows: 100%;
+
    height: 100%;
+
    overflow: hidden;
+
  }
</style>

<svelte:document
@@ -194,20 +209,27 @@
  {:else if showSpinner}
    <div class="spinner"><Spinner /></div>
  {/if}
-
{:else if $activeRouteStore.resource === "inbox"}
-
  <InboxView {...$activeRouteStore.params} />
-
{:else if $activeRouteStore.resource === "guide"}
-
  <GuideView {...$activeRouteStore.params} />
-
{:else if $activeRouteStore.resource === "repo.home"}
-
  <RepoHome {...$activeRouteStore.params} />
-
{:else if $activeRouteStore.resource === "repo.issue"}
-
  <Issue {...$activeRouteStore.params} />
-
{:else if $activeRouteStore.resource === "repo.issues"}
-
  <Issues {...$activeRouteStore.params} />
-
{:else if $activeRouteStore.resource === "repo.patch"}
-
  <Patch {...$activeRouteStore.params} />
-
{:else if $activeRouteStore.resource === "repo.patches"}
-
  <Patches {...$activeRouteStore.params} />
{:else}
-
  {unreachable($activeRouteStore)}
+
  <div class="layout">
+
    <AppSidebar
+
      sidebarData={$activeRouteStore.params.sidebarData}
+
      {activeRepo} />
+
    {#if $activeRouteStore.resource === "inbox"}
+
      <InboxView {...$activeRouteStore.params} />
+
    {:else if $activeRouteStore.resource === "guide"}
+
      <GuideView {...$activeRouteStore.params} />
+
    {:else if $activeRouteStore.resource === "repo.home"}
+
      <RepoHome {...$activeRouteStore.params} />
+
    {:else if $activeRouteStore.resource === "repo.issue"}
+
      <Issue {...$activeRouteStore.params} />
+
    {:else if $activeRouteStore.resource === "repo.issues"}
+
      <Issues {...$activeRouteStore.params} />
+
    {:else if $activeRouteStore.resource === "repo.patch"}
+
      <Patch {...$activeRouteStore.params} />
+
    {:else if $activeRouteStore.resource === "repo.patches"}
+
      <Patches {...$activeRouteStore.params} />
+
    {:else}
+
      {unreachable($activeRouteStore)}
+
    {/if}
+
  </div>
{/if}
modified src/lib/router/definitions.ts
@@ -52,6 +52,18 @@ export type LoadedRoute =
  | LoadedInboxRoute
  | LoadedGuideRoute;

+
export function isLoadedRepoRoute(
+
  route: LoadedRoute,
+
): route is LoadedRepoRoute {
+
  return (
+
    route.resource === "repo.home" ||
+
    route.resource === "repo.issue" ||
+
    route.resource === "repo.issues" ||
+
    route.resource === "repo.patch" ||
+
    route.resource === "repo.patches"
+
  );
+
}
+

export async function loadSidebarData(): Promise<SidebarData> {
  const [config, repos, notificationCount, seededNotReplicated] =
    await Promise.all([
modified src/modals/Guide.svelte
@@ -81,7 +81,7 @@
  }
</style>

-
<Layout {sidebarData}>
+
<Layout>
  <div class="page">
    <div class="hero">
      <img src="/flower.png" alt="" />
modified src/views/Inbox.svelte
@@ -107,7 +107,7 @@
  }
</style>

-
<Layout {sidebarData} selfScroll>
+
<Layout selfScroll>
  {#if notificationCount.value === 0}
    <div class="inbox-zero">
      <div
modified src/views/repo/Issue.svelte
@@ -16,7 +16,6 @@
  import { show } from "@app/lib/modal";
  import * as roles from "@app/lib/roles";
  import * as router from "@app/lib/router";
-
  import type { SidebarData } from "@app/lib/router/definitions";
  import {
    explorerUrl,
    issueStatusBackgroundColor,
@@ -49,7 +48,6 @@
    config: Config;
    threads: Thread[];
    status: IssueStatus;
-
    sidebarData: SidebarData;
  }

  /* eslint-disable prefer-const */
@@ -61,7 +59,6 @@
    config,
    threads,
    status: initialStatus,
-
    sidebarData,
  }: Props = $props();
  /* eslint-enable prefer-const */

@@ -346,7 +343,7 @@
  }
</style>

-
<Layout {sidebarData} activeRepo={repo}>
+
<Layout>
  <div class="page">
    <div class="topbar">
      <Icon name={issue.state.status === "open" ? "issue" : "issue-closed"} />
modified src/views/repo/Issues.svelte
@@ -15,7 +15,6 @@
  } from "@app/lib/issueCounts.svelte";
  import { show } from "@app/lib/modal";
  import * as router from "@app/lib/router";
-
  import type { SidebarData } from "@app/lib/router/definitions";
  import { modifierKey } from "@app/lib/utils";

  import Button from "@app/components/Button.svelte";
@@ -32,11 +31,10 @@
    repo: RepoInfo;
    issues: Issue[];
    status: IssueStatus;
-
    sidebarData: SidebarData;
  }

  /* eslint-disable prefer-const */
-
  let { repo, issues, status, sidebarData }: Props = $props();
+
  let { repo, issues, status }: Props = $props();
  /* eslint-enable prefer-const */

  let cacheState: CacheEvent | undefined = $state();
@@ -156,7 +154,7 @@
  }
</style>

-
<Layout {sidebarData} activeRepo={repo} selfScroll>
+
<Layout selfScroll>
  <div class="page">
    <div class="topbar">
      <span class="topbar-title">Issues</span>
modified src/views/repo/Layout.svelte
@@ -1,24 +1,16 @@
<script lang="ts">
-
  import type { RepoInfo } from "@bindings/repo/RepoInfo";
  import type { Snippet } from "svelte";

-
  import type { SidebarData } from "@app/lib/router/definitions";
-

-
  import AppSidebar from "@app/components/AppSidebar.svelte";
  import ScrollArea from "@app/components/ScrollArea.svelte";

  interface Props {
    children: Snippet;
-
    sidebarData: SidebarData;
-
    activeRepo?: RepoInfo;
    loadMoreContent?: () => Promise<void>;
    selfScroll?: boolean;
  }

  const {
    children,
-
    sidebarData,
-
    activeRepo = undefined,
    loadMoreContent = undefined,
    selfScroll = false,
  }: Props = $props();
@@ -26,35 +18,22 @@
  let loadingContent = false;
</script>

-
<style>
-
  .layout {
-
    display: grid;
-
    grid-template-columns: auto 1fr;
-
    grid-template-rows: 100%;
-
    height: 100%;
-
    overflow: hidden;
-
  }
-
</style>
-

-
<div class="layout">
-
  <AppSidebar {sidebarData} {activeRepo} />
-
  {#if selfScroll}
-
    <div
-
      style="height: 100%; overflow: hidden; min-width: 0; background-color: var(--color-surface-canvas);">
-
      {@render children()}
-
    </div>
-
  {:else}
-
    <ScrollArea
-
      style="height: 100%; width: 100%; background-color: var(--color-surface-canvas);"
-
      onScrollHalf={loadMoreContent
-
        ? () => {
-
            if (!loadingContent) {
-
              loadingContent = true;
-
              void loadMoreContent().finally(() => (loadingContent = false));
-
            }
+
{#if selfScroll}
+
  <div
+
    style="height: 100%; overflow: hidden; min-width: 0; background-color: var(--color-surface-canvas);">
+
    {@render children()}
+
  </div>
+
{:else}
+
  <ScrollArea
+
    style="height: 100%; width: 100%; background-color: var(--color-surface-canvas);"
+
    onScrollHalf={loadMoreContent
+
      ? () => {
+
          if (!loadingContent) {
+
            loadingContent = true;
+
            void loadMoreContent().finally(() => (loadingContent = false));
          }
-
        : undefined}>
-
      {@render children()}
-
    </ScrollArea>
-
  {/if}
-
</div>
+
        }
+
      : undefined}>
+
    {@render children()}
+
  </ScrollArea>
+
{/if}
modified src/views/repo/Patch.svelte
@@ -13,7 +13,6 @@
  import { nodeRunning } from "@app/lib/events";
  import { invoke } from "@app/lib/invoke";
  import * as router from "@app/lib/router";
-
  import type { SidebarData } from "@app/lib/router/definitions";
  import {
    didFromPublicKey,
    explorerUrl,
@@ -46,7 +45,6 @@
    activity: Operation<Action>[];
    status: PatchStatus | undefined;
    review: Review | DraftReview | undefined;
-
    sidebarData: SidebarData;
  }

  /* eslint-disable prefer-const */
@@ -58,7 +56,6 @@
    status: initialStatus,
    activity,
    review,
-
    sidebarData,
  }: Props = $props();
  /* eslint-enable prefer-const */

@@ -249,7 +246,7 @@
  }
</style>

-
<Layout {sidebarData} activeRepo={repo}>
+
<Layout>
  {#if review}
    <ReviewComponent
      {config}
modified src/views/repo/Patches.svelte
@@ -17,7 +17,6 @@
    updatePatchCounts,
  } from "@app/lib/patchCounts.svelte";
  import * as router from "@app/lib/router";
-
  import type { SidebarData } from "@app/lib/router/definitions";
  import { modifierKey } from "@app/lib/utils";

  import CobCacheWarning from "@app/components/CobCacheWarning.svelte";
@@ -33,10 +32,9 @@
    repo: RepoInfo;
    patches: PaginatedQuery<Patch[]>;
    status: PatchStatus | undefined;
-
    sidebarData: SidebarData;
  }

-
  const { repo, patches, status, sidebarData }: Props = $props();
+
  const { repo, patches, status }: Props = $props();

  let items = $state(patches.content);
  let cursor = patches.cursor;
@@ -225,7 +223,7 @@
  }
</style>

-
<Layout {sidebarData} activeRepo={repo} selfScroll>
+
<Layout selfScroll>
  <div class="page">
    <div class="topbar">
      <span class="topbar-title">Patches</span>
modified src/views/repo/RepoHome.svelte
@@ -9,7 +9,6 @@
  import { useOverlayScrollbars } from "overlayscrollbars-svelte";

  import { invoke, InvokeError } from "@app/lib/invoke";
-
  import type { SidebarData } from "@app/lib/router/definitions";
  import { highlight } from "@app/lib/syntax";

  import FileBlock from "@app/components/FileBlock.svelte";
@@ -27,11 +26,10 @@
    tree: Tree;
    repo: RepoInfo;
    readme: Readme | null;
-
    sidebarData: SidebarData;
  }

  /* eslint-disable prefer-const */
-
  let { tree, readme, repo, sidebarData }: Props = $props();
+
  let { tree, readme, repo }: Props = $props();
  /* eslint-enable prefer-const */

  let currentPath = $state("");
@@ -120,7 +118,7 @@
  }
</style>

-
<Layout {sidebarData} activeRepo={repo} selfScroll>
+
<Layout selfScroll>
  <div
    class="content"
    style:display="flex"
modified src/views/repo/router.ts
@@ -42,7 +42,6 @@ export interface LoadedRepoHomeRoute {
    repo: RepoInfo;
    sha?: string;
    tree: Tree;
-
    config: Config;
    readme: Readme | null;
    sidebarData: SidebarData;
  };
@@ -72,7 +71,6 @@ export interface LoadedRepoIssuesRoute {
  resource: "repo.issues";
  params: {
    repo: RepoInfo;
-
    config: Config;
    issues: Issue[];
    status: IssueStatus;
    sidebarData: SidebarData;
@@ -114,7 +112,6 @@ export interface LoadedRepoPatchesRoute {
  resource: "repo.patches";
  params: {
    repo: RepoInfo;
-
    config: Config;
    patches: PaginatedQuery<Patch[]>;
    status: PatchStatus | undefined;
    sidebarData: SidebarData;
@@ -137,10 +134,9 @@ export type LoadedRepoRoute =
export async function loadPatch(
  route: RepoPatchRoute,
): Promise<LoadedRepoPatchRoute> {
-
  const [sidebarData, config, repo, patches, patch, revisions, activity] =
+
  const [sidebarData, repo, patches, patch, revisions, activity] =
    await Promise.all([
      loadSidebarData(),
-
      invoke<Config>("config"),
      invoke<RepoInfo>("repo_by_id", {
        rid: route.rid,
      }),
@@ -163,6 +159,8 @@ export async function loadPatch(
      }),
    ]);

+
  const config = sidebarData.config;
+

  const draftReview =
    route.reviewId !== undefined &&
    draftReviewStorage.get(route.reviewId, {
@@ -195,9 +193,8 @@ export async function loadPatch(
export async function loadPatches(
  route: RepoPatchesRoute,
): Promise<LoadedRepoPatchesRoute> {
-
  const [sidebarData, config, repo, patches] = await Promise.all([
+
  const [sidebarData, repo, patches] = await Promise.all([
    loadSidebarData(),
-
    invoke<Config>("config"),
    invoke<RepoInfo>("repo_by_id", {
      rid: route.rid,
    }),
@@ -210,16 +207,15 @@ export async function loadPatches(

  return {
    resource: "repo.patches",
-
    params: { sidebarData, repo, config, patches, status: route.status },
+
    params: { sidebarData, repo, patches, status: route.status },
  };
}

export async function loadRepoHome(
  route: RepoHomeRoute,
): Promise<LoadedRepoHomeRoute> {
-
  const [sidebarData, config, repo, readme, tree] = await Promise.all([
+
  const [sidebarData, repo, readme, tree] = await Promise.all([
    loadSidebarData(),
-
    invoke<Config>("config"),
    invoke<RepoInfo>("repo_by_id", {
      rid: route.rid,
    }),
@@ -235,17 +231,16 @@ export async function loadRepoHome(

  return {
    resource: "repo.home",
-
    params: { sidebarData, repo, sha: route.sha, config, readme, tree },
+
    params: { sidebarData, repo, sha: route.sha, readme, tree },
  };
}

export async function loadIssue(
  route: RepoIssueRoute,
): Promise<LoadedRepoIssueRoute> {
-
  const [sidebarData, config, repo, issue, activity, issues, threads] =
+
  const [sidebarData, repo, issue, activity, issues, threads] =
    await Promise.all([
      loadSidebarData(),
-
      invoke<Config>("config"),
      invoke<RepoInfo>("repo_by_id", {
        rid: route.rid,
      }),
@@ -272,7 +267,7 @@ export async function loadIssue(
    params: {
      sidebarData,
      repo,
-
      config,
+
      config: sidebarData.config,
      issue,
      activity,
      issues,
@@ -285,9 +280,8 @@ export async function loadIssue(
export async function loadIssues(
  route: RepoIssuesRoute,
): Promise<LoadedRepoIssuesRoute> {
-
  const [sidebarData, config, repo, issues] = await Promise.all([
+
  const [sidebarData, repo, issues] = await Promise.all([
    loadSidebarData(),
-
    invoke<Config>("config"),
    invoke<RepoInfo>("repo_by_id", {
      rid: route.rid,
    }),
@@ -299,7 +293,7 @@ export async function loadIssues(

  return {
    resource: "repo.issues",
-
    params: { sidebarData, repo, config, issues, status: route.status },
+
    params: { sidebarData, repo, issues, status: route.status },
  };
}