Compare commits

..

1 Commits

Author SHA1 Message Date
muzam1l
3340857575 Add owner field to NFTokenCreateOffer UI. 2023-03-14 21:38:35 +05:30
7 changed files with 55 additions and 212 deletions

View File

@@ -39,7 +39,7 @@ const Select = forwardRef<any, Props>((props, ref) => {
return {
...provided,
position: 'relative',
width: '100%',
width: '100%'
}
},
singleValue: provided => ({
@@ -108,10 +108,9 @@ const Select = forwardRef<any, Props>((props, ref) => {
dropdownIndicator: (provided, state) => {
return {
...provided,
padding: 6,
color: state.isFocused ? colors.border : colors.secondary,
':hover': {
color: colors.border,
color: colors.border
}
}
}

View File

@@ -15,12 +15,11 @@ import {
import { useSnapshot } from 'valtio'
import state from '../../state'
import { streamState } from '../DebugStream'
import { Box, Button } from '..'
import { Button } from '..'
import Textarea from '../Textarea'
import { getFlags } from '../../state/constants/flags'
import { Plus, Trash } from 'phosphor-react'
import AccountSequence from '../Sequence'
import { typeIs } from '../../utils/helpers'
interface UIProps {
setState: (pTx?: Partial<TransactionState> | undefined) => TransactionState | undefined
@@ -88,22 +87,6 @@ export const TxUI: FC<UIProps> = ({
[setState, txFields]
)
const setRawField = useCallback(
(field: keyof TxFields, type: string, value: any) => {
// TODO $type should be a narrowed type
setState({
txFields: {
...txFields,
[field]: {
$type: type,
$value: value
}
}
})
},
[setState, txFields]
)
const handleEstimateFee = useCallback(
async (state?: TransactionState, silent?: boolean) => {
setFeeLoading(true)
@@ -151,16 +134,6 @@ export const TxUI: FC<UIProps> = ({
}
const otherFields = Object.keys(txFields).filter(k => !richFields.includes(k)) as [keyof TxFields]
const amountOptions = [
{ label: 'XRP', value: 'xrp' },
{ label: 'Token', value: 'token' }
] as const
const defaultTokenAmount = {
value: '0',
currency: '',
issuer: ''
}
return (
<Container
css={{
@@ -225,106 +198,23 @@ export const TxUI: FC<UIProps> = ({
let _value = txFields[field]
let value: string | undefined
if (typeIs(_value, 'object')) {
if (_value.$type === 'json' && typeIs(_value.$value, ['object', 'array'])) {
if (typeof _value === 'object') {
if (_value.$type === 'json' && typeof _value.$value === 'object') {
value = JSON.stringify(_value.$value, null, 2)
} else {
value = _value.$value?.toString()
value = _value.$value.toString()
}
} else {
value = _value?.toString()
}
const isXrpAmount = typeIs(_value, 'object') && _value.$type === 'amount.xrp'
const isTokenAmount = typeIs(_value, 'object') && _value.$type === 'amount.token'
const isXrp = typeof _value === 'object' && _value.$type === 'xrp'
const isJson = typeof _value === 'object' && _value.$type === 'json'
const isFee = field === 'Fee'
let rows = isJson ? (value?.match(/\n/gm)?.length || 0) + 1 : undefined
if (rows && rows > 5) rows = 5
let tokenAmount = defaultTokenAmount
if (isTokenAmount && typeIs(_value, 'object') && typeIs(_value.$value, 'object')) {
tokenAmount = {
value: _value.$value.value,
currency: _value.$value.currency,
issuer: _value.$value.issuer
}
}
if (isXrpAmount || isTokenAmount) {
return (
<TxField key={field} label={field}>
<Flex fluid css={{ alignItems: 'center' }}>
{isTokenAmount ? (
<Flex fluid row align="center" justify="space-between">
<Input
type="text"
placeholder="Issuer"
value={tokenAmount.issuer}
onChange={e =>
setRawField(field, 'amount.token', {
...tokenAmount,
issuer: e.target.value
})
}
/>
<Input
css={{ mx: '$1' }}
type="text"
value={tokenAmount.currency}
placeholder="Currency"
onChange={e => {
setRawField(field, 'amount.token', {
...tokenAmount,
currency: e.target.value
})
}}
/>
<Input
type="number"
value={tokenAmount.value}
placeholder="Value"
onChange={e => {
setRawField(field, 'amount.token', {
...tokenAmount,
value: e.target.value
})
}}
/>
</Flex>
) : (
<Input
css={{ flex: 'inherit' }}
type="number"
value={value}
onChange={e => handleSetField(field, e.target.value)}
/>
)}
<Box
css={{
ml: '$2',
width: '150px'
}}
>
<Select
instanceId="currency-type"
options={amountOptions}
value={isXrpAmount ? amountOptions['0'] : amountOptions['1']}
onChange={(e: any) => {
const opt = e as typeof amountOptions[number]
if (opt.value === 'xrp') {
setRawField(field, 'amount.xrp', '0')
} else {
setRawField(field, 'amount.token', defaultTokenAmount)
}
}}
/>
</Box>
</Flex>
</TxField>
)
}
return (
<TxField key={field} label={field}>
<TxField key={field} label={field + (isXrp ? ' (XRP)' : '')}>
{isJson ? (
<Textarea
rows={rows}
@@ -548,7 +438,7 @@ export const TxField: FC<{ label: string; children: ReactNode; multiLine?: boole
justifyContent: 'flex-end',
alignItems: multiLine ? 'flex-start' : 'center',
position: 'relative',
mb: '$2',
mb: '$3',
mt: '1px',
pr: '1px'
}}

View File

@@ -28,11 +28,7 @@
"TransactionType": "CheckCash",
"Amount": {
"$value": "100",
"$type": "amount.xrp"
},
"DeliverMin": {
"$value": "",
"$type": "amount.xrp"
"$type": "xrp"
},
"CheckID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334",
"Fee": "12"
@@ -67,7 +63,7 @@
"TransactionType": "EscrowCreate",
"Amount": {
"$value": "100",
"$type": "amount.xrp"
"$type": "xrp"
},
"Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"CancelAfter": 533257958,
@@ -124,11 +120,12 @@
"NFTokenID": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007",
"Amount": {
"$value": "100",
"$type": "amount.xrp"
"$type": "xrp"
},
"Flags": "1",
"Destination": "",
"Fee": "10"
"Fee": "10",
"Owner": ""
},
{
"TransactionType": "OfferCancel",
@@ -146,17 +143,10 @@
"Flags": "0",
"LastLedgerSequence": 7108682,
"Sequence": 8,
"TakerGets": {
"$type": "amount.xrp",
"$value": "6000000"
},
"TakerPays": {
"$type": "amount.token",
"$value": {
"currency": "GKO",
"issuer": "ruazs5h1qEsqpke88pcqnaseXdm6od2xc",
"value": "2"
}
"TakerGets": "6000000",
"Amount": {
"$value": "100",
"$type": "xrp"
}
},
{
@@ -165,7 +155,7 @@
"Destination": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"Amount": {
"$value": "100",
"$type": "amount.xrp"
"$type": "xrp"
},
"Fee": "12",
"Flags": "2147483648",
@@ -176,7 +166,7 @@
"TransactionType": "PaymentChannelCreate",
"Amount": {
"$value": "100",
"$type": "amount.xrp"
"$type": "xrp"
},
"Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"SettleDelay": 86400,
@@ -192,7 +182,7 @@
"Channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198",
"Amount": {
"$value": "200",
"$type": "amount.xrp"
"$type": "xrp"
},
"Expiration": 543171558,
"Fee": "10"
@@ -248,7 +238,7 @@
"Flags": "262144",
"LastLedgerSequence": 8007750,
"LimitAmount": {
"$type": "amount.token",
"$type": "json",
"$value": {
"currency": "USD",
"issuer": "rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc",

View File

@@ -6,7 +6,6 @@ import { showAlert } from '../state/actions/showAlert'
import { parseJSON } from '../utils/json'
import { extractFlags, getFlags } from './constants/flags'
import { fromHex } from '../utils/setHook'
import { typeIs } from '../utils/helpers'
export type SelectOption = {
value: string
@@ -137,43 +136,31 @@ export const prepareTransaction = (data: any) => {
Object.keys(options).forEach(field => {
let _value = options[field]
if (!typeIs(_value, 'object')) return
// amount.xrp
if (_value.$type === 'amount.xrp') {
if (_value.$value) {
options[field] = (+(_value as any).$value * 1000000 + '')
// convert xrp
if (_value && typeof _value === 'object' && _value.$type === 'xrp') {
if (+_value.$value) {
options[field] = (+_value.$value * 1000000 + '') as any
} else {
options[field] = undefined
options[field] = undefined // 👇 💀
}
}
// amount.token
if (_value.$type === 'amount.token') {
if (typeIs(_value.$value, 'string')) {
options[field] = parseJSON(_value.$value)
} else if (typeIs(_value.$value, 'object')) {
// handle type: `json`
if (_value && typeof _value === 'object' && _value.$type === 'json') {
if (typeof _value.$value === 'object') {
options[field] = _value.$value
} else {
options[field] = undefined
try {
options[field] = JSON.parse(_value.$value)
} catch (error) {
const message = `Input error for json field '${field}': ${error instanceof Error ? error.message : ''
}`
console.error(message)
options[field] = _value.$value
}
}
}
// json
if (_value.$type === 'json') {
const val = _value.$value;
let res: any = val;
if (typeIs(val, ["object", "array"])) {
options[field] = res
} else if (typeIs(val, "string") && (res = parseJSON(val))) {
options[field] = res;
} else {
options[field] = res;
}
}
})
// delete unnecessary fields
Object.keys(options).forEach(field => {
// delete unnecessary fields
if (!options[field]) {
delete options[field]
}
@@ -267,22 +254,16 @@ export const prepareState = (value: string, transactionType?: string) => {
Object.keys(rest).forEach(field => {
const value = rest[field]
const schemaVal = schema[field as keyof TxFields]
const isAmount = schemaVal &&
typeIs(schemaVal, "object") &&
schemaVal.$type.startsWith('amount.');
if (isAmount && ["number", "string"].includes(typeof value)) {
const isXrp =
typeof value !== 'object' &&
schemaVal &&
typeof schemaVal === 'object' &&
schemaVal.$type === 'xrp'
if (isXrp) {
rest[field] = {
$type: 'amount.xrp', // Maybe have $type map or something
$type: 'xrp',
$value: +value / 1000000 // ! maybe use bigint?
}
}
else if (isAmount && typeof value === 'object') {
rest[field] = {
$type: 'amount.token',
$value: value
}
} else if (typeof value === 'object') {
rest[field] = {
$type: 'json',

View File

@@ -24,9 +24,6 @@ const estimateFee = async (
const { signedTransaction } = sign(copyTx, keypair)
const res = await xrplSend({ command: 'fee', tx_blob: signedTransaction })
if (res.error) {
throw new Error(`[${res.error}] ${res.error_exception}.`);
}
if (res && res.drops) {
return res.drops
}
@@ -34,8 +31,7 @@ const estimateFee = async (
} catch (err) {
if (!opts.silent) {
console.error(err)
const msg = err instanceof Error ? err.message : 'Error estimating fee!';
toast.error(msg);
toast.error('Cannot estimate fee.') // ? Some better msg
}
return null
}

View File

@@ -19,17 +19,3 @@ export const getFileExtention = (filename?: string): string | undefined => {
const ext = (filename.includes('.') && filename.split('.').pop()) || undefined
return ext
}
type Type = "array" | "undefined" | "object" | "string" | "number" | "bigint" | "boolean" | "symbol" | "function"
type obj = Record<string | number | symbol, unknown>
type arr = unknown[]
export const typeIs = <T extends Type>(arg: any, t: T | T[]): arg is unknown & (T extends "array" ? arr : T extends "undefined" ? undefined | null : T extends "object" ? obj : T extends "string" ? string : T extends "number" ? number : T extends "bigint" ? bigint : T extends "boolean" ? boolean : T extends "symbol" ? symbol : T extends "function" ? Function : never) => {
const types = Array.isArray(t) ? t : [t]
return types.includes(typeOf(arg) as T);
}
export const typeOf = (arg: any): Type => {
const type = arg instanceof Array ? 'array' : arg === null ? 'undefined' : typeof arg
return type;
}

View File

@@ -1,8 +1,9 @@
import { typeIs, typeOf } from './helpers'
export const extractSchemaProps = <O extends object>(obj: O) =>
Object.entries(obj).reduce((prev, [key, val]) => {
const value = typeIs(val, "object") && '$type' in val && '$value' in val ? val?.$value : val
const typeOf = <T>(arg: T) =>
arg instanceof Array ? 'array' : arg === null ? 'undefined' : typeof arg
const value = typeOf(val) === 'object' && '$type' in val && '$value' in val ? val?.$value : val
const type = typeOf(value)
let schema: any = {
@@ -11,19 +12,19 @@ export const extractSchemaProps = <O extends object>(obj: O) =>
default: value
}
if (typeIs(value, "array")) {
if (typeOf(value) === 'array') {
const item = value[0] // TODO merge other item schema's into one
if (typeIs(item, "object")) {
if (typeOf(item) !== 'object') {
schema.items = {
type: 'object',
properties: extractSchemaProps(item),
default: item
}
}
// TODO primitive-value arrays
// TODO support primitive-value arrays
}
if (typeIs(value, "object")) {
if (typeOf(value) === 'object') {
schema.properties = extractSchemaProps(value)
}
return {