diff --git a/jestSetup.js b/jestSetup.js
index ff0869d2d54..9aa10d2717d 100644
--- a/jestSetup.js
+++ b/jestSetup.js
@@ -1,3 +1,5 @@
+import './node_modules/react-native-gesture-handler/jestSetup.js'
+
import { jest } from '@jest/globals'
import mockSafeAreaContext from 'react-native-safe-area-context/jest/mock'
@@ -46,12 +48,6 @@ jest.mock('@react-navigation/elements', () => ({
getDefaultHeaderHeight: () => 44
}))
-jest.mock('react-native-gesture-handler', () => ({
- PanGestureHandler({ children }) {
- return children
- }
-}))
-
jest.mock('rn-qr-generator', () => ({
detect() {
return Promise.resolve()
diff --git a/src/__tests__/modals/__snapshots__/AccelerateTxModal.test.tsx.snap b/src/__tests__/modals/__snapshots__/AccelerateTxModal.test.tsx.snap
index b4399674cd3..b54f8101607 100644
--- a/src/__tests__/modals/__snapshots__/AccelerateTxModal.test.tsx.snap
+++ b/src/__tests__/modals/__snapshots__/AccelerateTxModal.test.tsx.snap
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AccelerateTxModalComponent should render with loading props 1`] = `
-
-
+
`;
diff --git a/src/__tests__/modals/__snapshots__/AddressModal.test.tsx.snap b/src/__tests__/modals/__snapshots__/AddressModal.test.tsx.snap
index 7b234322731..42b9fa44851 100644
--- a/src/__tests__/modals/__snapshots__/AddressModal.test.tsx.snap
+++ b/src/__tests__/modals/__snapshots__/AddressModal.test.tsx.snap
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AddressModalComponent should render with loaded props 1`] = `
-
-
+
`;
diff --git a/src/__tests__/modals/__snapshots__/AutoLogoutModal.test.tsx.snap b/src/__tests__/modals/__snapshots__/AutoLogoutModal.test.tsx.snap
index 5403c445ee4..56cd5560983 100644
--- a/src/__tests__/modals/__snapshots__/AutoLogoutModal.test.tsx.snap
+++ b/src/__tests__/modals/__snapshots__/AutoLogoutModal.test.tsx.snap
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AutoLogoutModal should render with loading props 1`] = `
-
-
+
`;
diff --git a/src/__tests__/modals/__snapshots__/CategoryModal.test.tsx.snap b/src/__tests__/modals/__snapshots__/CategoryModal.test.tsx.snap
index b026c839963..d737c15bbb0 100644
--- a/src/__tests__/modals/__snapshots__/CategoryModal.test.tsx.snap
+++ b/src/__tests__/modals/__snapshots__/CategoryModal.test.tsx.snap
@@ -13,7 +13,6 @@ exports[`CategoryModal should render with a subcategory 1`] = `
}
}
accessible={true}
- collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
@@ -23,320 +22,424 @@ exports[`CategoryModal should render with a subcategory 1`] = `
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
- {
- "backgroundColor": "transparent",
- "bottom": 0,
- "left": 0,
- "opacity": 0,
- "position": "absolute",
- "right": 0,
- "top": 0,
- }
- }
- >
-
- ,
+ },
+ {
+ "opacity": 0,
+ },
+ ]
+ }
+ />,
-
- Choose a Category
-
+ />
+
+
+
- Income
+ Choose a Category
-
- Expense
-
-
-
+ Income
+
+
+
-
+
+ Expense
+
+
+
- Transfer
-
+
+ Transfer
+
+
+
+
+ Exchange
+
+
-
- Exchange
-
-
-
-
-
-
+ />
-
+
+ Sub-category
+
+
+
- Sub-category
-
+ testID="undefined.textInput"
+ textAlignVertical="top"
+ value="Paycheck"
+ />
-
-
-
-
-
-
-
+
+
+
+
-
-
+
+
+
+
+ Income:Paycheck
+
+
+
+
+ +
+
+
+
+
+
+
+
-
+
+
+
+ Exchange:Paycheck
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+ Expense:Paycheck
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ >
+
+
+
+ Transfer:Paycheck
+
+
+
+
+ +
+
+
+
+
+
-
-
-
-
- ,
+ },
+ {
+ "opacity": 0,
+ },
+ ]
+ }
+ />,
-
- Choose a Category
-
+ />
+
+
+
- Income
+ Choose a Category
-
- Expense
-
-
-
+ Income
+
+
+
-
+
+ Expense
+
+
+
- Transfer
-
+
+ Transfer
+
+
+
+
+ Exchange
+
+
-
- Exchange
-
-
-
-
-
-
+
+
+
-
+ Sub-category
+
+
+
- Sub-category
-
+ testID="undefined.textInput"
+ textAlignVertical="top"
+ value=""
+ />
-
-
-
-
-
-
-
+
+
+
+
-
-
+
+
+
+
+ Exchange:Buy Bitcoin
+
+
+
+
+
+
+
+
+
+
+
+ Exchange:Sell Bitcoin
+
+
+
+
+
+
+
+
+
+
+
+ Expense:Air Travel
+
+
+
+
+
+
+
-
+ >
+
+
+
+ Expense:Alcohol & Bars
+
+
+
+
+
-
-
-
-
- ,
+ },
+ {
+ "opacity": 0,
+ },
+ ]
+ }
+ />,
-
-
+ />
+
+
+
+
+
+
+
+
-
+ Thanks for using Edge!
+
+
+
+
+
+
+ }
+ >
- Thanks for using Edge!
+
-
-
-
-
-
-
-
-
-
+
-
- Knowledge Base
-
-
- Commonly asked questions and FAQ
-
-
+ Commonly asked questions and FAQ
+
-
+
+
+
+ >
+
+
+
+
-
-
-
-
-
-
+
-
- Submit a Support Ticket
-
-
- Troubleshooting and technical support
-
-
+ Troubleshooting and technical support
+
-
+
+
+
+ >
+
+
+
+
-
-
-
-
-
-
+
-
- Call for Assistance
-
-
- Get in touch by phone
-
-
+ Get in touch by phone
+
-
+
+
+
+ >
+
+
+
+
-
-
-
-
-
-
+
-
- Visit the Edge site
-
-
- More info on Edge
-
-
+ More info on Edge
+
-
+
+
+
+ >
+
+
+
+
-
-
-
-
-
-
+
-
- Terms of Service
-
-
- Understand your obligations and risks associated with our platform
-
-
+ Understand your obligations and risks associated with our platform
+
-
-
- Version 1.2.3
-
-
- Build 2019010101
-
-
-
-
-
+
+ Version 1.2.3
+
+
+ >
+ Build 2019010101
+
+
-
+
-
+
`;
diff --git a/src/__tests__/scenes/__snapshots__/CryptoExchangeQuoteScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/CryptoExchangeQuoteScene.test.tsx.snap
index c2e9a18dd45..6c9b48fdd66 100644
--- a/src/__tests__/scenes/__snapshots__/CryptoExchangeQuoteScene.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/CryptoExchangeQuoteScene.test.tsx.snap
@@ -1704,6 +1704,11 @@ exports[`CryptoExchangeQuoteScreenComponent should render with loading props 1`]
}
/>
{
const styles = getStyles(theme)
return (
- bridge.resolve(undefined)} paddingRem={[1, 0]}>
+ bridge.resolve(undefined)} paddingRem={[1, 0]}>
{lstrings.fio_address_choose_domain_label}
@@ -169,16 +169,16 @@ class DomainListModalComponent extends React.Component {
value={input}
iconComponent={SearchIconAnimated}
/>
-
-
-
+ {/* */}
+
)
}
}
diff --git a/src/components/modals/AccelerateTxModal.tsx b/src/components/modals/AccelerateTxModal.tsx
index 3c1b8d05046..46d0bca2686 100644
--- a/src/components/modals/AccelerateTxModal.tsx
+++ b/src/components/modals/AccelerateTxModal.tsx
@@ -13,7 +13,7 @@ import { WarningCard } from '../cards/WarningCard'
import { cacheStyles, Theme, ThemeProps, withTheme } from '../services/ThemeContext'
import { ModalMessage, ModalTitle } from '../themed/ModalParts'
import { Slider } from '../themed/Slider'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
import { RowUi4 } from '../ui4/RowUi4'
interface OwnProps {
@@ -111,7 +111,7 @@ export class AccelerateTxModalComponent extends PureComponent {
const isSending = status === 'sending'
return (
-
+
{lstrings.transaction_details_accelerate_transaction_header}
{lstrings.transaction_details_accelerate_transaction_instructional}
@@ -140,7 +140,7 @@ export class AccelerateTxModalComponent extends PureComponent {
disabledText={lstrings.transaction_details_accelerate_transaction_slider_disabled}
/>
-
+
)
}
}
diff --git a/src/components/modals/AddressModal.tsx b/src/components/modals/AddressModal.tsx
index 15ec36ff6c0..7c1d0b4caa6 100644
--- a/src/components/modals/AddressModal.tsx
+++ b/src/components/modals/AddressModal.tsx
@@ -20,7 +20,7 @@ import { cacheStyles, Theme, ThemeProps, withTheme } from '../services/ThemeCont
import { FilledTextInput } from '../themed/FilledTextInput'
import { MainButton } from '../themed/MainButton'
import { ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface OwnProps {
bridge: AirshipBridge
@@ -304,7 +304,7 @@ export class AddressModalComponent extends React.Component {
const styles = getStyles(theme)
return (
-
+
{title || lstrings.address_modal_default_header}
@@ -338,7 +338,7 @@ export class AddressModalComponent extends React.Component {
{/* TODO: Style ButtonsViewUi4 for Modals */}
-
+
)
}
}
diff --git a/src/components/modals/AutoLogoutModal.tsx b/src/components/modals/AutoLogoutModal.tsx
index f40644c998e..796bbefd2aa 100644
--- a/src/components/modals/AutoLogoutModal.tsx
+++ b/src/components/modals/AutoLogoutModal.tsx
@@ -10,8 +10,8 @@ import { DisplayTime, displayToSeconds, secondsToDisplay } from '../../util/disp
import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'
import { EdgeText } from '../themed/EdgeText'
import { ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
import { ButtonsViewUi4 } from '../ui4/ButtonsViewUi4'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface Props {
bridge: AirshipBridge
@@ -76,7 +76,7 @@ export const AutoLogoutModal = (props: Props) => {
}, [])
return (
-
+
{lstrings.dialog_title}
{isAndroid ? (
@@ -116,7 +116,7 @@ export const AutoLogoutModal = (props: Props) => {
)}
-
+
)
}
diff --git a/src/components/modals/ButtonsModal.tsx b/src/components/modals/ButtonsModal.tsx
index ff122d4def9..09078fe496a 100644
--- a/src/components/modals/ButtonsModal.tsx
+++ b/src/components/modals/ButtonsModal.tsx
@@ -2,11 +2,12 @@ import * as React from 'react'
import { View, ViewStyle } from 'react-native'
import { AirshipBridge } from 'react-native-airship'
+import { useHandler } from '../../hooks/useHandler'
import { showError } from '../services/AirshipInstance'
import { useTheme } from '../services/ThemeContext'
import { MainButton } from '../themed/MainButton'
import { ModalMessage, ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
export interface ButtonInfo {
label: string
@@ -26,12 +27,15 @@ export interface ButtonModalProps {
message?: string
children?: React.ReactNode
buttons: Buttons
- closeArrow?: boolean
disableCancel?: boolean
fullScreen?: boolean
// Adds a border:
warning?: boolean
+
+ /** @deprecated. Does nothing. */
+ // eslint-disable-next-line react/no-unused-prop-types
+ closeArrow?: boolean
}
/**
@@ -46,10 +50,10 @@ export interface ButtonModalProps {
* or other interactive elements.
*/
export function ButtonsModal(props: ButtonModalProps) {
- const { bridge, title, message, children, buttons, closeArrow = false, disableCancel = false, fullScreen = false, warning } = props
+ const { bridge, title, message, children, buttons, disableCancel = false, fullScreen = false, warning } = props
const theme = useTheme()
- const handleCancel = disableCancel ? () => {} : () => bridge.resolve(undefined)
+ const handleCancel = useHandler(() => bridge.resolve(undefined))
const containerStyle: ViewStyle = {
flex: fullScreen ? 1 : 0
@@ -62,7 +66,7 @@ export function ButtonsModal(prop
}
return (
-
+
{title != null ? {title} : null}
@@ -96,6 +100,6 @@ export function ButtonsModal(prop
return
})}
-
+
)
}
diff --git a/src/components/modals/CategoryModal.tsx b/src/components/modals/CategoryModal.tsx
index db5258a68e6..1037d2ae643 100644
--- a/src/components/modals/CategoryModal.tsx
+++ b/src/components/modals/CategoryModal.tsx
@@ -1,7 +1,7 @@
-import { FlashList, ListRenderItem } from '@shopify/flash-list'
import * as React from 'react'
-import { TouchableHighlight, View } from 'react-native'
+import { ListRenderItem, TouchableHighlight, View } from 'react-native'
import { AirshipBridge } from 'react-native-airship'
+import { FlatList } from 'react-native-gesture-handler'
import { Category, displayCategories, formatCategory, getSubcategories, joinCategory, setNewSubcategory, splitCategory } from '../../actions/CategoriesActions'
import { useAsyncEffect } from '../../hooks/useAsyncEffect'
@@ -14,8 +14,8 @@ import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'
import { DividerLine } from '../themed/DividerLine'
import { EdgeText } from '../themed/EdgeText'
import { FilledTextInput } from '../themed/FilledTextInput'
-import { ModalFooter, ModalFooterFade, ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalFooter, ModalTitle } from '../themed/ModalParts'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface Props {
bridge: AirshipBridge
@@ -133,7 +133,7 @@ export function CategoryModal(props: Props) {
))
return (
-
+
{lstrings.category_modal_title}
{categoryOrder.map(item => (
@@ -150,17 +150,17 @@ export function CategoryModal(props: Props) {
value={subcategory}
/>
-
-
+ {/* */}
-
+
)
}
diff --git a/src/components/modals/ConfirmContinueModal.tsx b/src/components/modals/ConfirmContinueModal.tsx
index a52b775c5d8..b1fbfa94c7b 100644
--- a/src/components/modals/ConfirmContinueModal.tsx
+++ b/src/components/modals/ConfirmContinueModal.tsx
@@ -10,7 +10,7 @@ import { EdgeText } from '../themed/EdgeText'
import { Fade } from '../themed/Fade'
import { MainButton } from '../themed/MainButton'
import { ModalMessage, ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface Props {
bridge: AirshipBridge
@@ -48,7 +48,7 @@ export function ConfirmContinueModal(props: Props) {
}
return (
-
+
{title != null && (
@@ -71,7 +71,7 @@ export function ConfirmContinueModal(props: Props) {
-
+
)
}
diff --git a/src/components/modals/FioCreateHandleModal.tsx b/src/components/modals/FioCreateHandleModal.tsx
index 89abee0f385..932c9f92609 100644
--- a/src/components/modals/FioCreateHandleModal.tsx
+++ b/src/components/modals/FioCreateHandleModal.tsx
@@ -10,8 +10,8 @@ import { parseMarkedText } from '../../util/parseMarkedText'
import { styled } from '../hoc/styled'
import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'
import { EdgeText } from '../themed/EdgeText'
-import { ThemedModal } from '../themed/ThemedModal'
import { ButtonsViewUi4 } from '../ui4/ButtonsViewUi4'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface Props {
bridge: AirshipBridge
@@ -38,7 +38,7 @@ export const FioCreateHandleModal = (props: Props) => {
})
return (
-
+
@@ -58,7 +58,7 @@ export const FioCreateHandleModal = (props: Props) => {
secondary={{ label: lstrings.not_now_button, onPress: handleCancel }}
layout="column"
/>
-
+
)
}
diff --git a/src/components/modals/FioExpiredModal.tsx b/src/components/modals/FioExpiredModal.tsx
index 665fc5aa09f..c16abbf1ef2 100644
--- a/src/components/modals/FioExpiredModal.tsx
+++ b/src/components/modals/FioExpiredModal.tsx
@@ -4,18 +4,18 @@ import { AirshipBridge } from 'react-native-airship'
import { lstrings } from '../../locales/strings'
import { MainButton } from '../themed/MainButton'
import { ModalMessage, ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
export function FioExpiredModal(props: { bridge: AirshipBridge; fioName: string }) {
const { bridge, fioName } = props
const title = `${lstrings.fio_address_confirm_screen_fio_label} ${lstrings.string_expiration}`
return (
- bridge.resolve(false)}>
+ bridge.resolve(false)}>
{title}
{lstrings.fio_domain_details_expired_soon}
{fioName}
bridge.resolve(true)} />
-
+
)
}
diff --git a/src/components/modals/FlipInputModal2.tsx b/src/components/modals/FlipInputModal2.tsx
index a291b591368..7106a76e18e 100644
--- a/src/components/modals/FlipInputModal2.tsx
+++ b/src/components/modals/FlipInputModal2.tsx
@@ -22,8 +22,8 @@ import { FiatText } from '../text/FiatText'
import { EdgeText } from '../themed/EdgeText'
import { ExchangedFlipInput2, ExchangedFlipInputAmounts, ExchangedFlipInputRef, ExchangeFlipInputFields } from '../themed/ExchangedFlipInput2'
import { MiniButton } from '../themed/MiniButton'
-import { ThemedModal } from '../themed/ThemedModal'
import { CardUi4 } from '../ui4/CardUi4'
+import { ModalUi4 } from '../ui4/ModalUi4'
export interface FlipInputModalResult {
nativeAmount: string
@@ -219,7 +219,7 @@ const FlipInputModal2Component = React.forwardRef((pro
}))
return (
-
+
{/* Extra view needed here to fullscreen the modal on small devices */}
{renderFlipInput()}
@@ -232,7 +232,7 @@ const FlipInputModal2Component = React.forwardRef((pro
-
+
)
})
diff --git a/src/components/modals/HelpModal.tsx b/src/components/modals/HelpModal.tsx
index 105b769c07a..c5ece07b33f 100644
--- a/src/components/modals/HelpModal.tsx
+++ b/src/components/modals/HelpModal.tsx
@@ -17,7 +17,7 @@ import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'
import { EdgeText } from '../themed/EdgeText'
import { ModalTitle } from '../themed/ModalParts'
import { SelectableRow } from '../themed/SelectableRow'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
const buildNumber = getBuildNumber()
const versionNumber = getVersion()
@@ -61,7 +61,7 @@ export const HelpModal = (props: Props) => {
const helpSiteMoreInfoText = sprintf(lstrings.help_site_more_info_text, config.appName)
return (
-
+
@@ -106,7 +106,7 @@ export const HelpModal = (props: Props) => {
{versionText}
{buildText}
-
+
)
}
diff --git a/src/components/modals/InsufficientFeesModal.tsx b/src/components/modals/InsufficientFeesModal.tsx
index 2de550c6cb4..a19040ca3c3 100644
--- a/src/components/modals/InsufficientFeesModal.tsx
+++ b/src/components/modals/InsufficientFeesModal.tsx
@@ -13,7 +13,7 @@ import { getCurrencyCode } from '../../util/CurrencyInfoHelpers'
import { roundedFee } from '../../util/utils'
import { MainButton } from '../themed/MainButton'
import { ModalMessage, ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface Props {
bridge: AirshipBridge
@@ -54,12 +54,12 @@ export function InsufficientFeesModal(props: Props) {
})
return (
-
+
{lstrings.buy_crypto_modal_title}
{sprintf(lstrings.buy_parent_crypto_modal_message_2s, amountString, name)}
-
+
)
}
diff --git a/src/components/modals/ListModal.tsx b/src/components/modals/ListModal.tsx
index fa3f108ed89..cc377f461cc 100644
--- a/src/components/modals/ListModal.tsx
+++ b/src/components/modals/ListModal.tsx
@@ -1,13 +1,13 @@
-import { FlashList, ListRenderItem } from '@shopify/flash-list'
import * as React from 'react'
-import { Keyboard, ViewStyle, ViewToken } from 'react-native'
+import { Keyboard, ListRenderItem, ViewStyle, ViewToken } from 'react-native'
import { AirshipBridge } from 'react-native-airship'
+import { FlatList } from 'react-native-gesture-handler'
import { useFilter } from '../../hooks/useFilter'
import { useTheme } from '../services/ThemeContext'
import { FilledTextInput } from '../themed/FilledTextInput'
import { ModalFooter, ModalMessage, ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface Props {
bridge: AirshipBridge
@@ -36,8 +36,6 @@ interface Props {
rowComponent?: (props: T) => React.ReactElement
rowDataFilter?: (filterText: string, data: T, index: number) => boolean
onViewableItemsChanged?: (info: { viewableItems: ViewToken[]; changed: ViewToken[] }) => void
- // Footer Props
- closeArrow?: boolean // Defaults to 'true'
}
export function ListModal({
@@ -50,7 +48,6 @@ export function ListModal({
fullScreen = true,
rowComponent,
rowDataFilter,
- closeArrow = true,
onSubmitEditing,
onViewableItemsChanged,
label: placeholder,
@@ -73,7 +70,7 @@ export function ListModal({
}, [theme])
return (
-
+
{title == null ? null : {title}}
{message == null ? null : {message}}
{textInput == null ? null : (
@@ -95,16 +92,16 @@ export function ListModal({
{...textProps}
/>
)}
- `${i}`}
renderItem={renderItem}
onScroll={() => Keyboard.dismiss()}
onViewableItemsChanged={onViewableItemsChanged}
/>
-
+
)
}
diff --git a/src/components/modals/LogsModal.tsx b/src/components/modals/LogsModal.tsx
index 3f1c30d386e..11ae5ee5af0 100644
--- a/src/components/modals/LogsModal.tsx
+++ b/src/components/modals/LogsModal.tsx
@@ -11,7 +11,7 @@ import { showToast } from '../services/AirshipInstance'
import { FilledTextInput } from '../themed/FilledTextInput'
import { MainButton } from '../themed/MainButton'
import { ModalMessage, ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface Props {
bridge: AirshipBridge
logs: MultiLogOutput
@@ -71,7 +71,7 @@ export const LogsModal = (props: Props) => {
}
return (
-
+
{lstrings.settings_button_export_logs}
{!isDangerous ? null : }
{isDangerous ? null : {lstrings.settings_modal_export_logs_message}}
@@ -89,6 +89,6 @@ export const LogsModal = (props: Props) => {
)}
-
+
)
}
diff --git a/src/components/modals/PasswordReminderModal.tsx b/src/components/modals/PasswordReminderModal.tsx
index 280e0d3246a..5b75d0b492d 100644
--- a/src/components/modals/PasswordReminderModal.tsx
+++ b/src/components/modals/PasswordReminderModal.tsx
@@ -10,8 +10,8 @@ import { showError, showToast } from '../services/AirshipInstance'
import { ThemeProps, withTheme } from '../services/ThemeContext'
import { FilledTextInput } from '../themed/FilledTextInput'
import { ModalMessage, ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
import { ButtonsViewUi4 } from '../ui4/ButtonsViewUi4'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface OwnProps {
bridge: AirshipBridge
@@ -79,7 +79,7 @@ export class PasswordReminderModalComponent extends React.PureComponent
+
{lstrings.password_reminder_remember_your_password}
{lstrings.password_reminder_you_will_need_your_password}
@@ -107,7 +107,7 @@ export class PasswordReminderModalComponent extends React.PureComponent
-
+
)
}
}
diff --git a/src/components/modals/PermissionsSettingModal.tsx b/src/components/modals/PermissionsSettingModal.tsx
index 8d6fe4bf704..96f669df730 100644
--- a/src/components/modals/PermissionsSettingModal.tsx
+++ b/src/components/modals/PermissionsSettingModal.tsx
@@ -11,7 +11,7 @@ import { showError } from '../services/AirshipInstance'
import { checkIfDenied } from '../services/PermissionsManager'
import { MainButton } from '../themed/MainButton'
import { ModalMessage } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
export function PermissionsSettingModal(props: {
bridge: AirshipBridge // returns true if mandatory and denied
@@ -45,9 +45,9 @@ export function PermissionsSettingModal(props: {
const handleClose = () => bridge.resolve(mandatory)
return (
-
+
{message}
-
+
)
}
diff --git a/src/components/modals/RawTextModal.tsx b/src/components/modals/RawTextModal.tsx
index 67c92f09840..afd88aa8704 100644
--- a/src/components/modals/RawTextModal.tsx
+++ b/src/components/modals/RawTextModal.tsx
@@ -7,7 +7,7 @@ import { lstrings } from '../../locales/strings'
import { showToast } from '../services/AirshipInstance'
import { MainButton } from '../themed/MainButton'
import { ModalMessage, ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface Props {
bridge: AirshipBridge
@@ -27,7 +27,7 @@ export function RawTextModal(props: Props) {
}
return (
-
+
{title != null ? {title} : null}
{body}
@@ -35,6 +35,6 @@ export function RawTextModal(props: Props) {
{disableCopy ? null : (
)}
-
+
)
}
diff --git a/src/components/modals/SwapVerifyTermsModal.tsx b/src/components/modals/SwapVerifyTermsModal.tsx
index dfc20ac99ea..bbcde81449d 100644
--- a/src/components/modals/SwapVerifyTermsModal.tsx
+++ b/src/components/modals/SwapVerifyTermsModal.tsx
@@ -10,7 +10,7 @@ import { Airship } from '../services/AirshipInstance'
import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'
import { MainButton } from '../themed/MainButton'
import { ModalMessage, ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface TermsUri {
termsUri?: string
@@ -59,7 +59,7 @@ function SwapVerifyTermsModal(props: Props) {
const styles = getStyles(theme)
return (
- bridge.resolve(false)}>
+ bridge.resolve(false)}>
{displayName}
@@ -84,7 +84,7 @@ function SwapVerifyTermsModal(props: Props) {
)}
-
+
)
}
diff --git a/src/components/modals/TextInputModal.tsx b/src/components/modals/TextInputModal.tsx
index b433b4b3dc4..8e4ec275a4d 100644
--- a/src/components/modals/TextInputModal.tsx
+++ b/src/components/modals/TextInputModal.tsx
@@ -8,7 +8,7 @@ import { Alert } from '../themed/Alert'
import { FilledTextInput } from '../themed/FilledTextInput'
import { MainButton } from '../themed/MainButton'
import { ModalMessage, ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface Props {
// Resolves to the entered string, or void if cancelled.
@@ -91,7 +91,7 @@ export function TextInputModal(props: Props) {
}
return (
- bridge.resolve(undefined)}>
+ bridge.resolve(undefined)}>
{title != null ? {title} : null}
{typeof message === 'string' ? {message} : <>{message}>}
{warningMessage != null ? : null}
@@ -126,6 +126,6 @@ export function TextInputModal(props: Props) {
) : (
)}
-
+
)
}
diff --git a/src/components/modals/TransferModal.tsx b/src/components/modals/TransferModal.tsx
index 7e3b9464e65..74d24ec1968 100644
--- a/src/components/modals/TransferModal.tsx
+++ b/src/components/modals/TransferModal.tsx
@@ -17,7 +17,7 @@ import { Airship } from '../services/AirshipInstance'
import { Theme, useTheme } from '../services/ThemeContext'
import { ModalTitle } from '../themed/ModalParts'
import { SelectableRow } from '../themed/SelectableRow'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
import { WalletListModal, WalletListResult } from './WalletListModal'
interface Props {
@@ -152,24 +152,13 @@ export const TransferModal = ({ account, bridge, depositOrSend, navigation }: Pr
const options = depositOrSend === 'deposit' ? depositOptions : sendOptions
return (
-
+
{depositOrSend === 'deposit' ? lstrings.loan_fragment_deposit : lstrings.fragment_send_subtitle}
{options.map((option, index) => {
const { title, icon, onPress } = option
- return (
- {icon}}
- // HACK: ThemedModal has 1 rem padding all around, making it
- // impossible to use components expecting split 0.5rem
- // margin/padding.
- marginRem={[0.5, -0.5]}
- />
- )
+ return {icon}} />
})}
-
+
)
}
diff --git a/src/components/modals/UpdateModal.tsx b/src/components/modals/UpdateModal.tsx
index 7235bac22f6..de22198517d 100644
--- a/src/components/modals/UpdateModal.tsx
+++ b/src/components/modals/UpdateModal.tsx
@@ -9,7 +9,7 @@ import { config } from '../../theme/appConfig'
import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'
import { MainButton } from '../themed/MainButton'
import { ModalMessage, ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface Props {
bridge: AirshipBridge
@@ -31,7 +31,7 @@ export function UpdateModal(props: Props) {
const message = sprintf(lstrings.update_fresh_new_version, config.appName)
return (
-
+
{lstrings.update_header}
@@ -39,7 +39,7 @@ export function UpdateModal(props: Props) {
{message}
-
+
)
}
diff --git a/src/components/modals/WalletListMenuModal.tsx b/src/components/modals/WalletListMenuModal.tsx
index 85717531dd6..d0c7033eba4 100644
--- a/src/components/modals/WalletListMenuModal.tsx
+++ b/src/components/modals/WalletListMenuModal.tsx
@@ -18,8 +18,8 @@ import { getWalletName } from '../../util/CurrencyWalletHelpers'
import { showError } from '../services/AirshipInstance'
import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'
import { ModalScrollArea, ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
import { CryptoIconUi4 } from '../ui4/CryptoIconUi4'
+import { ModalUi4 } from '../ui4/ModalUi4'
interface Option {
value: WalletListMenuKey
@@ -212,7 +212,7 @@ export function WalletListMenuModal(props: Props) {
)
return (
-
+
{wallet != null && (
{getWalletName(wallet)}
@@ -238,7 +238,7 @@ export function WalletListMenuModal(props: Props) {
))}
-
+
)
}
diff --git a/src/components/modals/WcSmartContractModal.tsx b/src/components/modals/WcSmartContractModal.tsx
index 51a08fccc98..ebef8e51ecf 100644
--- a/src/components/modals/WcSmartContractModal.tsx
+++ b/src/components/modals/WcSmartContractModal.tsx
@@ -19,12 +19,12 @@ import { zeroString } from '../../util/utils'
import { Airship, showError } from '../services/AirshipInstance'
import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'
import { Alert } from '../themed/Alert'
-import { ModalFooter, ModalFooterFade, ModalTitle } from '../themed/ModalParts'
+import { ModalFooter, ModalTitle } from '../themed/ModalParts'
import { SafeSlider } from '../themed/SafeSlider'
-import { ThemedModal } from '../themed/ThemedModal'
import { CryptoFiatAmountTile } from '../tiles/CryptoFiatAmountTile'
import { FiatAmountTile } from '../tiles/FiatAmountTile'
import { CardUi4 } from '../ui4/CardUi4'
+import { ModalUi4 } from '../ui4/ModalUi4'
import { RowUi4 } from '../ui4/RowUi4'
interface Props extends WcSmartContractModalProps {
@@ -141,7 +141,7 @@ export const WcSmartContractModal = (props: Props) => {
)
return (
-
+
{lstrings.wc_smartcontract_title}
@@ -177,8 +177,8 @@ export const WcSmartContractModal = (props: Props) => {
)}
{slider}
-
-
+ {/* */}
+
)
}
diff --git a/src/components/modals/WebViewModal.tsx b/src/components/modals/WebViewModal.tsx
index 16971d48b2a..2516dd9000b 100644
--- a/src/components/modals/WebViewModal.tsx
+++ b/src/components/modals/WebViewModal.tsx
@@ -4,7 +4,7 @@ import { WebView } from 'react-native-webview'
import { Airship } from '../services/AirshipInstance'
import { ModalTitle } from '../themed/ModalParts'
-import { ThemedModal } from '../themed/ThemedModal'
+import { ModalUi4 } from '../ui4/ModalUi4'
export async function showWebViewModal(title: string, uri: string): Promise {
await Airship.show(bridge => )
@@ -25,11 +25,11 @@ export const WebViewModal = (props: Props) => {
}
return (
-
+
{title}
-
+
)
}
diff --git a/src/components/scenes/CryptoExchangeQuoteScene.tsx b/src/components/scenes/CryptoExchangeQuoteScene.tsx
index fc69f383b1f..d16a314adea 100644
--- a/src/components/scenes/CryptoExchangeQuoteScene.tsx
+++ b/src/components/scenes/CryptoExchangeQuoteScene.tsx
@@ -25,9 +25,9 @@ import { LineTextDivider } from '../themed/LineTextDivider'
import { ModalFooter, ModalTitle } from '../themed/ModalParts'
import { SceneHeader } from '../themed/SceneHeader'
import { Slider } from '../themed/Slider'
-import { ThemedModal } from '../themed/ThemedModal'
import { WalletListSectionHeader } from '../themed/WalletListSectionHeader'
import { AlertCardUi4 } from '../ui4/AlertCardUi4'
+import { ModalUi4 } from '../ui4/ModalUi4'
export interface CryptoExchangeQuoteParams {
selectedQuote: EdgeSwapQuote
@@ -140,7 +140,7 @@ export const CryptoExchangeQuoteScene = (props: Props) => {
const handlePoweredByTap = useHandler(async () => {
await Airship.show(bridge => (
- bridge.resolve()}>
+ bridge.resolve()}>
{lstrings.quote_swap_provider}
{
renderSectionHeader={renderSectionHeader}
sections={sectionList}
/>
-
+
))
})
diff --git a/src/components/scenes/Fio/FioStakingChangeScene.tsx b/src/components/scenes/Fio/FioStakingChangeScene.tsx
index 1123596a788..d55a289ca87 100644
--- a/src/components/scenes/Fio/FioStakingChangeScene.tsx
+++ b/src/components/scenes/Fio/FioStakingChangeScene.tsx
@@ -28,7 +28,7 @@ import { ExchangedFlipInputAmounts } from '../../themed/ExchangedFlipInput2'
import { ModalMessage, ModalTitle } from '../../themed/ModalParts'
import { SceneHeader } from '../../themed/SceneHeader'
import { Slider } from '../../themed/Slider'
-import { ThemedModal } from '../../themed/ThemedModal'
+import { ModalUi4 } from '../../ui4/ModalUi4'
import { RowUi4 } from '../../ui4/RowUi4'
interface Props extends EdgeSceneProps<'fioStakingChange'> {
@@ -185,13 +185,13 @@ export const FioStakingChangeScene = withWallet((props: Props) => {
const handleUnlockDate = async () => {
await Airship.show(bridge => {
return (
-
+
}>
{lstrings.staking_change_unlock_explainer_title}
{lstrings.staking_change_unlock_explainer1}
{lstrings.staking_change_unlock_explainer2}
-
+
)
})
}
diff --git a/src/components/ui4/ModalUi4.tsx b/src/components/ui4/ModalUi4.tsx
new file mode 100644
index 00000000000..0eefe82fba1
--- /dev/null
+++ b/src/components/ui4/ModalUi4.tsx
@@ -0,0 +1,203 @@
+import * as React from 'react'
+import { BackHandler, Dimensions, StyleSheet, TouchableOpacity, TouchableWithoutFeedback, View } from 'react-native'
+import { AirshipBridge } from 'react-native-airship'
+import { Gesture, GestureDetector, ScrollView } from 'react-native-gesture-handler'
+import { cacheStyles } from 'react-native-patina'
+import Animated, { runOnJS, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'
+import AntDesignIcon from 'react-native-vector-icons/AntDesign'
+import { BlurView } from 'rn-id-blurview'
+
+import { useHandler } from '../../hooks/useHandler'
+import { fixSides, mapSides, sidesToMargin } from '../../util/sides'
+import { maybeComponent } from '../hoc/maybeComponent'
+import { Theme, useTheme } from '../services/ThemeContext'
+
+export interface ModalPropsUi4 {
+ bridge: AirshipBridge
+ children?: React.ReactNode
+
+ // Internal padding to place inside the component.
+ paddingRem?: number[] | number
+
+ // Include a scroll area:
+ scroll?: boolean
+
+ // Gives the box a border:
+ warning?: boolean
+
+ // Called when the user taps outside the modal or clicks the back button.
+ // If this is missing, the modal will not be closable.
+ onCancel?: () => void
+}
+
+const safeAreaGap = 64 // Overkill to avoid bottom of screen
+const duration = 300
+
+/**
+ * A modal that slides a modal up from the bottom of the screen
+ * and dims the rest of the app.
+ */
+export function ModalUi4(props: ModalPropsUi4): JSX.Element {
+ const { bridge, children, paddingRem, scroll = false, warning = false, onCancel } = props
+ const theme = useTheme()
+ const styles = getStyles(theme)
+
+ // Use margin instead of padding to give children the ability to bypass the
+ // default "padding," if necessary
+ const childrenMargin = sidesToMargin(mapSides(fixSides(paddingRem, 0.5), theme.rem))
+ const closeThreshold = theme.rem(6)
+ const dragSlop = theme.rem(1)
+
+ //
+ // Shared state
+ //
+
+ const opacity = useSharedValue(0)
+ const offset = useSharedValue(Dimensions.get('window').height)
+
+ const handleCancel = useHandler(() => {
+ if (onCancel != null) onCancel()
+ })
+
+ //
+ // Effects
+ //
+
+ React.useEffect(() => bridge.on('clear', handleCancel), [bridge, handleCancel])
+
+ React.useEffect(() => {
+ // Animate in:
+ opacity.value = withTiming(1, { duration })
+ offset.value = withTiming(0, { duration })
+
+ // Animate out:
+ bridge.on('result', () => {
+ opacity.value = withTiming(0, { duration })
+ offset.value = withTiming(Dimensions.get('window').height, { duration }, () => runOnJS(bridge.remove)())
+ })
+ }, [bridge, opacity, offset])
+
+ React.useEffect(() => {
+ const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
+ handleCancel()
+ return true
+ })
+ return () => backHandler.remove()
+ }, [handleCancel])
+
+ const gesture = Gesture.Pan()
+ .onUpdate(e => {
+ offset.value = e.translationY
+ })
+ .onEnd(() => {
+ if (offset.value > closeThreshold) {
+ runOnJS(handleCancel)()
+ }
+ offset.value = withTiming(0, { duration })
+ })
+
+ //
+ // Dynamic styles
+ //
+
+ const underlayStyle = useAnimatedStyle(() => ({
+ opacity: opacity.value
+ }))
+
+ const bodyStyle = useAnimatedStyle(() => ({
+ transform: [{ translateY: Math.max(-dragSlop, offset.value) }]
+ }))
+
+ const bodyLayout = {
+ borderColor: warning ? theme.warningText : theme.modalBorderColor,
+ borderWidth: warning ? 4 : theme.modalBorderWidth,
+ marginBottom: -safeAreaGap - dragSlop,
+ paddingBottom: safeAreaGap + dragSlop
+ }
+
+ return (
+ <>
+
+
+
+
+
+ {/* Need another Biew here because BlurView doesn't accept rounded corners in its styling */}
+
+
+
+
+
+
+
+ {scroll ? (
+
+ {children}
+
+ ) : (
+ {children}
+ )}
+
+ {onCancel == null ? null : (
+
+
+
+ )}
+
+
+ >
+ )
+}
+
+const getStyles = cacheStyles((theme: Theme) => ({
+ underlay: {
+ bottom: 0,
+ left: 0,
+ position: 'absolute',
+ right: 0,
+ top: 0
+ },
+ body: {
+ alignSelf: 'flex-end',
+ backgroundColor: theme.modalBackgroundUi4,
+ borderTopLeftRadius: theme.rem(1),
+ borderTopRightRadius: theme.rem(1),
+ flexShrink: 1,
+ width: theme.rem(30) // This works as a maxWidth because flexShrink is set
+ },
+
+ blurContainer: {
+ position: 'absolute',
+ width: '100%',
+ height: '100%',
+ borderTopLeftRadius: theme.rem(1),
+ borderTopRightRadius: theme.rem(1),
+ overflow: 'hidden'
+ },
+ dragBarContainer: {
+ alignItems: 'center',
+ left: 0,
+ position: 'absolute',
+ right: 0,
+ top: 0
+ },
+ dragBar: {
+ // TODO: Replace this color we have a proper design:
+ backgroundColor: theme.deactivatedText,
+ borderRadius: theme.rem(0.125),
+ height: theme.rem(0.25),
+ marginTop: theme.rem(0.5),
+ width: theme.rem(3)
+ },
+ closeIcon: {
+ alignItems: 'center',
+ height: theme.rem(2),
+ justifyContent: 'center',
+ position: 'absolute',
+ right: 0,
+ top: 0,
+ width: theme.rem(2)
+ }
+}))
+
+const MaybeScrollView = maybeComponent(ScrollView)
diff --git a/src/theme/variables/edgeDark.ts b/src/theme/variables/edgeDark.ts
index 2e086074b45..a0f7d693717 100644
--- a/src/theme/variables/edgeDark.ts
+++ b/src/theme/variables/edgeDark.ts
@@ -550,6 +550,8 @@ export const edgeDark: Theme = {
start: { x: 1, y: 0 }
},
+ modalBackgroundUi4: 'rgba(255, 255, 255, 0.376)',
+
txDirBgReceiveUi4: palette.greenOp60,
txDirBgSendUi4: palette.redOp60,
txDirBgSwapUi4: palette.grayOp70,
diff --git a/src/theme/variables/edgeLight.ts b/src/theme/variables/edgeLight.ts
index 591304d5f8f..44a03901cc7 100644
--- a/src/theme/variables/edgeLight.ts
+++ b/src/theme/variables/edgeLight.ts
@@ -519,6 +519,8 @@ export const edgeLight: Theme = {
start: { x: 1, y: 0 }
},
+ modalBackgroundUi4: '#ffffff80',
+
txDirBgReceiveUi4: palette.greenOp60,
txDirBgSendUi4: palette.redOp60,
txDirBgSwapUi4: palette.grayOp70,
diff --git a/src/theme/variables/testDark.ts b/src/theme/variables/testDark.ts
index 07e4c3a896a..fc5c69cedcd 100644
--- a/src/theme/variables/testDark.ts
+++ b/src/theme/variables/testDark.ts
@@ -550,6 +550,8 @@ export const testDark: Theme = {
start: { x: 1, y: 0 }
},
+ modalBackgroundUi4: '#00000080',
+
txDirBgReceiveUi4: palette.greenOp60,
txDirBgSendUi4: palette.redOp60,
txDirBgSwapUi4: palette.grayOp70,
diff --git a/src/theme/variables/testLight.ts b/src/theme/variables/testLight.ts
index 32b666db638..79c4159916f 100644
--- a/src/theme/variables/testLight.ts
+++ b/src/theme/variables/testLight.ts
@@ -519,6 +519,8 @@ export const testLight: Theme = {
start: { x: 1, y: 0 }
},
+ modalBackgroundUi4: '#ffffff80',
+
txDirBgReceiveUi4: palette.greenOp60,
txDirBgSendUi4: palette.redOp60,
txDirBgSwapUi4: palette.grayOp70,
diff --git a/src/types/Theme.ts b/src/types/Theme.ts
index 5afc844cfb7..96dc5320e2a 100644
--- a/src/types/Theme.ts
+++ b/src/types/Theme.ts
@@ -454,6 +454,8 @@ export interface Theme {
fioCardGradientUi4: ThemeGradientParams
swapCardGradientUi4: ThemeGradientParams
+ modalBackgroundUi4: string
+
txDirBgReceiveUi4: string
txDirBgSendUi4: string
txDirBgSwapUi4: string