diff --git a/src/features/shared/suggestion-list/_index.scss b/src/features/shared/suggestion-list/_index.scss index a710f4a08..6e1a4aace 100644 --- a/src/features/shared/suggestion-list/_index.scss +++ b/src/features/shared/suggestion-list/_index.scss @@ -8,10 +8,6 @@ z-index: 10; } - .suggestion-list-parent { - @apply absolute border border-[--border-color] left-0 top-[calc(100%+0.5rem)] w-full z-[100] rounded-2xl overflow-hidden min-w-[200px] bg-white dark:bg-dark-600-010-light mb-4 - } - .search-suggestion-list { .list-body { @@ -21,19 +17,7 @@ } .modal-suggestion-list { - width: 100%; - box-shadow: $box-shadow; - z-index: 9; - min-width: 200px; - overflow: hidden; - - @include themify(day) { - @apply bg-white; - } - - @include themify(night) { - @apply bg-dark-600-010-light; - } + @apply w-full border border-[--border-color] rounded-2xl z-10 min-w-[200px] bg-white overflow-hidden; .list-body { max-height: 200px; @@ -49,11 +33,6 @@ align-items: center; white-space: nowrap; - &:last-child { - border-bottom-left-radius: $border-radius; - border-bottom-right-radius: $border-radius; - } - @include themify(day) { @apply text-gray-warm; diff --git a/src/features/shared/suggestion-list/index.tsx b/src/features/shared/suggestion-list/index.tsx index dc14d0a54..a51b3eca2 100644 --- a/src/features/shared/suggestion-list/index.tsx +++ b/src/features/shared/suggestion-list/index.tsx @@ -185,12 +185,11 @@ export function SuggestionList({ opacity: 0, scaleY: 0.85 }} - className="suggestion-list-parent origin-top" + className="absolute border border-[--border-color] left-0 top-[calc(100%+0.5rem)] w-full z-[100] rounded-2xl overflow-hidden min-w-[200px] bg-white mb-4 origin-top" > - {modeItems.map((modeItem, modeKey) => { - const _items = modeItem.items; - return ( - _items.length > 0 && ( + {modeItems.map( + (modeItem, modeKey) => + modeItem.items.length > 0 && (
{modeItem.header && (
@@ -198,7 +197,7 @@ export function SuggestionList({
)}
- {_items.map((x: any, i: number) => ( + {modeItem.items.map((x: any, i: number) => (
) - ); - })} + )}
{header && ( -
+
{header}
)} @@ -258,7 +256,7 @@ export function SuggestionList({
{ e.preventDefault(); onSelect?.(x); diff --git a/src/features/shared/transfer/transfer-step-1-to.tsx b/src/features/shared/transfer/transfer-step-1-to.tsx new file mode 100644 index 000000000..f361856e0 --- /dev/null +++ b/src/features/shared/transfer/transfer-step-1-to.tsx @@ -0,0 +1,89 @@ +import i18next from "i18next"; +import { SuggestionList, UserAvatar } from "@/features/shared"; +import { FormControl, InputGroup } from "@ui/input"; +import { TransferFormText } from "@/features/shared/transfer/transfer-form-text"; +import React, { useContext, useMemo } from "react"; +import { TransferSharedStateContext } from "@/features/shared/transfer/transfer-shared-state"; +import { getTransactionsQuery } from "@/api/queries"; +import { useGlobalStore } from "@/core/global-store"; + +interface Props { + toWarning: string | undefined; + toError: Error | null; +} + +export function TransferStep1To({ toWarning, toError }: Props) { + const { setTo, to, exchangeWarning } = useContext(TransferSharedStateContext); + + const activeUser = useGlobalStore((s) => s.activeUser); + + const { data: transactions } = getTransactionsQuery(activeUser?.username).useClientQuery(); + const transactionsFlow = useMemo( + () => transactions?.pages.reduce((acc, page) => [...acc, ...page], []) ?? [], + [transactions] + ); + + const recent = useMemo( + () => + Array.from( + new Set( + transactionsFlow + .filter( + (x) => + (x.type === "transfer" && x.from === activeUser?.username) || + (x.type === "delegate_vesting_shares" && x.delegator === activeUser?.username) + ) + .map((x) => + x.type === "transfer" ? x.to : x.type === "delegate_vesting_shares" ? x.delegatee : "" + ) + .filter((x) => { + if (to!.trim() === "") { + return true; + } + + return x.indexOf(to!) !== -1; + }) + .reverse() + .slice(0, 5) + ) ?? [] + ), + [activeUser?.username, to, transactionsFlow] + ); + + return ( + <> +
+
+ +
+
+ setTo(to)} + items={recent} + renderer={(i) => ( + <> + + {i} + + )} + header={i18next.t("transfer.recent-transfers")} + > + + setTo(e.target.value)} + className={toError ? "is-invalid" : ""} + /> + + +
+
+ {toWarning && } + {toError && } + {exchangeWarning && } + + ); +} diff --git a/src/features/shared/transfer/transfer-step-1.tsx b/src/features/shared/transfer/transfer-step-1.tsx index 941b22c91..9aaf1acd9 100644 --- a/src/features/shared/transfer/transfer-step-1.tsx +++ b/src/features/shared/transfer/transfer-step-1.tsx @@ -1,7 +1,7 @@ import { TransferFormHeader } from "@/features/shared/transfer/transfer-form-header"; import i18next from "i18next"; import { Button } from "@ui/button"; -import { LinearProgress, SuggestionList, TransferAsset, UserAvatar } from "@/features/shared"; +import { TransferAsset } from "@/features/shared"; import { Form } from "@ui/form"; import { FormControl, InputGroup } from "@ui/input"; import React, { useCallback, useEffect, useMemo } from "react"; @@ -14,12 +14,7 @@ import { vestsToHp } from "@/utils"; import { useGlobalStore } from "@/core/global-store"; -import { - DEFAULT_DYNAMIC_PROPS, - getDynamicPropsQuery, - getPointsQuery, - getTransactionsQuery -} from "@/api/queries"; +import { DEFAULT_DYNAMIC_PROPS, getDynamicPropsQuery, getPointsQuery } from "@/api/queries"; import { TransferFormText } from "@/features/shared/transfer/transfer-form-text"; import { TransferAssetSwitch } from "@/features/shared/transfer/transfer-assets-switch"; import { EXCHANGE_ACCOUNTS } from "@/consts"; @@ -28,6 +23,7 @@ import { useDebounceTransferAccountData } from "./use-debounce-transfer-account- import { amountFormatCheck } from "@/utils/amount-format-check"; import { cryptoUtils } from "@hiveio/dhive"; import { EcencyConfigManager } from "@/config"; +import { TransferStep1To } from "@/features/shared/transfer/transfer-step-1-to"; interface Props { titleLngKey: string; @@ -44,7 +40,6 @@ export function TransferStep1({ titleLngKey }: Props) { to, mode, setExchangeWarning, - setTo, setStep, setAmount, amount, @@ -58,57 +53,29 @@ export function TransferStep1({ titleLngKey }: Props) { const { data: activeUserPoints } = getPointsQuery(activeUser?.username).useClientQuery(); const { data: dynamicProps } = getDynamicPropsQuery().useClientQuery(); - const { data: transactions, isLoading: inProgress } = getTransactionsQuery( - activeUser?.username - ).useClientQuery(); const { toWarning, toData, delegatedAmount, toError, delegateAccount } = useDebounceTransferAccountData(); - const transactionsFlow = useMemo( - () => transactions?.pages.reduce((acc, page) => [...acc, ...page], []) ?? [], - [transactions] - ); const w = useMemo( () => new HiveWallet(activeUser!.data, dynamicProps ?? DEFAULT_DYNAMIC_PROPS), [activeUser, dynamicProps] ); const subTitleLngKey = useMemo(() => `${mode}-sub-title`, [mode]); - const recent = useMemo( - () => - Array.from( - new Set( - transactionsFlow - .filter( - (x) => - (x.type === "transfer" && x.from === activeUser?.username) || - (x.type === "delegate_vesting_shares" && x.delegator === activeUser?.username) - ) - .map((x) => - x.type === "transfer" ? x.to : x.type === "delegate_vesting_shares" ? x.delegatee : "" - ) - .filter((x) => { - if (to!.trim() === "") { - return true; - } - - return x.indexOf(to!) !== -1; - }) - .reverse() - .slice(0, 5) - ) ?? [] - ), - [activeUser?.username, to, transactionsFlow] + + const showTo = useMemo( + () => ["transfer", "transfer-saving", "withdraw-saving", "power-up", "delegate"].includes(mode), + [mode] ); const canSubmit = useMemo( () => toData && !toError && + (!showTo || to) && !amountError && !memoError && - !inProgress && !exchangeWarning && parseFloat(amount) > 0, - [amount, amountError, exchangeWarning, inProgress, memoError, toData, toError] + [amount, amountError, exchangeWarning, memoError, showTo, to, toData, toError] ); const assets = useMemo(() => { let assets: TransferAsset[] = []; @@ -141,10 +108,6 @@ export function TransferStep1({ titleLngKey }: Props) { return assets; }, [mode]); - const showTo = useMemo( - () => ["transfer", "transfer-saving", "withdraw-saving", "power-up", "delegate"].includes(mode), - [mode] - ); const showMemo = useMemo( () => ["transfer", "transfer-saving", "withdraw-saving"].includes(mode), [mode] @@ -217,26 +180,15 @@ export function TransferStep1({ titleLngKey }: Props) { return balance; }, [delegatedAmount, getBalance]); - const exchangeHandler = useCallback( - (to: string, memo: string) => { - if (EXCHANGE_ACCOUNTS.includes(to)) { - if ((asset === "HIVE" || asset === "HBD") && !memo) { - setExchangeWarning(i18next.t("transfer.memo-required")); - } else { - setExchangeWarning(""); - } + useEffect(() => { + if (EXCHANGE_ACCOUNTS.includes(to)) { + if ((asset === "HIVE" || asset === "HBD") && !memo) { + setExchangeWarning(i18next.t("transfer.memo-required")); + } else { + setExchangeWarning(""); } - }, - [asset, setExchangeWarning] - ); - - const toChanged = useCallback( - (e: React.ChangeEvent) => { - setTo(e.target.value); - exchangeHandler(e.target.value, memo); - }, - [exchangeHandler, memo, setTo] - ); + } + }, [to, memo, asset, setExchangeWarning]); const nextPowerDown = useCallback(() => { setStep(2); @@ -256,9 +208,8 @@ export function TransferStep1({ titleLngKey }: Props) { const mError = cryptoUtils.isWif(memo.trim()); if (mError) setMemoError(i18next.t("transfer.memo-error").toUpperCase()); setMemo(memo); - exchangeHandler(to, memo); }, - [exchangeHandler, setMemo, setMemoError, to] + [setMemo, setMemoError] ); return ( @@ -290,9 +241,8 @@ export function TransferStep1({ titleLngKey }: Props) {
)} {step === 1 && (mode !== "power-down" || !w.isPoweringDown) && ( -
+
- {inProgress && }
@@ -305,47 +255,7 @@ export function TransferStep1({ titleLngKey }: Props) {
- {showTo && ( - <> -
-
- -
-
- { - setTo(to); - exchangeHandler(to, memo); - }} - items={recent} - renderer={(i) => ( - <> - - {i} - - )} - header={i18next.t("transfer.recent-transfers")} - > - - - - -
-
- {toWarning && } - {toError && ( - - )} - {exchangeWarning && } - - )} + {showTo && }
@@ -369,10 +279,7 @@ export function TransferStep1({ titleLngKey }: Props) { { - setAsset(e); - exchangeHandler(to, memo); - }} + onChange={(e) => setAsset(e)} /> )}
diff --git a/src/features/shared/transfer/use-debounce-transfer-account-data.ts b/src/features/shared/transfer/use-debounce-transfer-account-data.ts index 708148ebb..8cc18e31d 100644 --- a/src/features/shared/transfer/use-debounce-transfer-account-data.ts +++ b/src/features/shared/transfer/use-debounce-transfer-account-data.ts @@ -110,12 +110,11 @@ export function useDebounceTransferAccountData() { "claim-interest" ].includes(mode) ) { - setTo(activeUser?.username ?? ""); return activeUser?.data; } return toData; - }, [activeUser?.data, activeUser?.username, mode, setTo, toData]), + }, [activeUser?.data, mode, toData]), toError, delegateAccount, isLoading: useMemo(() => toLoading || vestingLoading, [toLoading, vestingLoading]) diff --git a/src/features/ui/button/styles.ts b/src/features/ui/button/styles.ts index c52464c5e..bfa548c02 100644 --- a/src/features/ui/button/styles.ts +++ b/src/features/ui/button/styles.ts @@ -19,7 +19,7 @@ export const BUTTON_OUTLINE_STYLES: Record = { primary: "border-blue-dark-sky hover:border-blue-dark-sky-hover focus:border-blue-dark-sky-active text-blue-dark-sky hover:text-blue-dark-sky-hover focus:text-blue-dark-sky-active disabled:opacity-50", secondary: - "border-gray-400 hover:border-gray-700 focus:border-gray-800 text-gray-600 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-500", + "border-[--border-color] hover:bg-[--border-color] focus:bg-[--border-color] hover:bg-opacity-10 focus:bg-opacity-20 text-gray-600 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-500", link: "", danger: "border-red hover:border-red-020 focus:border-red-030 text-red hover:text-red-020 focus:text-red-030",