From 9d1575ffec4764f834fa28b9faefee97ae88ee5b Mon Sep 17 00:00:00 2001 From: Nicola Elia <36515569+httpiga@users.noreply.github.com> Date: Tue, 17 Jan 2023 16:39:48 +0100 Subject: [PATCH] ui(Send): hide "number of collaborators" behind send options (#595) --- src/components/Accordion.tsx | 33 +++++ src/components/Earn.jsx | 125 ++++++++---------- src/components/Earn.module.css | 14 +- src/components/Orderbook.tsx | 2 +- src/components/Receive.jsx | 107 +++++++-------- src/components/Receive.module.css | 12 -- src/components/Send/CollaboratorsSelector.tsx | 109 ++++++++------- src/components/Send/index.tsx | 44 +++--- src/components/fb/SpendFidelityBondModal.tsx | 2 +- src/i18n/locales/en/translation.json | 1 + src/i18n/locales/fr/translation.json | 1 + 11 files changed, 213 insertions(+), 237 deletions(-) create mode 100644 src/components/Accordion.tsx diff --git a/src/components/Accordion.tsx b/src/components/Accordion.tsx new file mode 100644 index 00000000..d2d10ff5 --- /dev/null +++ b/src/components/Accordion.tsx @@ -0,0 +1,33 @@ +import { PropsWithChildren, useState } from 'react' +import { useSettings } from '../context/SettingsContext' +import * as rb from 'react-bootstrap' +import Sprite from './Sprite' + +interface AccordionProps { + title: string + defaultOpen?: boolean +} + +const Accordion = ({ title, defaultOpen = false, children }: PropsWithChildren) => { + const settings = useSettings() + const [isOpen, setIsOpen] = useState(defaultOpen) + + return ( +
+ setIsOpen((current) => !current)} + > + {title} + + +
+ +
{children}
+
+
+ ) +} + +export default Accordion diff --git a/src/components/Earn.jsx b/src/components/Earn.jsx index 5760df41..fc108641 100644 --- a/src/components/Earn.jsx +++ b/src/components/Earn.jsx @@ -18,6 +18,7 @@ import { EarnReportOverlay } from './EarnReport' import { OrderbookOverlay } from './Orderbook' import Balance from './Balance' import styles from './Earn.module.css' +import Accordion from './Accordion' // In order to prevent state mismatch, the 'maker stop' response is delayed shortly. // Even though the API response suggests that the maker has started or stopped immediately, it seems that this is not always the case. @@ -176,7 +177,6 @@ export default function Earn({ wallet }) { const serviceInfo = useServiceInfo() const reloadServiceInfo = useReloadServiceInfo() - const [showSettings, setShowSettings] = useState(false) const [alert, setAlert] = useState(null) const [serviceInfoAlert, setServiceInfoAlert] = useState(null) const [isLoading, setIsLoading] = useState(true) @@ -472,24 +472,11 @@ export default function Earn({ wallet }) { {!serviceInfo?.coinjoinInProgress && ( {({ handleSubmit, setFieldValue, handleChange, handleBlur, values, touched, errors, isSubmitting }) => ( - - {!serviceInfo?.makerRunning && !isWaitingMakerStart && !isWaitingMakerStop && ( -
- setShowSettings((current) => !current)} - > - {t('earn.button_settings')} - - - {showSettings && ( -
+ <> + + {!serviceInfo?.makerRunning && !isWaitingMakerStart && !isWaitingMakerStop && ( + + <> -
- {t('earn.description_rel_fee')} -
+ + {t('earn.description_rel_fee')} + {isLoading ? ( ) : ( - - + + % + {errors.feeRel} )} - {errors.feeRel}
) : ( @@ -561,16 +548,16 @@ export default function Earn({ wallet }) { : '', })} -
- {t('earn.description_abs_fee')} -
+ + {t('earn.description_abs_fee')} + {isLoading ? ( ) : ( - - + + + {errors.feeAbs} )} - {errors.feeAbs}
)} @@ -600,8 +587,8 @@ export default function Earn({ wallet }) { ) : ( - - + + + {errors.minsize} )} - {errors.minsize} + +
+ )} +
+ +
+ {(isWaitingMakerStart || isWaitingMakerStop) && ( +
- )} - -
+
- )} -
- -
- {(isWaitingMakerStart || isWaitingMakerStop) && ( -
-
-
-
+ + )} )} diff --git a/src/components/Earn.module.css b/src/components/Earn.module.css index 03aa7402..f43f9129 100644 --- a/src/components/Earn.module.css +++ b/src/components/Earn.module.css @@ -17,19 +17,7 @@ width: 100%; } -.settings-container { - margin-top: 1.5rem; -} - -.settings-container button.settings-btn { - background-color: transparent !important; - border: none; - padding-left: 0; - height: 3rem; - width: 100%; -} - -.settings-container .input-group-text { +.inputGroupText { width: 5ch; display: inline-flex; justify-content: center; diff --git a/src/components/Orderbook.tsx b/src/components/Orderbook.tsx index 54d5a58a..9e4cbf89 100644 --- a/src/components/Orderbook.tsx +++ b/src/components/Orderbook.tsx @@ -87,7 +87,7 @@ const withTooltip = (node: React.ReactElement, tooltip: string) => { // `TableNode` is known to have same properties as `ObwatchApi.Order`, hence prefer casting over object destructuring const toOrder = (tableNode: TableTypes.TableNode) => tableNode as unknown as ObwatchApi.Order -const renderOrderType = (val: string, t: TFunction<'translation', undefined>) => { +const renderOrderType = (val: string, t: TFunction) => { if (val === ObwatchApi.ABSOLUTE_ORDER_TYPE_VAL) { return withTooltip({t('orderbook.text_offer_type_absolute')}, val) } diff --git a/src/components/Receive.jsx b/src/components/Receive.jsx index 0da7bb3c..36059681 100644 --- a/src/components/Receive.jsx +++ b/src/components/Receive.jsx @@ -12,6 +12,7 @@ import { CopyButton } from './CopyButton' import { ShareButton, checkIsWebShareAPISupported } from './ShareButton' import { SelectableJar, jarFillLevel } from './jars/Jar' import styles from './Receive.module.css' +import Accordion from './Accordion' export default function Receive({ wallet }) { const { t } = useTranslation() @@ -25,7 +26,6 @@ export default function Receive({ wallet }) { const [amount, setAmount] = useState('') const [selectedJarIndex, setSelectedJarIndex] = useState(parseInt(location.state?.account, 10) || 0) const [addressCount, setAddressCount] = useState(0) - const [showSettings, setShowSettings] = useState(false) const sortedAccountBalances = useMemo(() => { if (!walletInfo) return [] @@ -105,66 +105,53 @@ export default function Receive({ wallet }) {
-
- setShowSettings((current) => !current)} - > - {t('receive.button_settings')} - - - {showSettings && ( -
- {!walletInfo || sortedAccountBalances.length === 0 ? ( - - - - ) : ( -
- {sortedAccountBalances.map((it) => ( - setSelectedJarIndex(jarIndex)} - /> - ))} -
- )} - - {t('receive.label_amount')} - - - - - setAmount(e.target.value)} - min={0} - step={1} + +
+ {!walletInfo || sortedAccountBalances.length === 0 ? ( + + + + ) : ( +
+ {sortedAccountBalances.map((it) => ( + setSelectedJarIndex(jarIndex)} /> - - - {t('receive.feedback_invalid_amount')} - - -
- )} -
-
+ ))} +
+ )} + + {t('receive.label_amount')} + + + + + setAmount(e.target.value)} + min={0} + step={1} + /> + + {t('receive.feedback_invalid_amount')} + +
+
- - {t('send.label_num_collaborators', { numCollaborators })} -
- {t('send.description_num_collaborators')} -
-
- {defaultCollaboratorsSelection.map((number) => { - const isSelected = !usesCustomNumCollaborators && numCollaborators === number - return ( - { - setUsesCustomNumCollaborators(false) - setNumCollaborators(number) - }} - disabled={disabled} - > - {number} - - ) + + {t('send.label_num_collaborators', { numCollaborators })} +
+ {t('send.description_num_collaborators')} +
+
+ {defaultCollaboratorsSelection.map((number) => { + const isSelected = !usesCustomNumCollaborators && numCollaborators === number + return ( + { + setUsesCustomNumCollaborators(false) + setNumCollaborators(number) + }} + disabled={disabled} + > + {number} + + ) + })} + { + onChange={(e) => { + setUsesCustomNumCollaborators(true) + validateAndSetCustomNumCollaborators(e.target.value) + }} + onClick={(e) => { + // @ts-ignore - FIXME: "Property 'value' does not exist on type 'EventTarget'" + if (e.target.value !== '') { setUsesCustomNumCollaborators(true) - validateAndSetCustomNumCollaborators(e.target.value) - }} - onClick={(e) => { // @ts-ignore - FIXME: "Property 'value' does not exist on type 'EventTarget'" - if (e.target.value !== '') { - setUsesCustomNumCollaborators(true) - // @ts-ignore - FIXME: "Property 'value' does not exist on type 'EventTarget'" - validateAndSetCustomNumCollaborators(e.target.value) - } - }} - disabled={disabled} - /> - {usesCustomNumCollaborators && ( - - {t('send.error_invalid_num_collaborators', { minNumCollaborators, maxNumCollaborators: 99 })} - - )} -
-
- + validateAndSetCustomNumCollaborators(e.target.value) + } + }} + disabled={disabled} + /> + {usesCustomNumCollaborators && ( + + {t('send.error_invalid_num_collaborators', { minNumCollaborators, maxNumCollaborators: 99 })} + + )} +
+
) } diff --git a/src/components/Send/index.tsx b/src/components/Send/index.tsx index 25af4b99..20496710 100644 --- a/src/components/Send/index.tsx +++ b/src/components/Send/index.tsx @@ -35,6 +35,7 @@ import { isValidJarIndex, isValidNumCollaborators, } from './helpers' +import Accordion from '../Accordion' const IS_COINJOIN_DEFAULT_VAL = true // initial value for `minimum_makers` from the default joinmarket.cfg (last check on 2022-02-20 of v0.9.5) @@ -602,19 +603,16 @@ export default function Send({ wallet }: SendProps) { )} - {alert && ( {alert.message} )} - {paymentSuccessfulInfoAlert && ( {paymentSuccessfulInfoAlert.message} )} - {!isLoading && !isOperationDisabled && isCoinjoin && !coinjoinPreconditionSummary.isFulfilled && (
)} - {!isLoading && walletInfo && ( )} - {t('send.label_source_jar')} @@ -805,24 +801,26 @@ export default function Send({ wallet }: SendProps) { {isSweep && frozenOrLockedWarning()} - - setIsCoinjoin(isToggled)} - disabled={isLoading || isOperationDisabled} - /> - + + + setIsCoinjoin(isToggled)} + disabled={isLoading || isOperationDisabled} + /> + +
+ +
+
- {isCoinjoin && ( - - )} {submitButtonOptions.text} )} - {showConfirmAbortModal && ( )} - {showConfirmSendModal && ( , i18nKey: string | string[]) => ({ +const errorResolver = (t: TFunction, i18nKey: string | string[]) => ({ resolver: (_: Response, reason: string) => `${t(i18nKey)} ${reason}`, fallbackReason: t('global.errors.reason_unknown'), }) diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index c5f15a78..43a0d046 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -232,6 +232,7 @@ "label_amount": "Amount in sats", "placeholder_amount": "Enter amount...", "feedback_invalid_amount": "Please provide a valid amount.", + "sending_options": "Sending options", "toggle_coinjoin": "Send as collaborative transaction", "toggle_coinjoin_subtitle": "Collaborative transactions improve the privacy of yourself and others.", "button_send": "Send", diff --git a/src/i18n/locales/fr/translation.json b/src/i18n/locales/fr/translation.json index 83cfbf9a..117c1140 100644 --- a/src/i18n/locales/fr/translation.json +++ b/src/i18n/locales/fr/translation.json @@ -195,6 +195,7 @@ "label_amount": "Montant en sats", "placeholder_amount": "Entrez le montant...", "feedback_invalid_amount": "Veuillez fournir un montant valide.", + "sending_options": "Options d'envoi", "toggle_coinjoin": "Envoyer en tant que transaction collaborative pour une meilleure confidentialité", "button_send": "Envoyer", "text_sending": "Envoi de ",