Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Preload blobs in project source browser
Alexis Sellier committed 4 years ago
commit 9bb9245d9082006c90dac6cd636d885b2ae8904e
parent e7f15acd97586769d25b2cc9a98577d694e6447c
5 files changed +84 -46
modified src/Loading.svelte
@@ -4,6 +4,7 @@
  export let center = false;
  export let fadeIn = false;
  export let margins = false;
+
  export let condensed = false;
</script>

<style>
@@ -34,6 +35,11 @@
    animation: sk-bouncedelay 1.4s infinite ease-in-out both;
  }

+
  .spinner.condensed > div {
+
    -webkit-animation: sk-bouncedelay-condensed 0.7s infinite linear both;
+
    animation: sk-bouncedelay-condensed 0.7s infinite linear both;
+
  }
+

  .spinner.small > div {
    width: 0.5rem;
    height: 0.5rem;
@@ -53,6 +59,21 @@
    animation-delay: -0.16s;
  }

+
  @-webkit-keyframes sk-bouncedelay-condensed {
+
    0%, 100% { -webkit-transform: scale(0.2) }
+
    50% { -webkit-transform: scale(1.0) }
+
  }
+

+
  @keyframes sk-bouncedelay-condensed {
+
    0%, 100% {
+
      -webkit-transform: scale(0.2);
+
      transform: scale(0.2);
+
    } 50% {
+
      -webkit-transform: scale(1.0);
+
      transform: scale(1.0);
+
    }
+
  }
+

  @-webkit-keyframes sk-bouncedelay {
    0%, 80%, 100% { -webkit-transform: scale(0) }
    40% { -webkit-transform: scale(1.0) }
@@ -85,8 +106,10 @@
  }
</style>

-
<div class="spinner" class:fade-in={fadeIn} class:small class:center class:margins>
+
<div class="spinner" class:fade-in={fadeIn} class:small class:center class:margins class:condensed>
  <div class="bounce1" style="background-color: var(--color-{color})"></div>
-
  <div class="bounce2" style="background-color: var(--color-{color})"></div>
-
  <div class="bounce3" style="background-color: var(--color-{color})"></div>
+
  {#if !condensed}
+
    <div class="bounce2" style="background-color: var(--color-{color})"></div>
+
    <div class="bounce3" style="background-color: var(--color-{color})"></div>
+
  {/if}
</div>
modified src/base/projects/Browser.svelte
@@ -11,13 +11,49 @@
  import Blob from './Blob.svelte';
  import Readme from './Readme.svelte';

+
  enum Status {
+
    Idle,
+
    Loading,
+
    Loaded,
+
  }
+

+
  type State =
+
      { status: Status.Idle }
+
    | { status: Status.Loading; path: string }
+
    | { status: Status.Loaded; path: string; blob: proj.Blob };
+

  export let urn: string;
  export let commit: string;
  export let config: Config;
  export let path: string;
  export let org = "";

-
  const onSelect = ({ detail: path }: { detail: string }) => {
+
  // When the component is loaded the first time, the blob is yet to be loaded.
+
  let state: State = { status: Status.Idle };
+

+
  const loadBlob = async (path: string): Promise<proj.Blob> => {
+
    if (state.status == Status.Loaded && state.path === path) {
+
      return state.blob;
+
    }
+

+
    const isMarkdownPath = utils.isMarkdownPath(path);
+
    const promise = path === "/"
+
      ? proj.getReadme(urn, commit, config)
+
      : proj.getBlob(urn, commit, path, { highlight: !isMarkdownPath }, config);
+

+
    state = { status: Status.Loading, path };
+
    state = { status: Status.Loaded, path, blob: await promise };
+

+
    return state.blob;
+
  };
+

+
  const onSelect = async ({ detail: path }: { detail: string }) => {
+
    // Ensure we don't spend any time in a "loading" state. This means
+
    // the loading spinner won't be shown, and instead the blob will be
+
    // displayed once loaded.
+
    const blob = await loadBlob(path);
+
    getBlob = new Promise(resolve => resolve(blob));
+

    navigate(proj.path({ urn, org, commit, path }));
  };

@@ -25,11 +61,11 @@
    return proj.getTree(urn, commit, path, config);
  };

-
  $: isMarkdownPath = utils.isMarkdownPath(path);
-
  $: getBlob = path === "/"
-
    ? proj.getReadme(urn, commit, config)
-
    : proj.getBlob(urn, commit, path, { highlight: !isMarkdownPath }, config);
+
  // This is reactive to respond to path changes that don't originate from this
+
  // component, eg. when using the browser's "back" button.
+
  $: getBlob = loadBlob(path);
  $: getAnchor = org ? Org.getAnchor(org, urn, config) : null;
+
  $: loading = state.status == Status.Loading ? state.path : null;
</script>

<style>
@@ -167,7 +203,7 @@
      {#if tree.entries.length}
        <div class="column-left">
          <div class="source-tree">
-
            <Tree {tree} {path} {fetchTree} on:select={onSelect} />
+
            <Tree {tree} {path} {fetchTree} {loading} on:select={onSelect} />
          </div>
        </div>
        <div class="column-right">
modified src/base/projects/Routes.svelte
@@ -4,26 +4,14 @@
  import type { Config } from '@app/config';

  export let config: Config;
-

-
  const joinPaths = (path: string, glob: string): string => {
-
    return glob.length > 0 ? [path, glob].join("/") : path;
-
  };
</script>

-
<Route path="/projects/:urn/head/:path/*" let:params>
-
  <View {config} urn={params.urn} path={joinPaths(params.path, params['*'])} />
-
</Route>
-

-
<Route path="/projects/:urn/:commit/:path/*" let:params>
-
  <View {config} urn={params.urn} commit={params.commit} path={joinPaths(params.path, params['*'])} />
+
<Route path="/projects/:urn/head/*" let:params>
+
  <View {config} urn={params.urn} path={params['*'] || "/"} />
</Route>

-
<Route path="/projects/:urn/:commit" let:params>
-
  <View {config} urn={params.urn} commit={params.commit} path="/" />
-
</Route>
-

-
<Route path="/projects/:urn/head" let:params>
-
  <View {config} urn={params.urn} path="/" />
+
<Route path="/projects/:urn/:commit/*" let:params>
+
  <View {config} urn={params.urn} commit={params.commit} path={params['*'] || "/"} />
</Route>

<Route path="/projects/:urn" let:params>
@@ -32,20 +20,12 @@

<!-- With an Org context -->

-
<Route path="/orgs/:org/projects/:urn/head/:path/*" let:params>
-
  <View {config} org={params.org} urn={params.urn} path={joinPaths(params.path, params['*'])} />
+
<Route path="/orgs/:org/projects/:urn/head/*" let:params>
+
  <View {config} org={params.org} urn={params.urn} path={params['*'] || "/"} />
</Route>

-
<Route path="/orgs/:org/projects/:urn/:commit/:path/*" let:params>
-
  <View {config} org={params.org} urn={params.urn} commit={params.commit} path={joinPaths(params.path, params['*'])} />
-
</Route>
-

-
<Route path="/orgs/:org/projects/:urn/:commit" let:params>
-
  <View {config} org={params.org} urn={params.urn} commit={params.commit} path="/" />
-
</Route>
-

-
<Route path="/orgs/:org/projects/:urn/head" let:params>
-
  <View {config} org={params.org} urn={params.urn} path="/" />
+
<Route path="/orgs/:org/projects/:urn/:commit/*" let:params>
+
  <View {config} org={params.org} urn={params.urn} commit={params.commit} path={params["*"] || "/"} />
</Route>

<Route path="/orgs/:org/projects/:urn" let:params>
modified src/base/projects/Tree.svelte
@@ -10,6 +10,7 @@
  export let fetchTree: (path: string) => Promise<Tree>;
  export let path: string;
  export let tree: Tree;
+
  export let loading: string | null = null;

  const dispatch = createEventDispatcher();
  const onSelect = ({ detail: path }: { detail: string }): void => {
@@ -29,7 +30,7 @@
  {:else}
    <File
      active={entry.path === path}
-
      loading={false}
+
      loading={entry.path === loading}
      name={entry.info.name}
      on:click={() => onSelect({ detail: entry.path })}
    />
modified src/base/projects/Tree/File.svelte
@@ -12,7 +12,7 @@
    border-radius: 0.25rem;
    cursor: pointer;
    display: flex;
-
    flex: 1;
+
    justify-content: space-between;
    line-height: 1.5em;
    margin: 0.125rem 0;
    padding: 0.25rem;
@@ -44,12 +44,10 @@
</style>

<div class="file" class:active on:click>
-
  {#if loading}
-
    <div class="spinner">
-
      <Loading small />
-
    </div>
-
  {:else}
-
    <!-- Nothing -->
-
  {/if}
  <span class="name">{name}</span>
+
  <div class="spinner">
+
    {#if loading}
+
      <Loading small condensed />
+
    {/if}
+
  </div>
</div>