1import React, { useCallback } from 'react';
2import * as ob from 'urbit-ob';
3import { Input, AccessoryIcon } from 'indigo-react';
4import { useField, useForm } from 'react-final-form';
14} from 'form/formatters';
15import { DEFAULT_HD_PATH } from 'lib/constants';
16import InputSigil from 'components/InputSigil';
19 StatelessTextInputProps,
20} from '@tlon/indigo-react';
22const PLACEHOLDER_GALAXY = '~pel';
23const PLACEHOLDER_STAR = '~sampel';
24export const PLACEHOLDER_PLANET = '~sampel-ponnym';
25const PLACEHOLDER_HD_PATH = DEFAULT_HD_PATH;
26const PLACEHOLDER_MNEMONIC =
27 'example crew supreme gesture quantum web media hazard theory mercy wing kitten';
28export const PLACEHOLDER_TICKET = '~sampel-ticlyt-migfun-falmel';
29const PLACEHOLDER_PASSWORD = '••••••-••••••-••••••-••••••';
30const PLACEHOLDER_ADDRESS = '0x';
31const PLACEHOLDER_HEX = '0x';
32const PLACEHOLDER_PRIVATE_KEY =
33 'a44de2416ee6beb2f323fab48b432925c9785808d33a6ca6d7ba00b45e9370c3';
34const PLACEHOLDER_EMAIL = 'email@example.com';
36const formatPat = buildFormatter([downcase, ensurePatFormat]);
38type TicketInputProps = React.HTMLProps<HTMLInputElement> & {
44export function TicketInput({
51 meta: { valid, error, validating, touched, active },
63 // This Input component is from the legacy `indigo-react` library, and is no longer being maintained.
64 // Ignoring the tsc compiler warning regarding unrecognized attributes from spreading `rest`
68 type={hidden ? 'password' : 'text'}
70 obscure={(value: string) => value.replace(/[^~-]+/g, '••••••')}
71 placeholder={hidden ? PLACEHOLDER_PASSWORD : PLACEHOLDER_TICKET}
75 touched && !active && error ? (
76 <AccessoryIcon.Failure />
78 <AccessoryIcon.Pending />
80 <AccessoryIcon.Success />
83 config={{ parse: formatPat }}
90export function MnemonicInput({ ...rest }) {
95 placeholder={PLACEHOLDER_MNEMONIC}
104export function HdPathInput({ ...rest }) {
109 placeholder={PLACEHOLDER_HD_PATH}
110 autoCapitalize="none"
118export function PassphraseInput({ ...rest }) {
123 placeholder="Passphrase"
124 autoCapitalize="none"
131type PointInputProps = React.HTMLProps<HTMLInputElement> & {
136export function PointInput({ name, size = 4, ...rest }: PointInputProps) {
139 meta: { active, valid, error },
141 subscription: { value: true, active: true, valid: true, error: true },
157 autoCapitalize="none"
171 config={{ parse: formatPat }}
178export function HexInput({ type = 'text', ...rest }) {
183 placeholder={PLACEHOLDER_HEX}
184 autoCapitalize="none"
188 config={{ parse: ensureHexPrefix }}
194export function PrivateKeyInput({ ...rest }) {
198 placeholder={PLACEHOLDER_PRIVATE_KEY}
199 config={{ parse: stripHexPrefix }}
205export function PsbtInput({ ...rest }) {
210 config={{ parse: stripHexPrefix }}
216export function AddressInput({ ...rest }) {
221 placeholder={PLACEHOLDER_ADDRESS}
222 autoCapitalize="none"
230export function NumberInput({ ...rest }) {
236 config={{ format: convertToNumber }}
242export function EmailInput({ ...rest }) {
247 placeholder={PLACEHOLDER_EMAIL}
248 autoCapitalize="none"
254type TicketSegmentInputProps = {
258 rest?: StatelessTextInputProps;
261export function TicketSegmentInput({
265}: TicketSegmentInputProps) {
266 const { input } = useField(name);
267 const { change } = useForm();
269 const onPaste = useCallback(
270 (event: React.ClipboardEvent<HTMLInputElement>) => {
272 const pasteBuffer = event.clipboardData.getData('text');
273 const patp = ensurePatFormat(pasteBuffer);
274 if (!ob.isValidPatp(patp)) {
275 throw new Error('invalid patp');
278 // prevent pasting the entire clipboard text into the field
279 event.preventDefault();
281 const segments = ticketToSegments(pasteBuffer);
282 segments.forEach((s, i) => {
283 change(`ticket${i}`, s);
286 console.log(`skipping ticket autofill: ${error}`);
292 // Advance the focused field after typing 6 characters
293 const onChange = useCallback(
294 (event: React.ChangeEvent<HTMLInputElement>) => {
295 const currentIndex = Number(name.replace('ticket', ''));
296 if (currentIndex < 3 && event.target.value.length === 6) {
297 const nextField: HTMLElement | null = document.querySelector(
298 `input[name=ticket${currentIndex + 1}]`
301 if (nextField !== null) {
309 // Run the react-final-form onChange handler,
310 // and our own custom one to advance the form cursor
311 const mergedInput = {
313 onChange: (e: React.ChangeEvent<HTMLInputElement>) => {
321 className={className}
323 autoCapitalize="none"
333export const HiddenInput = ({ ...rest }) => {
334 return <StatelessTextInput type="hidden" {...rest} />;
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list