Skip to content

Commit

Permalink
ui(send): add warn sign to selected jar on precondition violations (#648
Browse files Browse the repository at this point in the history
)

* ui(send): move precondition violation alert below jar selector

* ui(send): display warn sign in selectable jar on precondition violations

* ui(send): improve sending-in-progress screen

* ui(send): add checkmark to payment success alert
  • Loading branch information
theborakompanioni authored Aug 28, 2023
1 parent 7a6b920 commit 77c3ec9
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 51 deletions.
3 changes: 3 additions & 0 deletions public/sprite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
89 changes: 55 additions & 34 deletions src/components/Send/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,6 @@ function SweepAccordionToggle({ eventKey }: SweepAccordionToggleProps) {
)
}

type Alert = {
variant: 'success' | 'danger'
message: string
}

type SendProps = {
wallet: CurrentWallet
}
Expand All @@ -78,7 +73,7 @@ export default function Send({ wallet }: SendProps) {
const isCoinjoinInProgress = useMemo(() => serviceInfo && serviceInfo.coinjoinInProgress, [serviceInfo])
const isMakerRunning = useMemo(() => serviceInfo && serviceInfo.makerRunning, [serviceInfo])

const [alert, setAlert] = useState<Alert>()
const [alert, setAlert] = useState<SimpleAlert>()
const [isSending, setIsSending] = useState(false)
const [isCoinjoin, setIsCoinjoin] = useState(IS_COINJOIN_DEFAULT_VAL)
const [minNumCollaborators, setMinNumCollaborators] = useState(MINIMUM_MAKERS_DEFAULT_VAL)
Expand All @@ -92,7 +87,7 @@ export default function Send({ wallet }: SendProps) {
const [showFeeConfigModal, setShowFeeConfigModal] = useState(false)

const [waitForUtxosToBeSpent, setWaitForUtxosToBeSpent] = useState([])
const [paymentSuccessfulInfoAlert, setPaymentSuccessfulInfoAlert] = useState<Alert>()
const [paymentSuccessfulInfoAlert, setPaymentSuccessfulInfoAlert] = useState<SimpleAlert>()

const isOperationDisabled = useMemo(
() => isCoinjoinInProgress || isMakerRunning || waitForUtxosToBeSpent.length > 0,
Expand Down Expand Up @@ -143,7 +138,7 @@ export default function Send({ wallet }: SendProps) {
return walletInfo.data.utxos.utxos.filter((it) => it.mixdepth === sourceJarIndex)
}, [walletInfo, sourceJarIndex])

const coinjoinPreconditionSummary = useMemo(() => {
const sourceJarCoinjoinPreconditionSummary = useMemo(() => {
if (sourceJarUtxos === null) return null
return buildCoinjoinRequirementSummary(sourceJarUtxos)
}, [sourceJarUtxos])
Expand All @@ -160,23 +155,24 @@ export default function Send({ wallet }: SendProps) {
const submitButtonRef = useRef<HTMLButtonElement>(null)
const submitButtonOptions = useMemo(() => {
if (!isLoading) {
if (!isCoinjoin)
if (!isCoinjoin) {
return {
variant: 'danger',
text: t('send.button_send_without_improved_privacy'),
}
if (coinjoinPreconditionSummary && !coinjoinPreconditionSummary.isFulfilled)
} else if (sourceJarCoinjoinPreconditionSummary?.isFulfilled === false) {
return {
variant: 'warning',
text: t('send.button_send_despite_warning'),
}
}
}

return {
variant: 'dark',
text: t('send.button_send'),
}
}, [isLoading, isCoinjoin, coinjoinPreconditionSummary, t])
}, [isLoading, isCoinjoin, sourceJarCoinjoinPreconditionSummary, t])

const formIsValid = useMemo(() => {
return (
Expand Down Expand Up @@ -593,25 +589,35 @@ export default function Send({ wallet }: SendProps) {
<rb.Row className="align-items-center">
<rb.Col>{t('send.text_maker_running')}</rb.Col>
<rb.Col xs="auto">
<Sprite symbol="caret-right" width="24px" height="24px" />
<Sprite symbol="caret-right" width="24" height="24" />
</rb.Col>
</rb.Row>
</rb.Alert>
</Link>
)}
{isCoinjoinInProgress && (
<rb.Alert variant="info" className="mb-4 d-flex align-items-center">
{t('send.text_coinjoin_already_running')}

<div className="mb-4">
<div className="d-flex align-items-center justify-content-center mb-2">
<div className="d-flex align-items-center justify-content-center alert alert-success rounded-circle p-3">
<Sprite symbol="clock" width="32" height="32" />
</div>
</div>
<rb.Alert variant="success" className="d-flex align-items-center">
{t('send.text_coinjoin_already_running')}
<Sprite className="ms-auto" symbol="joining" width="20" height="20" />
</rb.Alert>
<rb.Button
variant={'outline-light'}
className="ms-auto"
variant="none"
className="w-100 mb-4"
disabled={showConfirmAbortModal}
onClick={() => abortCoinjoin()}
>
{t('global.abort')}
<div className="d-flex justify-content-center align-items-center">
<Sprite symbol="cancel" width="24" height="24" className="me-1" />
{t('global.abort')}
</div>
</rb.Button>
</rb.Alert>
</div>
)}
</>
</rb.Fade>
Expand All @@ -621,22 +627,17 @@ export default function Send({ wallet }: SendProps) {
</rb.Alert>
)}
{paymentSuccessfulInfoAlert && (
<rb.Alert className="small slashed-zeroes break-word" variant={paymentSuccessfulInfoAlert.variant}>
{paymentSuccessfulInfoAlert.message}
</rb.Alert>
)}
{!isLoading &&
!isOperationDisabled &&
isCoinjoin &&
coinjoinPreconditionSummary &&
!coinjoinPreconditionSummary.isFulfilled && (
<div className="mb-4">
<CoinjoinPreconditionViolationAlert
summary={coinjoinPreconditionSummary}
i18nPrefix="send.coinjoin_precondition."
/>
<>
<div className="d-flex align-items-center justify-content-center mb-2">
<div className="d-flex align-items-center justify-content-center alert alert-success rounded-circle p-3">
<Sprite symbol="checkmark" width="24" height="24" />
</div>
</div>
)}
<rb.Alert className="small slashed-zeroes break-word" variant={paymentSuccessfulInfoAlert.variant}>
{paymentSuccessfulInfoAlert.message}
</rb.Alert>
</>
)}
{!isLoading && walletInfo && (
<JarSelectorModal
isShown={destinationJarPickerShown}
Expand Down Expand Up @@ -692,12 +693,32 @@ export default function Send({ wallet }: SendProps) {
it.calculatedTotalBalanceInSats,
walletInfo.balanceSummary.calculatedTotalBalanceInSats
)}
variant={
it.accountIndex === sourceJarIndex &&
isCoinjoin &&
sourceJarCoinjoinPreconditionSummary?.isFulfilled === false
? 'warning'
: undefined
}
onClick={(jarIndex) => setSourceJarIndex(jarIndex)}
/>
))}
</div>
)}
</rb.Form.Group>

{!isLoading &&
!isOperationDisabled &&
isCoinjoin &&
sourceJarCoinjoinPreconditionSummary?.isFulfilled === false && (
<div className="mb-4">
<CoinjoinPreconditionViolationAlert
summary={sourceJarCoinjoinPreconditionSummary}
i18nPrefix="send.coinjoin_precondition."
/>
</div>
)}

<rb.Form.Group className="mb-4" controlId="destination">
<rb.Form.Label>{t('send.label_recipient')}</rb.Form.Label>
<div className="position-relative">
Expand Down
12 changes: 6 additions & 6 deletions src/components/jars/Jar.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
--bs-code-color: var(--bs-blue);
color: var(--bs-blue);
font-size: 0.75rem;
min-height: 1rem;
}

.selectableJarContainer {
Expand All @@ -77,21 +78,20 @@
cursor: none;
}

.selectableJarContainer > .selectionCircle {
height: 1.25rem;
width: 1.25rem;
margin-top: 0.8rem;
.selectableJarContainer .selectionCircle {
height: 1.5rem;
width: 1.5rem;

border-radius: 50%;
display: inline-block;
border: 1px solid var(--bs-body-color);
}

.selectableJarContainer:not(.selectable) > .selectionCircle {
.selectableJarContainer:not(.selectable) .selectionCircle {
visibility: hidden;
}

.selectableJarContainer.selected > .selectionCircle {
.selectableJarContainer.selected .selectionCircle {
visibility: visible !important;
background-color: var(--bs-body-color);
}
Expand Down
33 changes: 22 additions & 11 deletions src/components/jars/Jar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface JarProps {
interface SelectableJarProps {
isSelectable: boolean
isSelected: boolean
variant?: 'default' | 'warning'
onClick: (index: JarIndex) => void
}

Expand Down Expand Up @@ -127,16 +128,18 @@ const Jar = ({ index, balance, frozenBalance, fillLevel, isOpen = false }: JarPr
<div className={`${styles.jarBalance} jar-balance-container-hook`}>
<Balance valueString={balance.toString()} convertToUnit={settings.unit} showBalance={settings.showBalance} />
</div>
{frozenBalance && frozenBalance > 0 ? (
<div className={`${styles.jarBalance} ${styles.frozen} small jar-balance-container-hook`}>
<Sprite symbol="snowflake" width="14" height="14" />
<Balance
valueString={frozenBalance.toString()}
convertToUnit={settings.unit}
showBalance={settings.showBalance}
/>
</div>
) : null}
<div className={`${styles.jarBalance} ${styles.frozen} jar-balance-container-hook`}>
{frozenBalance && frozenBalance > 0 ? (
<>
<Sprite symbol="snowflake" width="14" height="14" />
<Balance
valueString={frozenBalance.toString()}
convertToUnit={settings.unit}
showBalance={settings.showBalance}
/>
</>
) : null}
</div>
</div>
</div>
)
Expand All @@ -153,6 +156,7 @@ const SelectableJar = ({
isSelectable,
isSelected,
onClick,
variant = 'default',
}: JarProps & SelectableJarProps) => {
return (
<div
Expand All @@ -163,7 +167,14 @@ const SelectableJar = ({
onClick={() => isSelectable && onClick(index)}
>
<Jar index={index} balance={balance} frozenBalance={frozenBalance} fillLevel={fillLevel} />
<div className={styles.selectionCircle}></div>
<div className="d-flex justify-content-center align-items-center gap-1 mt-2 position-relative">
<div className={styles.selectionCircle} />
{variant === 'warning' && (
<div className="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-warning text-dark p-0">
<Sprite symbol="warn" width="20" height="20" />
</div>
)}
</div>
</div>
)
}
Expand Down

0 comments on commit 77c3ec9

Please sign in to comment.