Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Add loading more projects
Sebastian Martinez committed 3 years ago
commit b1e0c7a1b52d457495b49f40485109a9c2bd576d
parent 733f5c6c8a3fc5647eed41ea653d2f65bb2af8f9
5 files changed +83 -41
modified src/Profile.svelte
@@ -20,6 +20,7 @@
  import { MissingReverseRecord, NotFoundError } from "@app/error";
  import NotFound from "@app/NotFound.svelte";
  import RadicleUrn from "@app/RadicleUrn.svelte";
+
  import Async from "@app/Async.svelte";
  import Badge from "@app/Badge.svelte";
  import Button from "@app/Button.svelte";

@@ -446,7 +447,14 @@
      {/await}
    {/if}
    {#if profile.seed?.valid}
-
      <Projects {profile} seed={profile.seed} {account} {config} />
+
      <Async fetch={profile.seed.getProjects(10, profile.id)} let:result>
+
        <Projects
+
          {profile}
+
          seed={profile.seed}
+
          {account}
+
          projects={result}
+
          {config} />
+
      </Async>
    {/if}
  </main>

modified src/base/orgs/View/Projects.svelte
@@ -3,21 +3,23 @@
  import { onMount } from "svelte";
  import type { Config } from "@app/config";
  import * as proj from "@app/project";
-
  import Loading from "@app/Loading.svelte";
-
  import Message from "@app/Message.svelte";
  import Widget from "@app/base/projects/Widget.svelte";
  import type { Profile } from "@app/profile";
  import type { ProjectInfo, Anchor, PendingAnchor } from "@app/project";
  import type { Seed } from "@app/base/seeds/Seed";
  import AnchorActions from "@app/base/profiles/AnchorActions.svelte";
+
  import List from "@app/List.svelte";

  export let seed: Seed;
  export let profile: Profile | null = null;
  export let account: string | null = null;
+
  export let projects: proj.ProjectInfo[];
  export let config: Config;

  let anchors: Record<string, Anchor> = {};
  let pendingAnchors: Record<string, PendingAnchor> = {};
+
  // A pointer to the current page of projects added to the listing
+
  let page = 0;

  const loadAnchors = async () => {
    if (profile) {
@@ -31,6 +33,19 @@
    }
  };

+
  const fetchMoreProjects = async (): Promise<proj.ProjectInfo[]> => {
+
    const projects = await proj.Project.getProjects(seed.api, {
+
      perPage: 10,
+
      page: (page += 1),
+
    });
+
    if (projects.length > 0) {
+
      return projects;
+
    }
+

+
    // We return an empty array, for when no more projects are found, since List is looking for an iterable.
+
    return [];
+
  };
+

  const onClick = (project: ProjectInfo) => {
    navigate(
      proj.path({
@@ -59,36 +74,31 @@
</style>

<div class="projects">
-
  {#await seed.getProjects(profile?.id)}
-
    <Loading center />
-
  {:then projects}
-
    {#each projects as project}
-
      {@const anchor = anchors[project.urn]}
-
      {@const pendingAnchor = pendingAnchors[project.urn]}
-
      {#if project.head}
-
        <div class="project">
-
          <Widget {project} {seed} {anchor} on:click={() => onClick(project)}>
-
            <span class="actions" slot="actions">
-
              {#if profile?.org?.safe && account && anchor}
-
                {#if pendingAnchor}
-
                  <!-- Pending anchor -->
-
                  <AnchorActions
-
                    {account}
-
                    {config}
-
                    anchor={pendingAnchor}
-
                    safe={profile.org.safe}
-
                    on:success={() => loadAnchors()} />
+
  <List items={projects} query={fetchMoreProjects}>
+
    <svelte:fragment slot="list" let:items>
+
      {#each items as project}
+
        {@const anchor = anchors[project.urn]}
+
        {@const pendingAnchor = pendingAnchors[project.urn]}
+
        {#if project.head}
+
          <div class="project">
+
            <Widget {project} {seed} {anchor} on:click={() => onClick(project)}>
+
              <span class="actions" slot="actions">
+
                {#if profile?.org?.safe && account && anchor}
+
                  {#if pendingAnchor}
+
                    <!-- Pending anchor -->
+
                    <AnchorActions
+
                      {account}
+
                      {config}
+
                      anchor={pendingAnchor}
+
                      safe={profile.org.safe}
+
                      on:success={() => loadAnchors()} />
+
                  {/if}
                {/if}
-
              {/if}
-
            </span>
-
          </Widget>
-
        </div>
-
      {/if}
-
    {/each}
-
  {:catch err}
-
    <Message error>
-
      <strong>Error:</strong>
-
      failed to load projects: {err.message}.
-
    </Message>
-
  {/await}
+
              </span>
+
            </Widget>
+
          </div>
+
        {/if}
+
      {/each}
+
    </svelte:fragment>
+
  </List>
</div>
modified src/base/seeds/Seed.ts
@@ -105,10 +105,10 @@ export class Seed {
    return proj.Project.getInfo(urn, this.api);
  }

-
  async getProjects(id?: string): Promise<proj.ProjectInfo[]> {
+
  async getProjects(perPage: number, id?: string): Promise<proj.ProjectInfo[]> {
    const result = id
-
      ? await proj.Project.getDelegateProjects(id, this.api)
-
      : await proj.Project.getProjects(this.api);
+
      ? await proj.Project.getDelegateProjects(id, this.api, { perPage })
+
      : await proj.Project.getProjects(this.api, { perPage });

    return result.map((project: proj.ProjectInfo) => ({
      ...project,
modified src/base/seeds/View.svelte
@@ -11,12 +11,16 @@
  import Address from "@app/Address.svelte";
  import SiweConnect from "@app/SiweConnect.svelte";
  import type { SeedSession } from "@app/siwe";
+
  import Async from "@app/Async.svelte";
+
  import { Project } from "@app/project";
+
  import type { Host } from "@app/api";

  export let config: Config;
  export let session: Session | null;
  export let host: string;

  const hostName = formatSeedHost(host);
+
  const seedHost: Host = { host, port: null };
  let siweSession: SeedSession | null = null;

  $: if (session?.siwe) {
@@ -153,7 +157,9 @@
      <div class="desktop" />
    </div>
    <!-- Seed Projects -->
-
    <Projects {seed} {config} />
+
    <Async fetch={Project.getProjects(seedHost, { perPage: 10 })} let:result>
+
      <Projects {seed} {config} projects={result} />
+
    </Async>
  </main>
{:catch}
  <NotFound
modified src/project.ts
@@ -330,15 +330,33 @@ export class Project implements ProjectInfo {
    };
  }

-
  static async getProjects(host: Host): Promise<ProjectInfo[]> {
-
    return new Request("projects", host).get();
+
  static async getProjects(
+
    host: Host,
+
    opts?: {
+
      perPage?: number;
+
      page?: number;
+
    },
+
  ): Promise<ProjectInfo[]> {
+
    const params: Record<string, any> = {
+
      "per-page": opts?.perPage,
+
      page: opts?.page,
+
    };
+
    return new Request("projects", host).get(params);
  }

  static async getDelegateProjects(
    delegate: string,
    host: Host,
+
    opts?: {
+
      perPage?: number;
+
      page?: number;
+
    },
  ): Promise<ProjectInfo[]> {
-
    return new Request(`delegates/${delegate}/projects`, host).get();
+
    const params: Record<string, any> = {
+
      "per-page": opts?.perPage,
+
      page: opts?.page,
+
    };
+
    return new Request(`delegates/${delegate}/projects`, host).get(params);
  }

  static async getRemote(