Radish alpha
r
rad:z4D5UCArafTzTQpDZNQRuqswh3ury
Radicle desktop app
Radicle
Git
Cache content-addressed API calls on the frontend
Rūdolfs Ošiņš committed 1 year ago
commit 7c0da906a83a66ca8b261037da1316cb5188a04d
parent 83390ae
11 files changed +126 -34
modified crates/radicle-tauri/src/commands/diff.rs
@@ -9,7 +9,7 @@ use crate::AppState;
pub async fn get_diff(
    ctx: tauri::State<'_, AppState>,
    rid: identity::RepoId,
-
    options: radicle_types::cobs::diff::Options,
+
    options: radicle_types::cobs::diff::DiffOptions,
) -> Result<types::diff::Diff, Error> {
    ctx.get_diff(rid, options)
}
added crates/radicle-types/bindings/cob/DiffOptions.ts
@@ -0,0 +1,8 @@
+
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+

+
export type DiffOptions = {
+
  base: string;
+
  head: string;
+
  unified: number | null;
+
  highlight: boolean | null;
+
};
modified crates/radicle-types/src/cobs/diff.rs
@@ -1,9 +1,14 @@
use radicle::git;
use serde::{Deserialize, Serialize};
+
use ts_rs::TS;

-
#[derive(Serialize, Deserialize)]
-
pub struct Options {
+
#[derive(TS, Serialize, Deserialize)]
+
#[ts(export)]
+
#[ts(export_to = "cob/")]
+
pub struct DiffOptions {
+
    #[ts(as = "String")]
    pub base: git::Oid,
+
    #[ts(as = "String")]
    pub head: git::Oid,
    pub unified: Option<u32>,
    pub highlight: Option<bool>,
modified crates/radicle-types/src/traits/repo.rs
@@ -182,7 +182,11 @@ pub trait Repo: Profile {
        })
    }

-
    fn get_diff(&self, rid: identity::RepoId, options: cobs::diff::Options) -> Result<Diff, Error> {
+
    fn get_diff(
+
        &self,
+
        rid: identity::RepoId,
+
        options: cobs::diff::DiffOptions,
+
    ) -> Result<Diff, Error> {
        let unified = options.unified.unwrap_or(5);
        let highlight = options.highlight.unwrap_or(true);
        let profile = self.profile();
modified crates/test-http-api/src/api.rs
@@ -165,7 +165,7 @@ async fn diff_stats_handler(
#[derive(Serialize, Deserialize)]
struct DiffBody {
    pub rid: identity::RepoId,
-
    pub options: types::cobs::diff::Options,
+
    pub options: types::cobs::diff::DiffOptions,
}

async fn diff_handler(
modified package-lock.json
@@ -44,6 +44,7 @@
        "hast-util-to-dom": "^4.0.1",
        "keyux": "^0.11.1",
        "lodash": "^4.17.21",
+
        "lru-cache": "^11.1.0",
        "marked": "^15.0.7",
        "marked-emoji": "^2.0.0",
        "marked-footnote": "^1.2.4",
@@ -3388,6 +3389,16 @@
      "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==",
      "dev": true
    },
+
    "node_modules/lru-cache": {
+
      "version": "11.1.0",
+
      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz",
+
      "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==",
+
      "dev": true,
+
      "license": "ISC",
+
      "engines": {
+
        "node": "20 || >=22"
+
      }
+
    },
    "node_modules/magic-string": {
      "version": "0.30.17",
      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
modified package.json
@@ -59,6 +59,7 @@
    "hast-util-to-dom": "^4.0.1",
    "keyux": "^0.11.1",
    "lodash": "^4.17.21",
+
    "lru-cache": "^11.1.0",
    "marked": "^15.0.7",
    "marked-emoji": "^2.0.0",
    "marked-footnote": "^1.2.4",
modified src/components/Changes.svelte
@@ -1,10 +1,8 @@
<script lang="ts">
-
  import type { Commit } from "@bindings/repo/Commit";
-
  import type { Diff } from "@bindings/diff/Diff";
  import type { CodeComments } from "./Diff.svelte";
  import type { Revision } from "@bindings/cob/patch/Revision";

-
  import { invoke } from "@app/lib/invoke";
+
  import { cachedGetDiff, cachedListCommits } from "@app/lib/invoke";
  import { pluralize } from "@app/lib/utils";

  import Changeset from "@app/components/Changeset.svelte";
@@ -57,26 +55,6 @@
  const isActiveCommit = (commitId: string) => selectedCommit === commitId;
  const isTeaserDisabled = (commitId: string) =>
    selectedCommit ? selectedCommit !== commitId : false;
-

-
  async function loadHighlightedDiff(rid: string, base: string, head: string) {
-
    return invoke<Diff>("get_diff", {
-
      rid,
-
      options: {
-
        base,
-
        head,
-
        unified: 3,
-
        highlight: true,
-
      },
-
    });
-
  }
-

-
  async function loadCommits(rid: string, base: string, head: string) {
-
    return invoke<Commit[]>("list_commits", {
-
      rid,
-
      base,
-
      head,
-
    });
-
  }
</script>

<style>
@@ -158,7 +136,7 @@
</div>

<div class:hide={hideChanges}>
-
  {#await loadCommits(rid, revision.base, revision.head) then commits}
+
  {#await cachedListCommits(rid, revision.base, revision.head) then commits}
    <div style:margin-bottom="1rem">
      <CommitsContainer>
        {#snippet leftHeader()}
@@ -209,7 +187,7 @@
    </div>
  {/await}

-
  {#await loadHighlightedDiff(rid, base, head)}
+
  {#await cachedGetDiff(rid, { base, head, unified: 3, highlight: true })}
    <span class="txt-small">Loading…</span>
  {:then diff}
    <Changeset expanded={filesExpanded} {head} {diff} {codeComments} />
modified src/components/PatchTeaser.svelte
@@ -1,7 +1,6 @@
<script lang="ts">
  import type { Patch } from "@bindings/cob/patch/Patch";
  import type { PatchStatus } from "@app/views/repo/router";
-
  import type { Stats } from "@bindings/cob/Stats";

  import {
    authorForNodeId,
@@ -9,7 +8,7 @@
    patchStatusBackgroundColor,
    patchStatusColor,
  } from "@app/lib/utils";
-
  import { invoke } from "@app/lib/invoke";
+
  import { cachedDiffStats } from "@app/lib/invoke";
  import { push } from "@app/lib/router";

  import DiffStatBadge from "@app/components/DiffStatBadge.svelte";
@@ -102,7 +101,7 @@

  <div class="global-flex" style:margin-left="auto">
    {#if !compact}
-
      {#await invoke<Stats>( "diff_stats", { rid, base: patch.base, head: patch.head }, ) then stats}
+
      {#await cachedDiffStats(rid, patch.base, patch.head) then stats}
        <DiffStatBadge {stats} />
      {/await}

added src/lib/cached.ts
@@ -0,0 +1,21 @@
+
import { LRUCache } from "lru-cache";
+

+
export function cached<Args extends unknown[], V>(
+
  f: (...args: Args) => Promise<V>,
+
  makeKey: (...args: Args) => string,
+
  options?: LRUCache.Options<string, { value: V }, unknown>,
+
): (...args: Args) => Promise<V> {
+
  const cache = new LRUCache(options || { max: 500 });
+
  return async function (...args: Args): Promise<V> {
+
    const key = makeKey(...args);
+
    const cached = cache.get(key);
+

+
    if (cached === undefined) {
+
      const value = await f(...args);
+
      cache.set(key, { value });
+
      return value;
+
    } else {
+
      return cached.value;
+
    }
+
  };
+
}
modified src/lib/invoke.ts
@@ -1,12 +1,31 @@
+
import type { Commit } from "@bindings/repo/Commit";
+
import type { Diff } from "@bindings/diff/Diff";
+
import type { DiffOptions } from "@bindings/cob/DiffOptions";
+
import type { Stats } from "@bindings/cob/Stats";
+

import * as tauri from "@tauri-apps/api/core";
+
import { cached } from "@app/lib/cached";

export async function invoke<T = null>(
  cmd: string,
  args?: tauri.InvokeArgs,
  options?: tauri.InvokeOptions,
): Promise<T> {
+
  return withTestBackend<T>(tauri.invoke, cmd, args, options);
+
}
+

+
async function withTestBackend<T>(
+
  fn: (
+
    cmd: string,
+
    args?: tauri.InvokeArgs,
+
    options?: tauri.InvokeOptions,
+
  ) => Promise<T>,
+
  cmd: string,
+
  args?: tauri.InvokeArgs,
+
  options?: tauri.InvokeOptions,
+
) {
  if (window.__TAURI_INTERNALS__) {
-
    return tauri.invoke(cmd, args, options);
+
    return fn(cmd, args, options);
  } else {
    return fetch(`http://127.0.0.1:8081/${cmd}`, {
      method: "POST",
@@ -27,6 +46,52 @@ export async function invoke<T = null>(
  }
}

+
async function getDiff(rid: string, options: DiffOptions): Promise<Diff> {
+
  return withTestBackend(tauri.invoke, "get_diff", {
+
    rid,
+
    options,
+
  });
+
}
+

+
export const cachedGetDiff = cached(
+
  getDiff,
+
  (...[rid, options]) =>
+
    `get_diff:${rid}:${JSON.stringify(options, Object.keys(options).sort())}`,
+
  { max: 10_000 },
+
);
+

+
async function listCommits(
+
  rid: string,
+
  base: string,
+
  head: string,
+
): Promise<Commit[]> {
+
  return withTestBackend(tauri.invoke, "list_commits", { rid, base, head });
+
}
+

+
export const cachedListCommits = cached(
+
  listCommits,
+
  (...[rid, base, head]) => `list_commits:${rid}:${base}:${head}`,
+
  { max: 5_000 },
+
);
+

+
async function diffStats(
+
  rid: string,
+
  base: string,
+
  head: string,
+
): Promise<Stats> {
+
  return withTestBackend(tauri.invoke, "diff_stats", {
+
    rid,
+
    base,
+
    head,
+
  });
+
}
+

+
export const cachedDiffStats = cached(
+
  diffStats,
+
  (...[rid, base, head]) => `diff_stats:${rid}:${base}:${head}`,
+
  { max: 10_000 },
+
);
+

export async function writeToClipboard(
  text: string,
  opts?: {