Newer
Older
CrypticOreWallet / src / components / addresses / picker / PickerHints.tsx
@Drew Lemmy Drew Lemmy on 12 Mar 2021 2 KB feat: address picker hints
// Copyright (c) 2020-2021 Drew Lemmy
// This file is part of KristWeb 2 under GPL-3.0.
// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt
import { useState, useEffect, useMemo } from "react";

import {
  isValidAddress, stripNameSuffix,
  useAddressPrefix, useNameSuffix
} from "@utils/currency";

import * as api from "@api";
import { KristAddressWithNames, lookupAddress } from "@api/lookup";
import { KristName } from "@api/types";

import { AddressHint } from "./AddressHint";
import { NameHint } from "./NameHint";

import { debounce } from "lodash-es";

import Debug from "debug";
const debug = Debug("kristweb:address-picker-hints");

const HINT_LOOKUP_DEBOUNCE = 250;

export function usePickerHints(
  nameHint?: boolean,
  value?: string,
  hasExactName?: boolean
): JSX.Element | null {
  const addressPrefix = useAddressPrefix();
  const nameSuffix = useNameSuffix();

  // Handle showing an address or name hint if the value is valid
  const [foundAddress, setFoundAddress] = useState<KristAddressWithNames | false | undefined>();
  const [foundName, setFoundName] = useState<KristName | false | undefined>();

  const lookupHint = useMemo(() => debounce((
    nameSuffix: string,
    value: string,
    hasAddress?: boolean,
    hasName?: boolean,
    nameHint?: boolean
  ) => {
    debug("looking up hint for %s (address: %b) (name: %b)",
      value, hasAddress, hasName);

    if (hasAddress) {
      // Lookup an address
      setFoundName(undefined);
      lookupAddress(value, nameHint)
        .then(setFoundAddress)
        .catch(() => setFoundAddress(false));
    } else if (hasName) {
      // Lookup a name
      setFoundAddress(undefined);

      const rawName = stripNameSuffix(nameSuffix, value);

      api.get<{ name: KristName }>("names/" + encodeURIComponent(rawName))
        .then(res => setFoundName(res.name))
        .catch(() => setFoundName(false));
    }
  }, HINT_LOOKUP_DEBOUNCE), []);

  // Look up the address/name if it is valid (debounced to 250ms)
  useEffect(() => {
    if (!value) {
      setFoundAddress(undefined);
      setFoundName(undefined);
      return;
    }

    // hasExactAddress fails for walletsOnly, so use this variant instead
    const hasValidAddress = !!value
      && isValidAddress(addressPrefix, value);

    if (!hasValidAddress && !hasExactName) {
      setFoundAddress(undefined);
      setFoundName(undefined);
      return;
    }

    // Perform the lookup (debounced)
    lookupHint(nameSuffix, value, hasValidAddress, hasExactName, nameHint);
  }, [lookupHint, nameSuffix, value, addressPrefix, hasExactName, nameHint]);

  // Return an address hint if possible
  if (foundAddress !== undefined) return (
    <AddressHint address={foundAddress || undefined} nameHint={nameHint} />
  );

  // Return a name hint if possible
  if (foundName !== undefined) return (
    <NameHint name={foundName || undefined} />
  );

  return null;
}