Newer
Older
CrypticOreWallet / src / components / addresses / picker / options.ts
@Drew Lemmy Drew Lemmy on 10 Mar 2021 3 KB feat: add names to address picker
// 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 { TFunction } from "react-i18next";

import { WalletMap } from "@wallets";

import { getCategoryHeader } from "./Header";
import { getAddressItem } from "./Item";

// Ant design's autocomplete/select/rc-select components don't seem to return
// the proper types for these, so just provide our own types that are 'good
// enough'. I have a feeling the AutoComplete/Select components just accept
// basically anything for options, and passes the full objects down as props.
// The documentation on the topic is very limited.
export interface OptionValue {
  label: React.ReactNode;

  // For some reason, all these props get passed all the way to the DOM element!
  // Make this a 'valid' DOM prop
  "data-wallet-label"?: string;
  value: string;
}
export interface OptionChildren {
  label: React.ReactNode;
  options: OptionValue[];
}
export type Option = OptionValue | OptionChildren;

// -----------------------------------------------------------------------------
// WALLET OPTIONS
// -----------------------------------------------------------------------------
interface WalletOptions {
  categorised: Record<string, OptionValue[]>;
  uncategorised: OptionValue[];
  categoryCount: number;
}

/** Groups the wallets by category for autocompletion and generates their select
 * options. */
function getWalletOptions(wallets: WalletMap): WalletOptions {
  const categorised: Record<string, OptionValue[]> = {};
  const uncategorised: OptionValue[] = [];

  for (const id in wallets) {
    const wallet = wallets[id];
    const { category } = wallet;

    // Generate the autocomplete option for this wallet
    const item = getAddressItem({ wallet });

    // Group it by category if possible
    if (category) {
      if (categorised[category]) categorised[category].push(item);
      else categorised[category] = [item];
    } else {
      uncategorised.push(item);
    }
  }

  // TODO: sort the addresses too?

  return {
    categorised,
    uncategorised,
    categoryCount: Object.keys(categorised).length
  };
}

// -----------------------------------------------------------------------------
// FULL OPTIONS
// -----------------------------------------------------------------------------
/** Gets the base options to show for autocompletion, including the wallets,
 * grouped by category if possible. Will include the address book soon too. */
export function getOptions(t: TFunction, wallets: WalletMap): Option[] {
  // Wallet options
  const { categorised, uncategorised, categoryCount }
    = getWalletOptions(wallets);

  // Sort the wallet categories in a human-friendly manner
  const sortedCategories = Object.keys(categorised);
  sortedCategories.sort((a, b) => a.localeCompare(b, undefined, {
    sensitivity: "base",
    numeric: true
  }));

  // Generate the option groups for each category, along with the corresponding
  // wallet entries.
  const categoryItems = sortedCategories.map(c => ({
    ...getCategoryHeader(c),
    options: categorised[c]
  }));

  return [
    // Categorised wallets
    ...categoryItems,

    // Uncategorised wallets
    {
      ...getCategoryHeader(categoryCount > 0
        ? t("addressPicker.categoryOtherWallets")
        : t("addressPicker.categoryWallets")),
      options: uncategorised
    },

    // TODO: Address book
  ];
}