Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Implement registration view
Alexis Sellier committed 5 years ago
commit f74f45bc884216d9b4a524e814518363ca7e5604
parent 1f139c12d975cc791d9f63e09805ee4e47926a03
9 files changed +158 -17
modified public/index.css
@@ -146,13 +146,25 @@ button.small {
	padding-bottom: 0.5rem;
	min-width: 6rem;
}
+
button.tiny {
+
	min-width: 0;
+
	padding: 0.125rem 0.75rem;
+
}

a {
	color: var(--color-secondary);
	text-decoration: none;
}
a:hover {
-
	text-decoration: none;
+
	color: var(--color-secondary-faded);
+
	border-bottom-color: var(--color-secondary-faded);
+
}
+
a.primary {
+
	color: var(--color-primary);
+
}
+
a.primary:hover {
+
	color: var(--color-primary-faded);
+
	border-bottom-color: var(--color-primary-faded);
}

input[type="text"], button {
@@ -240,6 +252,12 @@ input.wide {
	color: var(--color-subtle);
	font-style: italic;
}
+
.label {
+
	color: var(--color-secondary);
+
}
+
.bold {
+
	font-weight: bold;
+
}

button.error:hover {
	background-color: var(--color-negative);
@@ -254,9 +272,6 @@ td {
	text-align: left;
	text-overflow: ellipsis;
}
-
td.label {
-
	color: var(--color-secondary);
-
}
td strong {
	font-weight: 600;
}
@@ -270,3 +285,7 @@ h1 {
	text-overflow: ellipsis;
	overflow-x: hidden;
}
+

+
p {
+
	margin: 0.5rem 0;
+
}
modified src/Header.svelte
@@ -41,6 +41,7 @@
    margin-right: 1.5rem;
    font-weight: 500;
    color: var(--color-foreground-6);
+
    border: none;
  }
  header .nav a:hover {
    color: var(--color-foreground);
added src/Link.svelte
@@ -0,0 +1,17 @@
+
<script lang="typescript">
+
  import { Link } from "svelte-routing";
+

+
  export let to: string;
+
  export let primary = false;
+

+
  function getProps(_props: any) {
+
    if (primary) {
+
      return { "class": "primary" };
+
    }
+
    return {};
+
  }
+
</script>
+

+
<Link to="{to}" getProps="{getProps}">
+
  <slot />
+
</Link>
modified src/Modal.svelte
@@ -1,15 +1,7 @@
<script lang="typescript">
-
  import { createEventDispatcher } from 'svelte';
-

  export let floating = false;
  export let error = false;
  export let subtle = false;
-

-
  let dispatch = createEventDispatcher();
-

-
  const onClose = () => {
-
    dispatch('close');
-
  };
</script>

<style>
modified src/base/register/Register.svelte
@@ -95,7 +95,7 @@
  {#if state === State.NameUnavailable}
    <Modal floating>
      <span slot="title">
-
        {inputValue}.radicle.eth
+
        {inputValue}.{config.registrar.domain}
      </span>
      <span slot="body">
        The name <span class="highlight">{inputValue}</span> is not available for registration.
@@ -109,7 +109,7 @@
  {/if}

  <div class="input-caption">
-
    Register a <strong>radicle.eth</strong> name
+
    Register a <strong>{config.registrar.domain}</strong> name
  </div>
  <div class="input-main">
    <span class="name">
@@ -122,7 +122,7 @@
          disabled={state === State.CheckingAvailability}
          type="text"
        />
-
        <span class="root">.radicle.eth</span>
+
        <span class="root">.{config.registrar.domain}</span>
      </div>
    </span>
    {#if !inputValue}
added src/base/register/Registration.svelte
@@ -0,0 +1,68 @@
+
<script lang="typescript">
+
  import { getRegistration } from './registrar';
+
  import type { Config } from '@app/config';
+
  import Loading from '@app/Loading.svelte';
+
  import Link from '@app/Link.svelte';
+
  import Modal from '@app/Modal.svelte';
+

+
  export let subdomain: string;
+
  export let config: Config;
+
</script>
+

+
<style>
+
  .fields {
+
    display: grid;
+
    grid-template-columns: auto auto auto;
+
    grid-gap: 1.5rem;
+
  }
+
  .fields > div {
+
    justify-self: start;
+
    align-self: center;
+
  }
+
</style>
+

+
{#await getRegistration(subdomain, config)}
+
  <Loading />
+
{:then registration}
+
  {#if registration}
+
    <main>
+
      <h1 class="bold">{subdomain}.{config.registrar.domain}</h1>
+
      <div class="fields">
+
        <!-- Address -->
+
        <div class="label">Address</div>
+
        <div>
+
          {#if registration.address}
+
            {registration.address}
+
          {:else}
+
            <span class="subtle">Not set</span>
+
          {/if}
+
        </div>
+
        <div>
+
          {#if !registration.address}
+
            <button class="tiny primary">
+
              Set
+
            </button>
+
          {/if}
+
        </div>
+
        <!-- Owner -->
+
        <div class="label">Owner</div>
+
        <div>{registration.owner}</div>
+
        <div><button class="tiny secondary">Transfer</button></div>
+
      </div>
+
    </main>
+
  {:else}
+
    <Modal subtle>
+
      <span slot="title">
+
        {subdomain}.{config.registrar.domain}
+
      </span>
+

+
      <span slot="body">
+
        <p>The name <strong>{subdomain}</strong> is not registered.</p>
+
      </span>
+

+
      <span slot="actions">
+
        <Link to={`/register/${subdomain}`} primary>Register &rarr;</Link>
+
      </span>
+
    </Modal>
+
  {/if}
+
{/await}
modified src/base/register/Routes.svelte
@@ -3,10 +3,10 @@
  import Register from '@app/base/register/Register.svelte';
  import Begin from '@app/base/register/steps/Begin.svelte';
  import Submit from '@app/base/register/steps/Submit.svelte';
+
  import Registration from '@app/base/register/Registration.svelte';
  import Error from '@app/Error.svelte';
  import type { Config } from '@app/config';
  import type { Session } from '@app/session';
-
  import { Failure } from '@app/error';

  export let session: Session | null;
  export let config: Config;
@@ -31,3 +31,7 @@
    />
  {/if}
</Route>
+

+
<Route path="registrations/:name" let:params>
+
  <Registration {config} subdomain={params.name} />
+
</Route>
modified src/base/register/registrar.ts
@@ -22,6 +22,28 @@ const registrarAbi = [
  {"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"valid","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}
];

+
interface Registration {
+
  name: string
+
  owner: string
+
  address: string | null
+
}
+

+
export async function getRegistration(label: string, config: Config): Promise<Registration | null> {
+
  if (await registrar(config).available(label)) {
+
    // If the name is available, ie. not registered, we don't return anything.
+
    return null;
+
  }
+
  const name =`${label}.${config.registrar.domain}`;
+
  const address = await config.provider.resolveName(name);
+
  const owner = await getOwner(name, config);
+

+
  return {
+
    name,
+
    owner,
+
    address
+
  };
+
}
+

export function registrar(config: Config) {
  return new ethers.Contract(config.registrar.address, registrarAbi, config.provider);
}
@@ -112,3 +134,19 @@ function makeCommitment(name: string, owner: string, salt: Uint8Array): string {
  ]);
  return ethers.utils.keccak256(bytes);
}
+

+
async function getOwner(name: string, config: Config): Promise<string> {
+
  const ensAbi = [
+
    "function owner(bytes32 node) view returns (address)"
+
  ];
+

+
  let ensAddr = config.provider.network.ensAddress;
+
  if (! ensAddr) {
+
    throw new Error("ENS address is not defined");
+
  }
+

+
  let registry = new ethers.Contract(ensAddr, ensAbi, config.provider);
+
  let owner = await registry.owner(ethers.utils.namehash(name));
+

+
  return owner;
+
}
modified src/base/register/steps/Begin.svelte
@@ -1,4 +1,6 @@
<script lang="typescript">
+
  // TODO: Should check for availability here, before saying a name is available.
+
  // Perhaps the availability check should be moved out of the 'submit' step then.
  import { navigate } from 'svelte-routing';
  import { formatAddress } from '@app/utils';
  import { registrar } from '../registrar';
@@ -38,7 +40,7 @@

<div class="modal">
  <div class="modal-title">
-
    {subdomain}.radicle.eth
+
    {subdomain}.{config.registrar.domain}
  </div>

  <div class="modal-body">