(Feature) - v3 Decoded Tx - Generic Modal (#2054)

Co-authored-by: Agustín Longoni <agustin.longoni@altoros.com>
Co-authored-by: Daniel Sanchez <daniel.sanchez@gnosis.pm>
This commit is contained in:
Fernando 2021-04-01 17:47:15 -03:00 committed by GitHub
parent 4e96152e21
commit 15ae933a18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 495 additions and 313 deletions

View File

@ -50,12 +50,6 @@ const notificationStyles = {
info: {
background: '#fff',
},
receiveModal: {
height: 'auto',
maxWidth: 'calc(100% - 30px)',
minHeight: '544px',
overflow: 'hidden',
},
}
const Frame = styled.div`
@ -149,7 +143,7 @@ const App: React.FC = ({ children }) => {
description="Receive Tokens Form"
handleClose={onReceiveHide}
open={safeActionsState.showReceive}
paperClassName={classes.receiveModal}
paperClassName="receive-modal"
title="Receive Tokens"
>
<ReceiveModal onClose={onReceiveHide} safeAddress={safeAddress} safeName={safeName} />

View File

@ -0,0 +1,174 @@
import { Text } from '@gnosis.pm/safe-react-components'
import React, { ReactElement, useState } from 'react'
import TextField from 'src/components/forms/TextField'
import GnoField from 'src/components/forms/Field'
import GnoForm from 'src/components/forms/GnoForm'
import { required } from 'src/components/forms/validator'
import { Modal } from '.'
export default {
title: 'Modal',
component: Modal,
parameters: {
children: 'The body of the modal or the whole modal being composed by `Modal.Header` and `Modal.Footer` components',
title: 'The title, useful for screen readers',
description: 'A description, useful for screen readers',
handleClose:
'A callback which will be called when an action to close the modal is triggered (Esc, clicking outside, etc)',
open: 'If `true`, the modal will be displayed. Hidden otherwise.',
},
compositionElements: [
{
title: 'Modal.Header',
component: <Modal.Header />,
parameters: {
title: 'The title that will be displayed in the modal',
titleNote: 'An annotation for the title, like "1 of 2"',
onClose: 'Callback to be called when attempt to close the modal',
},
compositionElements: [
{
title: 'Modal.Header.Title',
component: <Modal.Header.Title size="xs">{}</Modal.Header.Title>,
description: 'safe-react-component exposed with a few styles added to personalize the modal header',
},
],
},
{
title: 'Modal.Header',
component: <Modal.Body>{}</Modal.Body>,
parameters: {
children: 'whatever is required to be rendered in the footer. Usually buttons.',
noPadding: 'a flag that will set padding to 0 (zero) in case it is needed',
},
},
{
title: 'Modal.Footer',
component: <Modal.Footer>{}</Modal.Footer>,
parameters: {
children: 'whatever is required to be rendered in the footer. Usually buttons.',
},
compositionElements: [
{
title: 'Modal.Footer.Buttons',
component: <Modal.Footer.Buttons />,
description: 'standard two buttons wrapped implementation. One "Cancel" and one "Submit" button.',
},
],
},
],
}
const SimpleFormModal = ({ title, description, handleClose, handleSubmit, isOpen, children }) => (
<Modal title={title} description={description} handleClose={handleClose} open={isOpen}>
{/* header */}
<Modal.Header onClose={handleClose}>
<Modal.Header.Title>{title}</Modal.Header.Title>
</Modal.Header>
<GnoForm onSubmit={handleSubmit}>
{() => (
<>
{/* body */}
<Modal.Body>{children}</Modal.Body>
{/* footer */}
<Modal.Footer>
<Modal.Footer.Buttons cancelButtonProps={{ text: 'Close', onClick: handleClose }} />
</Modal.Footer>
</>
)}
</GnoForm>
</Modal>
)
const Username = () => (
<label htmlFor="username">
<Text size="lg" strong>
Username
</Text>
<GnoField
autoComplete="off"
component={TextField}
name="username"
id="username"
placeholder="your username"
validate={required}
/>
</label>
)
export const FormModal = (): ReactElement => {
const [isOpen, setIsOpen] = useState(false)
const handleClose = () => {
setIsOpen(false)
console.log('modal closed')
}
const handleSubmit = (values) => {
alert(JSON.stringify(values, null, 2))
console.log('form submitted', values)
handleClose()
}
return (
<div>
<button onClick={() => setIsOpen(true)}>Open Modal</button>
{/* Modal with Form */}
<SimpleFormModal
title="My first modal"
description="My first modal description"
handleClose={handleClose}
handleSubmit={handleSubmit}
isOpen={isOpen}
>
{/* Form Fields */}
<Username />
</SimpleFormModal>
</div>
)
}
export const RemoveSomething = (): ReactElement => {
const [isOpen, setIsOpen] = useState(false)
const title = 'Remove Something'
const handleClose = () => {
setIsOpen(false)
console.log('modal closed')
}
const handleSubmit = () => {
alert('Something was removed')
handleClose()
}
return (
<div>
<button onClick={() => setIsOpen(true)}>Open Modal</button>
{/* Modal */}
<Modal handleClose={handleClose} title={title} description={title} open={isOpen}>
{/* Header */}
<Modal.Header onClose={handleClose}>
<Modal.Header.Title>{title}</Modal.Header.Title>
</Modal.Header>
{/* Body */}
<Modal.Body>
<Text size="md">You are about to remove something</Text>
</Modal.Body>
{/* Footer */}
<Modal.Footer>
<Modal.Footer.Buttons
cancelButtonProps={{ onClick: handleClose }}
confirmButtonProps={{ onClick: handleSubmit, color: 'error', text: 'Remove' }}
/>
</Modal.Footer>
</Modal>
</div>
)
}

View File

@ -1,69 +1,267 @@
import Modal from '@material-ui/core/Modal'
import { makeStyles, createStyles } from '@material-ui/core/styles'
import { Button, Icon, theme, Title as TitleSRC } from '@gnosis.pm/safe-react-components'
import { ButtonProps as ButtonPropsMUI, Modal as ModalMUI } from '@material-ui/core'
import cn from 'classnames'
import React, { ReactElement, ReactNode } from 'react'
import React, { ReactElement, ReactNode, ReactNodeArray } from 'react'
import styled from 'styled-components'
import { sm } from 'src/theme/variables'
type Theme = typeof theme
const useStyles = makeStyles(
createStyles({
root: {
alignItems: 'center',
flexDirection: 'column',
display: 'flex',
overflowY: 'scroll',
},
paper: {
position: 'relative',
top: '68px',
width: '500px',
borderRadius: sm,
backgroundColor: '#ffffff',
boxShadow: '0 0 5px 0 rgba(74, 85, 121, 0.5)',
'&:focus': {
outline: 'none',
},
display: 'flex',
flexDirection: 'column',
},
}),
)
const ModalStyled = styled(ModalMUI)`
& {
align-items: center;
flex-direction: column;
display: flex;
overflow-y: scroll;
}
.overlay {
background-color: rgba(232, 231, 230, 0.75) !important;
}
.paper {
position: relative;
top: 68px;
width: 500px;
border-radius: 8px;
background-color: #ffffff;
box-shadow: 1px 2px 10px 0 rgba(40, 54, 61, 0.18);
display: flex;
flex-direction: column;
&:focus {
outline: none;
}
// TODO: replace class-based styles by params
&.receive-modal {
height: auto;
max-width: calc(100% - 130px);
min-height: 544px;
overflow: hidden;
}
&.bigger-modal-window {
width: 775px;
height: auto;
}
&.smaller-modal-window {
height: auto;
}
&.modal {
height: auto;
max-width: calc(100% - 130px);
}
}
`
interface GnoModalProps {
children: ReactNode
description: string
// type copied from Material-UI Modal's `close` prop
handleClose?: {
bivarianceHack(event: Record<string, unknown>, reason: 'backdropClick' | 'escapeKeyDown'): void
}['bivarianceHack']
modalClassName?: string
handleClose?: (event: Record<string, unknown>, reason: 'backdropClick' | 'escapeKeyDown') => void
open: boolean
paperClassName?: string
title: string
}
const GnoModal = ({
children,
description,
handleClose,
modalClassName,
open,
paperClassName,
title,
}: GnoModalProps): ReactElement => {
const classes = useStyles()
const GnoModal = ({ children, description, handleClose, open, paperClassName, title }: GnoModalProps): ReactElement => {
return (
<Modal
<ModalStyled
BackdropProps={{ className: 'overlay' }}
aria-describedby={description}
aria-labelledby={title}
className={cn(classes.root, modalClassName)}
onClose={handleClose}
open={open}
>
<div className={cn(classes.paper, paperClassName, 'classpep')}>{children}</div>
</Modal>
<div className={cn('paper', paperClassName)}>{children}</div>
</ModalStyled>
)
}
export default GnoModal
/*****************/
/* Generic Modal */
/*****************/
/*** Header ***/
const HeaderSection = styled.div`
display: flex;
padding: 24px 18px 24px 24px;
border-bottom: 2px solid ${({ theme }) => theme.colors.separator};
h5 {
color: ${({ theme }) => theme.colors.text};
}
.close-button {
align-self: flex-end;
background: none;
border: none;
padding: 5px;
width: 26px;
height: 26px;
span {
margin-right: 0;
}
:hover {
background: ${({ theme }) => theme.colors.separator};
border-radius: 16px;
cursor: pointer;
}
}
`
const TitleStyled = styled(TitleSRC)`
display: flex;
align-items: center;
flex-basis: 100%;
.image,
img {
width: 20px;
margin-right: 10px;
}
.note,
span {
margin-left: 12px;
}
`
interface TitleProps {
children: string | ReactNode
size?: keyof Theme['title']['size']
withoutMargin?: boolean
strong?: boolean
}
const Title = ({ children, ...props }: TitleProps): ReactElement => (
<TitleStyled size="xs" withoutMargin {...props}>
{children}
</TitleStyled>
)
interface HeaderProps {
children?: ReactNode
onClose?: (event: any) => void
}
const Header = ({ children, onClose }: HeaderProps): ReactElement => {
return (
<HeaderSection className="modal-header">
{children}
{onClose && (
<button className="close-button" onClick={onClose}>
<Icon size="sm" type="cross" />
</button>
)}
</HeaderSection>
)
}
Header.Title = Title
/*** Body ***/
const BodySection = styled.div<{ withoutPadding: BodyProps['withoutPadding'] }>`
padding: ${({ withoutPadding }) => (withoutPadding ? 0 : '24px')};
`
interface BodyProps {
children: ReactNode | ReactNodeArray
withoutPadding?: boolean
}
const Body = ({ children, withoutPadding = false }: BodyProps): ReactElement => (
<BodySection className="modal-body" withoutPadding={withoutPadding}>
{children}
</BodySection>
)
/*** Footer ***/
const FooterSection = styled.div`
display: flex;
justify-content: center;
border-top: 2px solid ${({ theme }) => theme.colors.separator};
padding: 24px;
`
const ButtonStyled = styled(Button)`
&.MuiButtonBase-root {
margin: 0 10px;
}
`
type CustomButtonMUIProps = Omit<ButtonPropsMUI, 'size' | 'color' | 'variant'> & {
to?: string
component?: ReactNode
}
interface ButtonProps extends CustomButtonMUIProps {
text?: string
size?: keyof Theme['buttons']['size']
color?: 'primary' | 'secondary' | 'error'
variant?: 'bordered' | 'contained' | 'outlined'
}
interface ButtonsProps {
cancelButtonProps?: ButtonProps
confirmButtonProps?: ButtonProps
}
const Buttons = ({ cancelButtonProps = {}, confirmButtonProps = {} }: ButtonsProps): ReactElement => {
const { text: cancelText = 'Cancel' } = cancelButtonProps
const { text: confirmText = 'Submit' } = confirmButtonProps
return (
<>
<ButtonStyled
size="md"
color="primary"
variant="outlined"
type={cancelButtonProps?.onClick ? 'button' : 'submit'}
{...cancelButtonProps}
>
{cancelText}
</ButtonStyled>
<ButtonStyled size="md" type={confirmButtonProps?.onClick ? 'button' : 'submit'} {...confirmButtonProps}>
{confirmText}
</ButtonStyled>
</>
)
}
interface FooterProps {
children: ReactNode | ReactNodeArray
}
const Footer = ({ children }: FooterProps): ReactElement => (
<FooterSection className="modal-footer">{children}</FooterSection>
)
Footer.Buttons = Buttons
interface ModalProps {
children: ReactNode
description: string
handleClose: () => void
open: boolean
title: string
}
export const Modal = ({ children, ...props }: ModalProps): ReactElement => {
return (
<GnoModal {...props} paperClassName="modal">
{children}
</GnoModal>
)
}
Modal.Header = Header
Modal.Body = Body
Modal.Footer = Footer

View File

@ -67,7 +67,7 @@ export const CreateEditEntryModal = ({
description={isNew ? 'Create new addressBook entry' : 'Edit addressBook entry'}
handleClose={onClose}
open={isOpen}
paperClassName={classes.smallerModalWindow}
paperClassName="smaller-modal-window"
title={isNew ? 'Create new entry' : 'Edit entry'}
>
<Row align="center" className={classes.heading} grow>

View File

@ -23,8 +23,5 @@ export const useStyles = makeStyles(
height: '84px',
justifyContent: 'center',
},
smallerModalWindow: {
height: 'auto',
},
}),
)

View File

@ -25,7 +25,7 @@ const DeleteEntryModalComponent = ({ classes, deleteEntryModalHandler, entryToDe
description="Delete entry"
handleClose={onClose}
open={isOpen}
paperClassName={classes.smallerModalWindow}
paperClassName="smaller-modal-window"
title="Delete entry"
>
<Row align="center" className={classes.heading} grow>

View File

@ -27,7 +27,4 @@ export const styles = () => ({
buttonCancel: {
color: '#008c73',
},
smallerModalWindow: {
height: 'auto',
},
})

View File

@ -1,6 +1,5 @@
import CircularProgress from '@material-ui/core/CircularProgress'
import { makeStyles } from '@material-ui/core/styles'
import cn from 'classnames'
import React, { Suspense, useEffect, useState } from 'react'
import Modal from 'src/components/Modal'
@ -32,12 +31,6 @@ const SendCustomTx = React.lazy(() => import('./screens/ContractInteraction/Send
const ReviewCustomTx = React.lazy(() => import('./screens/ContractInteraction/ReviewCustomTx'))
const useStyles = makeStyles({
scalableModalWindow: {
height: 'auto',
},
scalableStaticModalWindow: {
height: 'auto',
},
loaderStyle: {
height: '500px',
width: '100%',
@ -86,8 +79,6 @@ const SendModal = ({
setTx({})
}, [activeScreenType, isOpen])
const scalableModalSize = activeScreen === 'chooseTxType'
const handleTxCreation = (txInfo: SendCollectibleTxInfo) => {
setActiveScreen('sendFundsReviewTx')
setTx(txInfo)
@ -117,7 +108,7 @@ const SendModal = ({
description="Send Tokens Form"
handleClose={onClose}
open={isOpen}
paperClassName={cn(scalableModalSize ? classes.scalableStaticModalWindow : classes.scalableModalWindow)}
paperClassName="smaller-modal-window"
title="Send Tokens"
>
<Suspense

View File

@ -92,7 +92,7 @@ const Balances = (): React.ReactElement => {
}))
}
const { assetDivider, assetTab, assetTabActive, assetTabs, controls, receiveModal, tokenControls } = classes
const { assetDivider, assetTab, assetTabActive, assetTabs, controls, tokenControls } = classes
const { erc721Enabled, sendFunds, showReceive } = state
return (
@ -174,7 +174,7 @@ const Balances = (): React.ReactElement => {
description="Receive Tokens Form"
handleClose={() => onHide('Receive')}
open={showReceive}
paperClassName={receiveModal}
paperClassName="receive-modal"
title="Receive Tokens"
>
<ReceiveModal safeAddress={address} safeName={safeName} onClose={() => onHide('Receive')} />

View File

@ -58,12 +58,6 @@ export const styles = createStyles({
marginLeft: '0',
},
},
receiveModal: {
height: 'auto',
maxWidth: 'calc(100% - 30px)',
minHeight: '544px',
overflow: 'hidden',
},
send: {
width: '75px',
minWidth: '75px',

View File

@ -1,3 +1,4 @@
// TODO: remove this file. It's no longer used
import { screenSm, sm } from 'src/theme/variables'
import { createStyles } from '@material-ui/core'

View File

@ -122,7 +122,7 @@ export const RemoveModuleModal = ({ onClose, selectedModulePair }: RemoveModuleM
<Modal
description="Remove the selected Module"
handleClose={onClose}
paperClassName={classes.modal}
paperClassName="modal"
title="Remove Module"
open
>

View File

@ -101,11 +101,6 @@ export const styles = createStyles({
cursor: 'pointer',
},
},
modal: {
height: 'auto',
maxWidth: 'calc(100% - 30px)',
overflow: 'hidden',
},
gasCostsContainer: {
backgroundColor: background,
padding: `0 ${lg}`,

View File

@ -1,4 +1,3 @@
import { createStyles, makeStyles } from '@material-ui/core/styles'
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
@ -18,15 +17,6 @@ import { OwnerForm } from './screens/OwnerForm'
import { ReviewAddOwner } from './screens/Review'
import { ThresholdForm } from './screens/ThresholdForm'
const styles = createStyles({
biggerModalWindow: {
width: '775px',
height: 'auto',
},
})
const useStyles = makeStyles(styles)
export type OwnerValues = {
ownerAddress: string
ownerName: string
@ -66,7 +56,6 @@ type Props = {
}
export const AddOwnerModal = ({ isOpen, onClose }: Props): React.ReactElement => {
const classes = useStyles()
const [activeScreen, setActiveScreen] = useState('selectOwner')
const [values, setValues] = useState<OwnerValues>({ ownerName: '', ownerAddress: '', threshold: '' })
const dispatch = useDispatch()
@ -123,7 +112,7 @@ export const AddOwnerModal = ({ isOpen, onClose }: Props): React.ReactElement =>
description="Add owner to Safe"
handleClose={onClose}
open={isOpen}
paperClassName={classes.biggerModalWindow}
paperClassName="bigger-modal-window"
title="Add owner to Safe"
>
<>

View File

@ -60,7 +60,7 @@ export const EditOwnerModal = ({ isOpen, onClose, ownerAddress, selectedOwnerNam
description="Edit owner from Safe"
handleClose={onClose}
open={isOpen}
paperClassName={classes.smallerModalWindow}
paperClassName="smaller-modal-window"
title="Edit owner from Safe"
>
<Row align="center" className={classes.heading} grow>

View File

@ -32,7 +32,4 @@ export const styles = createStyles({
cursor: 'pointer',
},
},
smallerModalWindow: {
height: 'auto',
},
})

View File

@ -1,4 +1,3 @@
import { createStyles, makeStyles } from '@material-ui/core/styles'
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
@ -15,15 +14,6 @@ import { safeParamAddressFromStateSelector, safeThresholdSelector } from 'src/lo
import { Dispatch } from 'src/logic/safe/store/actions/types.d'
import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters'
const styles = createStyles({
biggerModalWindow: {
width: '775px',
height: 'auto',
},
})
const useStyles = makeStyles(styles)
type OwnerValues = {
ownerAddress: string
ownerName: string
@ -78,7 +68,6 @@ export const RemoveOwnerModal = ({
ownerAddress,
ownerName,
}: RemoveOwnerProps): React.ReactElement => {
const classes = useStyles()
const [activeScreen, setActiveScreen] = useState('checkOwner')
const [values, setValues] = useState<OwnerValues>({ ownerAddress, ownerName, threshold: '' })
const dispatch = useDispatch()
@ -120,7 +109,7 @@ export const RemoveOwnerModal = ({
description="Remove owner from Safe"
handleClose={onClose}
open={isOpen}
paperClassName={classes.biggerModalWindow}
paperClassName="bigger-modal-window"
title="Remove owner from Safe"
>
<>

View File

@ -1,4 +1,3 @@
import { createStyles, makeStyles } from '@material-ui/core/styles'
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
@ -18,15 +17,6 @@ import { OwnerForm } from 'src/routes/safe/components/Settings/ManageOwners/Repl
import { ReviewReplaceOwnerModal } from 'src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review'
import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters'
const styles = createStyles({
biggerModalWindow: {
width: '775px',
height: 'auto',
},
})
const useStyles = makeStyles(styles)
type OwnerValues = {
newOwnerAddress: string
newOwnerName: string
@ -84,7 +74,6 @@ export const ReplaceOwnerModal = ({
ownerAddress,
ownerName,
}: ReplaceOwnerProps): React.ReactElement => {
const classes = useStyles()
const [activeScreen, setActiveScreen] = useState('checkOwner')
const [values, setValues] = useState({
newOwnerAddress: '',
@ -137,7 +126,7 @@ export const ReplaceOwnerModal = ({
description="Replace owner from Safe"
handleClose={onClose}
open={isOpen}
paperClassName={classes.biggerModalWindow}
paperClassName="bigger-modal-window"
title="Replace owner from Safe"
>
<>

View File

@ -68,7 +68,7 @@ export const RemoveSafeModal = ({ isOpen, onClose }: RemoveSafeModalProps): Reac
description="Remove the selected Safe"
handleClose={onClose}
open={isOpen}
paperClassName={classes.modal}
paperClassName="modal"
title="Remove Safe"
>
<Row align="center" className={classes.heading} grow>

View File

@ -52,9 +52,4 @@ export const styles = createStyles({
cursor: 'pointer',
},
},
modal: {
height: 'auto',
maxWidth: 'calc(100% - 30px)',
overflow: 'hidden',
},
})

View File

@ -1,102 +0,0 @@
import { Icon, Text, Title } from '@gnosis.pm/safe-react-components'
import React, { ReactElement, ReactNode, ReactNodeArray } from 'react'
import styled from 'styled-components'
import GnoModal from 'src/components/Modal'
import { useStyles } from 'src/routes/safe/components/Settings/SpendingLimit/style'
const TitleSection = styled.div`
display: flex;
justify-content: space-between;
padding: 16px 24px;
border-bottom: 2px solid ${({ theme }) => theme.colors.separator};
`
const StyledButton = styled.button`
background: none;
border: none;
padding: 5px;
width: 26px;
height: 26px;
span {
margin-right: 0;
}
:hover {
background: ${({ theme }) => theme.colors.separator};
border-radius: 16px;
cursor: pointer;
}
`
const FooterSection = styled.div`
border-top: 2px solid ${({ theme }) => theme.colors.separator};
padding: 16px 24px;
`
const FooterWrapper = styled.div`
display: flex;
justify-content: space-around;
`
export interface TopBarProps {
title: string
titleNote?: string
onClose: () => void
}
const TopBar = ({ title, titleNote, onClose }: TopBarProps): ReactElement => (
<TitleSection>
<Title size="xs" withoutMargin>
{title}
{titleNote && (
<>
{' '}
<Text size="lg" color="secondaryLight" as="span">
{titleNote}
</Text>
</>
)}
</Title>
<StyledButton onClick={onClose}>
<Icon size="sm" type="cross" />
</StyledButton>
</TitleSection>
)
interface FooterProps {
children: ReactNodeArray
}
const Footer = ({ children }: FooterProps): ReactElement => (
<FooterSection>
<FooterWrapper>{children}</FooterWrapper>
</FooterSection>
)
export interface ModalProps {
children: ReactNode
description: string
handleClose: () => void
open: boolean
title: string
}
// TODO: this is a potential proposal for `safe-react-components` Modal
// By being able to combine components for better flexibility, this way Buttons can be part of the form body
const Modal = ({ children, ...props }: ModalProps): ReactElement => {
const classes = useStyles()
return (
<GnoModal {...props} paperClassName={classes.modal}>
{children}
</GnoModal>
)
}
Modal.TopBar = TopBar
Modal.Footer = Footer
export default Modal

View File

@ -1,12 +1,11 @@
import { Button } from '@gnosis.pm/safe-react-components'
import { Text } from '@gnosis.pm/safe-react-components'
import { FormState, Mutator } from 'final-form'
import React, { ReactElement } from 'react'
import styled from 'styled-components'
import GnoForm from 'src/components/forms/GnoForm'
import GnoButton from 'src/components/layout/Button'
import { Modal } from 'src/components/Modal'
import { Amount, Beneficiary, ResetTime, Token } from 'src/routes/safe/components/Settings/SpendingLimit/FormFields'
import Modal from 'src/routes/safe/components/Settings/SpendingLimit/Modal'
const FormContainer = styled.div`
padding: 24px 8px 24px 24px;
@ -24,14 +23,6 @@ const FormContainer = styled.div`
'resetTimeOption resetTimeOption';
`
const YetAnotherButton = styled(GnoButton)`
&.Mui-disabled {
background-color: ${({ theme }) => theme.colors.primary};
color: ${({ theme }) => theme.colors.white};
opacity: 0.5;
}
`
const formMutators: Record<string, Mutator<{ beneficiary: { name: string } }>> = {
setBeneficiary: (args, state, utils) => {
utils.changeValue(state, 'beneficiary', () => args[0])
@ -55,7 +46,16 @@ const canReview = ({
const Create = ({ initialValues, onCancel, onReview }: NewSpendingLimitProps): ReactElement => {
return (
<>
<Modal.TopBar title="New Spending Limit" titleNote="1 of 2" onClose={onCancel} />
<Modal.Header onClose={onCancel}>
<Modal.Header.Title size="xs" withoutMargin>
<>
New Spending Limit
<Text size="lg" color="secondaryLight" as="span">
1 of 2
</Text>
</>
</Modal.Header.Title>
</Modal.Header>
<GnoForm formMutators={formMutators} onSubmit={onReview} initialValues={initialValues}>
{(...args) => {
@ -69,21 +69,10 @@ const Create = ({ initialValues, onCancel, onReview }: NewSpendingLimitProps): R
</FormContainer>
<Modal.Footer>
<Button color="primary" size="md" onClick={onCancel}>
Cancel
</Button>
{/* TODO: replace this with safe-react-components button. */}
{/* This is used as "submit" SRC Button does not triggers submission up until the 2nd click */}
<YetAnotherButton
color="primary"
size="medium"
variant="contained"
type="submit"
disabled={!canReview(args[2])}
>
Review
</YetAnotherButton>
<Modal.Footer.Buttons
cancelButtonProps={{ onClick: onCancel }}
confirmButtonProps={{ disabled: !canReview(args[2]), text: 'Review' }}
/>
</Modal.Footer>
</>
)

View File

@ -1,10 +1,10 @@
import { Button, Text } from '@gnosis.pm/safe-react-components'
import { Text } from '@gnosis.pm/safe-react-components'
import React, { ReactElement, useEffect, useMemo, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import Block from 'src/components/layout/Block'
import Col from 'src/components/layout/Col'
import Row from 'src/components/layout/Row'
import { Modal } from 'src/components/Modal'
import { createTransaction, CreateTransactionArgs } from 'src/logic/safe/store/actions/createTransaction'
import { SafeRecordProps, SpendingLimit } from 'src/logic/safe/store/models/safe'
import {
@ -22,7 +22,6 @@ import { fromTokenUnit, toTokenUnit } from 'src/logic/tokens/utils/humanReadable
import { sameAddress } from 'src/logic/wallets/ethAddresses'
import { RESET_TIME_OPTIONS } from 'src/routes/safe/components/Settings/SpendingLimit/FormFields/ResetTime'
import { AddressInfo, ResetTimeInfo, TokenInfo } from 'src/routes/safe/components/Settings/SpendingLimit/InfoDisplay'
import Modal from 'src/routes/safe/components/Settings/SpendingLimit/Modal'
import { useStyles } from 'src/routes/safe/components/Settings/SpendingLimit/style'
import { safeParamAddressFromStateSelector, safeSpendingLimitsSelector } from 'src/logic/safe/store/selectors'
import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters'
@ -242,9 +241,16 @@ export const ReviewSpendingLimits = ({ onBack, onClose, txToken, values }: Revie
>
{(txParameters, toggleEditMode) => (
<>
<Modal.TopBar title="New Spending Limit" titleNote="2 of 2" onClose={onClose} />
<Modal.Header onClose={onClose}>
<Modal.Header.Title size="xs" withoutMargin>
New Spending Limit
<Text size="lg" color="secondaryLight" as="span">
2 of 2
</Text>
</Modal.Header.Title>
</Modal.Header>
<Block className={classes.container}>
<Modal.Body>
<Col margin="lg">
<AddressInfo address={values.beneficiary} title="Beneficiary" />
</Col>
@ -286,7 +292,7 @@ export const ReviewSpendingLimits = ({ onBack, onClose, txToken, values }: Revie
isTransactionExecution={isExecution}
isOffChainSignature={isOffChainSignature}
/>
</Block>
</Modal.Body>
<div className={classes.gasCostsContainer}>
<TransactionFees
gasCostFormatted={gasCostFormatted}
@ -298,23 +304,17 @@ export const ReviewSpendingLimits = ({ onBack, onClose, txToken, values }: Revie
</div>
<Modal.Footer>
<Button
color="primary"
size="md"
onClick={() => onBack({ values: {}, txToken: makeToken(), step: CREATE })}
>
Back
</Button>
<Button
color="primary"
size="md"
variant="contained"
onClick={() => handleSubmit(txParameters)}
disabled={existentSpendingLimit === undefined || txEstimationExecutionStatus === EstimationStatus.LOADING}
>
Submit
</Button>
<Modal.Footer.Buttons
cancelButtonProps={{
onClick: () => onBack({ values: {}, txToken: makeToken(), step: CREATE }),
text: 'Back',
}}
confirmButtonProps={{
onClick: () => handleSubmit(txParameters),
disabled:
existentSpendingLimit === undefined || txEstimationExecutionStatus === EstimationStatus.LOADING,
}}
/>
</Modal.Footer>
</>
)}

View File

@ -2,10 +2,10 @@ import { List } from 'immutable'
import React, { ReactElement, Reducer, useCallback, useReducer } from 'react'
import { useSelector } from 'react-redux'
import { Modal } from 'src/components/Modal'
import { makeToken, Token } from 'src/logic/tokens/store/model/token'
import { sameAddress } from 'src/logic/wallets/ethAddresses'
import { extendedSafeTokensSelector } from 'src/routes/safe/container/selector'
import Modal from 'src/routes/safe/components/Settings/SpendingLimit/Modal'
import Create from './Create'
import { ReviewSpendingLimits } from './Review'

View File

@ -1,29 +1,27 @@
import { Button } from '@gnosis.pm/safe-react-components'
import cn from 'classnames'
import React, { ReactElement, useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import Block from 'src/components/layout/Block'
import Col from 'src/components/layout/Col'
import Row from 'src/components/layout/Row'
import { Modal } from 'src/components/Modal'
import { TransactionFees } from 'src/components/TransactionsFees'
import { EstimationStatus, useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas'
import useTokenInfo from 'src/logic/safe/hooks/useTokenInfo'
import { createTransaction } from 'src/logic/safe/store/actions/createTransaction'
import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
import { getDeleteAllowanceTxData } from 'src/logic/safe/utils/spendingLimits'
import { fromTokenUnit } from 'src/logic/tokens/utils/humanReadableValue'
import { EditableTxParameters } from 'src/routes/safe/components/Transactions/helpers/EditableTxParameters'
import { TxParametersDetail } from 'src/routes/safe/components/Transactions/helpers/TxParametersDetail'
import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters'
import { SPENDING_LIMIT_MODULE_ADDRESS } from 'src/utils/constants'
import { RESET_TIME_OPTIONS } from './FormFields/ResetTime'
import { AddressInfo, ResetTimeInfo, TokenInfo } from './InfoDisplay'
import { SpendingLimitTable } from './LimitsTable/dataFetcher'
import Modal from './Modal'
import { useStyles } from './style'
import { EstimationStatus, useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas'
import { EditableTxParameters } from 'src/routes/safe/components/Transactions/helpers/EditableTxParameters'
import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters'
import { TxParametersDetail } from 'src/routes/safe/components/Transactions/helpers/TxParametersDetail'
import Row from 'src/components/layout/Row'
import { TransactionFees } from 'src/components/TransactionsFees'
import cn from 'classnames'
interface RemoveSpendingLimitModalProps {
onClose: () => void
@ -126,9 +124,13 @@ export const RemoveLimitModal = ({ onClose, spendingLimit, open }: RemoveSpendin
{(txParameters, toggleEditMode) => {
return (
<>
<Modal.TopBar title="Remove Spending Limit" onClose={onClose} />
<Modal.Header onClose={onClose}>
<Modal.Header.Title size="xs" withoutMargin>
Remove Spending Limit
</Modal.Header.Title>
</Modal.Header>
<Block className={classes.container}>
<Modal.Body>
<Col margin="lg">
<AddressInfo title="Beneficiary" address={spendingLimit.beneficiary} />
</Col>
@ -152,7 +154,7 @@ export const RemoveLimitModal = ({ onClose, spendingLimit, open }: RemoveSpendin
isTransactionExecution={isExecution}
isOffChainSignature={isOffChainSignature}
/>
</Block>
</Modal.Body>
<Row className={cn(classes.modalDescription, classes.gasCostsContainer)}>
<TransactionFees
@ -165,18 +167,15 @@ export const RemoveLimitModal = ({ onClose, spendingLimit, open }: RemoveSpendin
</Row>
<Modal.Footer>
<Button size="md" color="secondary" onClick={onClose}>
Cancel
</Button>
<Button
color="error"
size="md"
variant="contained"
onClick={() => removeSelectedSpendingLimit(txParameters)}
disabled={txEstimationExecutionStatus === EstimationStatus.LOADING}
>
Remove
</Button>
<Modal.Footer.Buttons
cancelButtonProps={{ onClick: onClose }}
confirmButtonProps={{
color: 'error',
onClick: () => removeSelectedSpendingLimit(txParameters),
disabled: txEstimationExecutionStatus === EstimationStatus.LOADING,
text: 'Remove',
}}
/>
</Modal.Footer>
</>
)

View File

@ -120,10 +120,6 @@ export const useStyles = makeStyles(
cursor: 'pointer',
},
},
modal: {
height: 'auto',
maxWidth: 'calc(100% - 30px)',
},
amountInput: {
width: '100% !important',
},