fixes to name entry, error handling, visuals

This commit is contained in:
dr-frmr 2024-08-13 23:26:34 +03:00
parent 3ba1a2e53d
commit c9f0566955
No known key found for this signature in database
5 changed files with 38 additions and 104 deletions

View File

@ -1,4 +1,4 @@
import React, { useEffect, useRef } from "react";
import React, { useEffect, useRef, useState } from "react";
import isValidDomain from "is-valid-domain";
import { toAscii } from "idna-uts46-hx";
import { usePublicClient } from 'wagmi'
@ -14,33 +14,38 @@ export const NAME_NOT_OWNER = "Name already exists and does not belong to this w
export const NAME_NOT_REGISTERED = "Name is not registered";
type ClaimOsNameProps = {
address?: `0x${string}`;
name: string;
setName: React.Dispatch<React.SetStateAction<string>>;
nameValidities: string[];
setNameValidities: React.Dispatch<React.SetStateAction<string[]>>;
triggerNameCheck: boolean;
setTba?: React.Dispatch<React.SetStateAction<string>>;
isReset?: boolean;
};
function EnterKnsName({
address,
name,
setName,
nameValidities,
setNameValidities,
triggerNameCheck,
setTba,
isReset = false,
}: ClaimOsNameProps) {
const client = usePublicClient();
const debouncer = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
if (isReset) return;
const [isPunyfied, setIsPunyfied] = useState('');
useEffect(() => {
if (debouncer.current) clearTimeout(debouncer.current);
debouncer.current = setTimeout(async () => {
let index: number;
let validities = [...nameValidities];
let validities: string[] = [];
setIsPunyfied('');
const len = [...name].length;
index = validities.indexOf(NAME_LENGTH);
@ -57,6 +62,8 @@ function EnterKnsName({
if (index === -1) validities.push(NAME_INVALID_PUNY);
}
if (normalized !== (name + ".os")) setIsPunyfied(normalized);
// only check if name is valid punycode
if (normalized && normalized !== '.os') {
index = validities.indexOf(NAME_URL);
@ -66,6 +73,7 @@ function EnterKnsName({
index = validities.indexOf(NAME_CLAIMED);
// only check if name is valid and long enough
if (validities.length === 0 || index !== -1 && normalized.length > 2) {
try {
const namehash = kinohash(normalized)
@ -78,34 +86,44 @@ function EnterKnsName({
args: [namehash]
})
const tba = data?.[0];
if (tba !== undefined) {
setTba ? (setTba(tba)) : null;
} else {
validities.push(NAME_NOT_REGISTERED);
}
const owner = data?.[1];
const owner_is_zero = owner === "0x0000000000000000000000000000000000000000";
if (!owner_is_zero && index === -1) validities.push(NAME_CLAIMED);
if (!owner_is_zero && !isReset) validities.push(NAME_CLAIMED);
if (!owner_is_zero && isReset && address && owner !== address) validities.push(NAME_NOT_OWNER);
if (isReset && owner_is_zero) validities.push(NAME_NOT_REGISTERED);
} catch (e) {
console.error({ e })
if (index !== -1) validities.splice(index, 1);
}
}
}
setNameValidities(validities);
}, 100);
}, 500);
}, [name, triggerNameCheck, isReset]);
const noDots = (e: any) =>
e.target.value.indexOf(".") === -1 && setName(e.target.value);
const noDotsOrSpaces = (e: any) =>
e.target.value.indexOf(".") === -1 && e.target.value.indexOf(" ") === -1 && setName(e.target.value);
return (
<div className="enter-kns-name">
<div className="input-wrapper">
<input
value={name}
onChange={noDots}
onChange={noDotsOrSpaces}
type="text"
required
name="dot-os-name"
placeholder="e.g. myname"
placeholder="mynode123"
className="kns-input"
/>
<span className="kns-suffix">.os</span>
@ -113,6 +131,7 @@ function EnterKnsName({
{nameValidities.map((x, i) => (
<p key={i} className="error-message">{x}</p>
))}
{isPunyfied !== '' && <p className="puny-warning">special characters will be converted to punycode: {isPunyfied}</p>}
</div>
);
}

View File

@ -135,7 +135,7 @@
.kns-suffix {
padding: 0.5rem;
background-color: var(--gray);
background-color: var(--blue);
border: 1px solid var(--tasteful-dark);
border-left: none;
border-radius: 0 4px 4px 0;
@ -240,7 +240,6 @@
color: var(--off-white);
}
button.clear {
button.secondary {
width: 100%;
background-color: var(--ansi-red);
}

View File

@ -51,7 +51,7 @@ function CommitDotOsName({
useEffect(() => setTriggerNameCheck(!triggerNameCheck), [address])
const enterOsNameProps = { name, setName, nameValidities, setNameValidities, triggerNameCheck }
const enterOsNameProps = { address, name, setName, nameValidities, setNameValidities, triggerNameCheck }
useEffect(() => {
if (!address) {
@ -102,7 +102,7 @@ function CommitDotOsName({
<>
<h3 className="form-label">
<Tooltip text="Kinodes need an onchain node identity in order to communicate with other nodes in the network.">
Choose a name for your Kinode
Choose a name for your computer
</Tooltip>
</h3>
<EnterKnsName {...enterOsNameProps} />

View File

@ -119,7 +119,7 @@ function Login({
<div className="additional-options">
<button
className="clear"
className="secondary"
onClick={() => navigate('/reset')}
>
Reset Node & Networking Info

View File

@ -2,24 +2,18 @@ import {
FormEvent,
useCallback,
useEffect,
useRef,
useState,
} from "react";
import { useNavigate } from "react-router-dom";
import { toAscii } from "idna-uts46-hx";
import isValidDomain from "is-valid-domain";
import Loader from "../components/Loader";
import { PageProps } from "../lib/types";
import { KINOMAP, MULTICALL, generateNetworkingKeys, kinomapAbi, mechAbi } from "../abis";
import { MULTICALL, generateNetworkingKeys, mechAbi } from "../abis";
import { Tooltip } from "../components/Tooltip";
import DirectCheckbox from "../components/DirectCheckbox";
import EnterKnsName from "../components/EnterKnsName";
import { useAccount, usePublicClient, useWaitForTransactionReceipt, useWriteContract } from "wagmi";
import { useAccount, useWaitForTransactionReceipt, useWriteContract } from "wagmi";
import { useConnectModal, useAddRecentTransaction } from "@rainbow-me/rainbowkit";
import { kinohash } from "../utils/kinohash";
import { NAME_URL, NAME_INVALID_PUNY, NAME_NOT_OWNER, NAME_NOT_REGISTERED } from "../components/EnterKnsName";
interface ResetProps extends PageProps { }
@ -28,7 +22,6 @@ function ResetKnsName({
setDirect,
setReset,
knsName,
setKnsName,
setNetworkingKey,
setIpAddress,
setWsPort,
@ -37,7 +30,6 @@ function ResetKnsName({
}: ResetProps) {
const { address } = useAccount();
const navigate = useNavigate();
const client = usePublicClient();
const { openConnectModal } = useConnectModal();
const { data: hash, writeContract, isPending, isError, error } = useWriteContract({
@ -54,7 +46,6 @@ function ResetKnsName({
const addRecentTransaction = useAddRecentTransaction();
const [name, setName] = useState<string>(knsName.slice(0, -3));
const [nameVets, setNameVets] = useState<string[]>([]);
const [nameValidities, setNameValidities] = useState<string[]>([])
const [tba, setTba] = useState<string>("");
const [triggerNameCheck, setTriggerNameCheck] = useState<boolean>(false);
@ -73,77 +64,6 @@ function ResetKnsName({
}
}, [address, openConnectModal]);
const nameDebouncer = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
if (nameDebouncer.current) clearTimeout(nameDebouncer.current);
nameDebouncer.current = setTimeout(async () => {
setNameVets([]);
if (name === "") return;
let index: number;
let vets = [...nameVets];
let normalized: string;
index = vets.indexOf(NAME_INVALID_PUNY);
try {
normalized = toAscii(name + ".os");
if (index !== -1) vets.splice(index, 1);
} catch (e) {
if (index === -1) vets.push(NAME_INVALID_PUNY);
}
// only check if name is valid punycode
if (normalized! !== undefined) {
index = vets.indexOf(NAME_URL);
if (name !== "" && !isValidDomain(normalized)) {
if (index === -1) vets.push(NAME_URL);
} else if (index !== -1) vets.splice(index, 1);
try {
const namehash = kinohash(normalized)
console.log('normalized', normalized)
console.log('namehash', namehash)
// maybe separate into helper function for readability?
// also note picking the right chain ID & address!
const data = await client?.readContract({
address: KINOMAP,
abi: kinomapAbi,
functionName: "get",
args: [namehash]
})
const tba = data?.[0];
const owner = data?.[1];
console.log('GOT data', data)
console.log('GOT tba', tba)
index = vets.indexOf(NAME_NOT_OWNER);
if (owner === address && index !== -1) vets.splice(index, 1);
else if (index === -1 && owner !== address)
vets.push(NAME_NOT_OWNER);
index = vets.indexOf(NAME_NOT_REGISTERED);
if (index !== -1) vets.splice(index, 1);
if (tba !== undefined) {
setTba(tba);
}
} catch (e) {
index = vets.indexOf(NAME_NOT_REGISTERED);
if (index === -1) vets.push(NAME_NOT_REGISTERED);
}
if (nameVets.length === 0) setKnsName(normalized);
}
setNameVets(vets);
}, 500);
}, [name, triggerNameCheck]); // eslint-disable-line react-hooks/exhaustive-deps
const handleResetRecords = useCallback(
async (e: FormEvent) => {
e.preventDefault();
@ -167,10 +87,6 @@ function ResetKnsName({
reset: true,
});
console.log('data', data)
console.log('tba', tba)
writeContract({
address: tba as `0x${string}`,
abi: mechAbi,
@ -213,7 +129,7 @@ function ResetKnsName({
Node ID to reset:
</Tooltip>
</h3>
<EnterKnsName {...{ name, setName, nameVets, triggerNameCheck, nameValidities, setNameValidities, isReset: true }} />
<EnterKnsName {...{ address, name, setName, triggerNameCheck, nameValidities, setNameValidities, setTba, isReset: true }} />
<DirectCheckbox {...{ direct, setDirect }} />
<p>
A reset will not delete any data. It only updates the networking information that your node publishes onchain.