Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Show Seed ID on project page
Alexis Sellier committed 4 years ago
commit 2821409418468fc6c07d0fcab6f5cb1623459065
parent fd5b6178504c6fe1d61820a6e9e6f051397cbc4c
10 files changed +86 -25
modified public/index.css
@@ -50,6 +50,7 @@
	--color-foreground-subtler: #333344;
	--color-foreground-background: #121a21;
	--color-foreground-background-subtle: #10171e;
+
	--color-foreground-background-lighter: #151f24;
	--color-foreground-1: #242e38;
	--color-foreground-2: #29343d;
	--color-foreground-3: #333e47;
modified src/api.ts
@@ -15,7 +15,7 @@ export async function get(
  }

  const base = config.seed.host;
-
  const port = config.seed.port;
+
  const port = config.seed.api.port;
  const search = new URLSearchParams(query).toString();
  const baseUrl = `https://${base}:${port}/v1/${path}`;
  const url = search ? `${baseUrl}?${search}` : baseUrl;
modified src/base/projects/Browser.svelte
@@ -34,6 +34,8 @@
  let state: State = { status: Status.Idle };
  // Whether the clone dropdown is visible.
  let cloneDropdown = false;
+
  // Whether the seed dropdown is visible.
+
  let seedDropdown = false;

  const loadBlob = async (path: string): Promise<proj.Blob> => {
    if (state.status == Status.Loaded && state.path === path) {
@@ -119,6 +121,14 @@
    margin-right: 0;
  }

+
  .seed {
+
    cursor: pointer;
+
    border-radius: 0.25rem;
+
  }
+
  .seed:hover {
+
    background-color: var(--color-foreground-background-lighter);
+
  }
+

  .clone {
    color: var(--color-primary);
    background-color: var(--color-primary-background);
@@ -131,7 +141,7 @@
  .clone:hover {
    background-color: var(--color-primary-background-lighter);
  }
-
  .clone-dropdown {
+
  .dropdown {
    background-color: var(--color-foreground-background);
    padding: 1rem;
    margin-top: 0.5rem;
@@ -143,18 +153,27 @@
    display: block;
  }
  .clone-dropdown input {
-
    font-size: 0.75rem;
    color: var(--color-primary);
+
    background: var(--color-primary-background);
+
  }
+
  .seed-dropdown input {
+
    color: var(--color-foreground-90);
+
    background: var(--color-foreground-background-lighter);
+
  }
+
  .seed-dropdown.seed-dropdown-visible {
+
    display: block;
+
  }
+
  .dropdown input {
+
    font-size: 0.75rem;
    font-family: var(--font-family-monospace);
    padding: 0.5rem;
-
    background: var(--color-primary-background);
    border: none;
    outline: none;
    width: 24rem;
-
    text-overflow: ellipsis;
+
    text-overflow: ellipsis !important;
    border-radius: 0.25rem;
  }
-
  .clone-dropdown label {
+
  .dropdown label {
    display: block;
    color: var(--color-foreground-faded);
    padding: 0.5rem 0.5rem 0 0.25rem;
@@ -245,16 +264,26 @@
      {#if config.seed.host}
        <span>
          <div class="clone" on:click={() => cloneDropdown = !cloneDropdown}>
-
            Clone ↓
+
            Clone
          </div>
-
          <div class="clone-dropdown" class:clone-dropdown-visible={cloneDropdown}>
+
          <div class="dropdown clone-dropdown" class:clone-dropdown-visible={cloneDropdown}>
            <input readonly name="clone-url" value="https://{config.seed.host}/{utils.parseRadicleId(urn)}"/>
            <label for="clone-url">Use Git to clone this repository from the URL above.</label>
          </div>
        </span>
-
        <div class="stat" title="Project data is fetched from this seed">
-
          <span>{config.seed.host}</span>
-
        </div>
+
        <span>
+
          <div class="stat seed" on:click={() => seedDropdown = !seedDropdown} title="Project data is fetched from this seed">
+
            <span>{config.seed.host}</span>
+
          </div>
+
          <div class="dropdown seed-dropdown" class:seed-dropdown-visible={seedDropdown}>
+
            {#if config.seed.id}
+
              <input readonly name="clone-url" value={`${config.seed.id}@${config.seed.host}:${config.seed.link.port}`}/>
+
              <label for="seed-url">Bootstrap your Radicle node with this seed.</label>
+
            {:else if org}
+
              <label for="#">Seed ID is not set for {org.name}.</label>
+
            {/if}
+
          </div>
+
        </span>
      {/if}
      <div class="stat">
        <strong>{tree.stats.commits}</strong> commit(s)
modified src/base/projects/View.svelte
@@ -28,8 +28,9 @@
      resolve(null);
    }
  }).then(async (orgProfile) => {
-
    const seed = orgProfile?.seed;
-
    const cfg = seed ? config.withSeed(seed) : config;
+
    const seedHost = orgProfile?.seedHost;
+
    const seedId = orgProfile?.seedId || undefined;
+
    const cfg = seedHost ? config.withSeed(seedHost, seedId) : config;
    const info = await proj.getInfo(urn, cfg);

    projectInfo = info;
modified src/base/registrations/View.svelte
@@ -79,7 +79,7 @@
            { name: "seed.host", label: "Seed Host", validate: "domain", placeholder: "seed.acme.org",
              description: "The seed host address. " +
                "Only domain names with TLS are supported. " +
-
                `HTTP(S) API requests use port ${config.seed.port}.`,
+
                `HTTP(S) API requests use port ${config.seed.api.port}.`,
              value: r.profile.seedHost, editable: true },
            { name: "seed.id", label: "Seed ID", validate: "id", placeholder: "hynkyndc6w3p8urucakobzncqny7xxtw88...",
              description: "The Device ID of a Radicle Link node that hosts entities associated with this name.",
modified src/base/registrations/registrar.ts
@@ -106,7 +106,7 @@ export async function getAvatar(name: string, config: Config, resolver?: Resolve
  return resolver.getText('avatar');
}

-
export async function getSeed(name: string, config: Config, resolver?: Resolver): Promise<string | null> {
+
export async function getSeedHost(name: string, config: Config, resolver?: Resolver): Promise<string | null> {
  name = name.toLowerCase();

  resolver = resolver ?? await config.provider.getResolver(name);
@@ -116,6 +116,16 @@ export async function getSeed(name: string, config: Config, resolver?: Resolver)
  return resolver.getText('eth.radicle.seed.host');
}

+
export async function getSeedId(name: string, config: Config, resolver?: Resolver): Promise<string | null> {
+
  name = name.toLowerCase();
+

+
  resolver = resolver ?? await config.provider.getResolver(name);
+
  if (! resolver) {
+
    return null;
+
  }
+
  return resolver.getText('eth.radicle.seed.id');
+
}
+

export function registrar(config: Config): ethers.Contract {
  return new ethers.Contract(config.registrar.address, config.abi.registrar, config.provider);
}
modified src/config.json
@@ -50,7 +50,13 @@
  },
  "walletConnect": { "bridge": "https://radicle.bridge.walletconnect.org" },
  "ceramic": { "api": "https://gateway.ceramic.network" },
-
  "radicle": { "seed": { "host": "0.0.0.0", "port": 8777 } },
+
  "radicle": {
+
    "seed": {
+
      "host": "0.0.0.0",
+
      "api": { "port": 8777 },
+
      "link": { "port": 8776 }
+
    }
+
  },
  "ipfs": { "gateway": "https://ipfs.io/ipfs/" },
  "abi": {
    "registrar": [
modified src/config.ts
@@ -55,7 +55,12 @@ export class Config {
    viewer: string | null;
  };
  abi: { [contract: string]: string[] };
-
  seed: { host?: string; port: number };
+
  seed: {
+
    host?: string;
+
    id?: string;
+
    api: { port: number };
+
    link: { port: number };
+
  };
  idx: { client: IDX };
  ceramic: { client: CeramicClient };
  tokens: string[];
@@ -125,11 +130,14 @@ export class Config {
  }

  // Return the config with an overwritten seed URL.
-
  withSeed(seedHost: string): Config {
+
  withSeed(seedHost: string, seedId?: string): Config {
    const cfg = {} as Config;
    Object.assign(cfg, this);
    cfg.seed.host = seedHost;

+
    if (seedId) {
+
      cfg.seed.id = seedId;
+
    }
    return cfg;
  }

modified src/profile.ts
@@ -75,10 +75,15 @@ export class Profile {
  }

  // Using undefined as return type if nothing to be returned since it works better with <a href> links
-
  get seed(): string | undefined {
+
  get seedHost(): string | undefined {
    return this.profile?.ens?.seedHost ?? undefined;
  }

+
  // Using undefined as return type if nothing to be returned since it works better with <a href> links
+
  get seedId(): string | undefined {
+
    return this.profile?.ens?.seedId ?? undefined;
+
  }
+

  // Get the name, and if not available, the address.
  get nameOrAddress(): string {
    return this.name ?? this.address;
@@ -87,8 +92,8 @@ export class Profile {
  // Return the profile-specific config. This sets various URLs in the config,
  // based on profile data.
  config(config: Config): Config {
-
    if (this.seed) {
-
      return config.withSeed(this.seed);
+
    if (this.seedHost) {
+
      return config.withSeed(this.seedHost, this.seedId);
    }
    return config;
  }
modified src/utils.ts
@@ -7,7 +7,7 @@ import type { Config } from '@app/config';
import config from "@app/config.json";
import { assert } from '@app/error';
import type { EnsProfile } from "@app/base/registrations/registrar";
-
import { getAvatar, getSeed, getRegistration } from '@app/base/registrations/registrar';
+
import { getAvatar, getSeedHost, getSeedId, getRegistration } from '@app/base/registrations/registrar';
import type { BasicProfile } from "@ceramicstudio/idx-constants";
import { ProfileType } from '@app/profile';

@@ -282,13 +282,14 @@ export async function resolveEnsProfile(addressOrName: string, profileType: Prof
      }

      if (profileType === ProfileType.Project) {
-
        promises.push(getSeed(name, config, resolver));
+
        promises.push(getSeedHost(name, config, resolver));
+
        promises.push(getSeedId(name, config, resolver));
      } else if (profileType === ProfileType.Minimal) {
        promises.push(Promise.resolve(null));
      }

      const project = await Promise.allSettled(promises);
-
      const [avatar, address, seedHost] =
+
      const [avatar, address, seedHost, seedId] =
        project.map(r => r.status == "fulfilled" ? r.value : null);

      return {
@@ -296,8 +297,8 @@ export async function resolveEnsProfile(addressOrName: string, profileType: Prof
        avatar,
        address,
        seedHost,
+
        seedId,
        url: null,
-
        seedId: null,
        twitter: null,
        github: null,
      };