mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-10-05 11:18:24 +00:00
Fix error and info modal on Android
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@ minibits-upload-key.keystore
|
|||||||
GoogleService-Info.plist
|
GoogleService-Info.plist
|
||||||
minibits-prod-firebase-adminsdk.json
|
minibits-prod-firebase-adminsdk.json
|
||||||
dist/
|
dist/
|
||||||
zapstore
|
zsp
|
||||||
|
|
||||||
# OSX
|
# OSX
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
shouldAutocreateTestPlan = "YES">
|
shouldAutocreateTestPlan = "YES">
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "minibits_wallet",
|
"name": "minibits_wallet",
|
||||||
"version": "0.4.0-beta.2",
|
"version": "0.4.0-beta.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"android:clean": "cd android && ./gradlew clean",
|
"android:clean": "cd android && ./gradlew clean",
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export const AmountInput = forwardRef<TextInput, AmountInputProps>(
|
|||||||
const [hasBottomAmountFocusedOnce, setHasBottomAmountFocusedOnce] = useState(false)
|
const [hasBottomAmountFocusedOnce, setHasBottomAmountFocusedOnce] = useState(false)
|
||||||
const [hasBeenFirstTimeConverted, setHasBeenFirstTimeConverted] = useState<boolean>(false)
|
const [hasBeenFirstTimeConverted, setHasBeenFirstTimeConverted] = useState<boolean>(false)
|
||||||
|
|
||||||
const amountInputColor = useThemeColor("amountInput")
|
const focusedInputColor = useThemeColor("amountInput")
|
||||||
const convertedAmountColor = useThemeColor("headerSubTitle")
|
const convertedAmountColor = useThemeColor("headerSubTitle")
|
||||||
const symbolColor = useThemeColor("headerSubTitle")
|
const symbolColor = useThemeColor("headerSubTitle")
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ export const AmountInput = forwardRef<TextInput, AmountInputProps>(
|
|||||||
fontFamily: typography.primary?.bold,
|
fontFamily: typography.primary?.bold,
|
||||||
fontWeight: 'bold', // android
|
fontWeight: 'bold', // android
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
color: amountInputColor,
|
color: focusedInputColor,
|
||||||
}
|
}
|
||||||
|
|
||||||
const animatedTopStyle = useAnimatedStyle(() => ({
|
const animatedTopStyle = useAnimatedStyle(() => ({
|
||||||
@@ -273,7 +273,12 @@ export const AmountInput = forwardRef<TextInput, AmountInputProps>(
|
|||||||
onEndEditing={onAmountEndEditing}
|
onEndEditing={onAmountEndEditing}
|
||||||
onFocus={handleTopFocus}
|
onFocus={handleTopFocus}
|
||||||
onBlur={handleTopBlur}
|
onBlur={handleTopBlur}
|
||||||
style={[defaultTopStyle, style, animatedTopStyle]}
|
style={[
|
||||||
|
defaultTopStyle,
|
||||||
|
style,
|
||||||
|
animatedTopStyle,
|
||||||
|
{ color: focused === 'top' ? focusedInputColor : convertedAmountColor }
|
||||||
|
]}
|
||||||
maxLength={9}
|
maxLength={9}
|
||||||
keyboardType="decimal-pad"
|
keyboardType="decimal-pad"
|
||||||
returnKeyType="done"
|
returnKeyType="done"
|
||||||
@@ -307,6 +312,7 @@ export const AmountInput = forwardRef<TextInput, AmountInputProps>(
|
|||||||
style,
|
style,
|
||||||
{ color: convertedAmountColor },
|
{ color: convertedAmountColor },
|
||||||
animatedBottomStyle,
|
animatedBottomStyle,
|
||||||
|
{ color: focused === 'bottom' ? focusedInputColor : convertedAmountColor }
|
||||||
]}
|
]}
|
||||||
maxLength={9}
|
maxLength={9}
|
||||||
keyboardType="decimal-pad"
|
keyboardType="decimal-pad"
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ interface ModalProps extends ViewProps {
|
|||||||
top?: number
|
top?: number
|
||||||
onBackdropPress?: any
|
onBackdropPress?: any
|
||||||
onBackButtonPress?: any
|
onBackButtonPress?: any
|
||||||
backdropOpacity?: number
|
onModalHide?: () => void
|
||||||
|
backdropOpacity?: number
|
||||||
/**
|
/**
|
||||||
* The heading text to display if not using `headingTx`.
|
* The heading text to display if not using `headingTx`.
|
||||||
*/
|
*/
|
||||||
@@ -109,11 +110,12 @@ interface ModalProps extends ViewProps {
|
|||||||
*/
|
*/
|
||||||
export function BottomModal(props: ModalProps) {
|
export function BottomModal(props: ModalProps) {
|
||||||
const {
|
const {
|
||||||
isVisible = true,
|
isVisible = true,
|
||||||
onBackdropPress,
|
onBackdropPress,
|
||||||
onBackButtonPress,
|
onBackButtonPress,
|
||||||
|
onModalHide,
|
||||||
//backdropOpacity = Platform.OS === 'ios' ? 0.25 : 0,
|
//backdropOpacity = Platform.OS === 'ios' ? 0.25 : 0,
|
||||||
backdropOpacity = 0.25,
|
backdropOpacity = 0.25,
|
||||||
content,
|
content,
|
||||||
contentTx,
|
contentTx,
|
||||||
contentTxOptions,
|
contentTxOptions,
|
||||||
@@ -175,6 +177,7 @@ export function BottomModal(props: ModalProps) {
|
|||||||
avoidKeyboard={Platform.OS === 'ios'}
|
avoidKeyboard={Platform.OS === 'ios'}
|
||||||
onBackdropPress={onBackdropPress}
|
onBackdropPress={onBackdropPress}
|
||||||
onBackButtonPress={onBackButtonPress}
|
onBackButtonPress={onBackButtonPress}
|
||||||
|
onModalHide={onModalHide}
|
||||||
backdropOpacity={backdropOpacity}
|
backdropOpacity={backdropOpacity}
|
||||||
useNativeDriverForBackdrop={true}
|
useNativeDriverForBackdrop={true}
|
||||||
hideModalContentWhileAnimating={true}
|
hideModalContentWhileAnimating={true}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import JSONTree from 'react-native-json-tree'
|
|||||||
import Clipboard from '@react-native-clipboard/clipboard'
|
import Clipboard from '@react-native-clipboard/clipboard'
|
||||||
import { useStores } from '../models'
|
import { useStores } from '../models'
|
||||||
import useIsInternetReachable from '../utils/useIsInternetReachable'
|
import useIsInternetReachable from '../utils/useIsInternetReachable'
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||||
|
|
||||||
type ErrorModalProps = {
|
type ErrorModalProps = {
|
||||||
error: AppError
|
error: AppError
|
||||||
@@ -58,22 +59,23 @@ export const ErrorModal: FC<ErrorModalProps> = function ({ error }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bg = useThemeColor('error')
|
const bg = useThemeColor('error')
|
||||||
|
const insets = useSafeAreaInsets()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomModal
|
<BottomModal
|
||||||
isVisible={isErrorVisible}
|
isVisible={isErrorVisible}
|
||||||
onBackdropPress={onClose}
|
onBackdropPress={onClose}
|
||||||
onBackButtonPress={onClose}
|
onBackButtonPress={onClose}
|
||||||
|
style={{ marginBottom: Platform.OS === 'android' ? -(insets.top + insets.bottom) : 0 }}
|
||||||
ContentComponent={
|
ContentComponent={
|
||||||
<>
|
<View>
|
||||||
<View style={{ flexDirection: 'row', alignItems: 'center', marginBottom: spacing.small }}>
|
<View style={{ flexDirection: 'row', alignItems: 'center', marginBottom: spacing.small }}>
|
||||||
<Icon icon="faTriangleExclamation" size={spacing.large} color={bg} />
|
<Icon icon="faTriangleExclamation" size={spacing.large} color={bg} />
|
||||||
<Text style={{ marginLeft: spacing.small }}>{error.name}</Text>
|
<Text style={{ marginLeft: spacing.small }}>{error.name}</Text>
|
||||||
</View>
|
</View>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
style={{
|
style={{
|
||||||
//height: spacing.screenHeight * 0.1,
|
maxHeight: spacing.screenHeight * 0.1,
|
||||||
maxHeight: spacing.screenHeight * 0.07,
|
|
||||||
}}>
|
}}>
|
||||||
<Text style={{ marginBottom: spacing.small }}>{error.message}</Text>
|
<Text style={{ marginBottom: spacing.small }}>{error.message}</Text>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
@@ -147,7 +149,7 @@ export const ErrorModal: FC<ErrorModalProps> = function ({ error }) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</>
|
</View>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import React, { FC, useState, useEffect } from 'react'
|
import React, { FC, useState, useEffect } from 'react'
|
||||||
import { View } from 'react-native'
|
import { Platform, View } from 'react-native'
|
||||||
import { BottomModal, Icon, Text } from '../components'
|
import { BottomModal, Icon, Text } from '../components'
|
||||||
import { spacing, useThemeColor } from '../theme'
|
import { spacing, useThemeColor } from '../theme'
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||||
|
|
||||||
|
|
||||||
type InfoModalProps = {
|
type InfoModalProps = {
|
||||||
@@ -25,12 +26,14 @@ export const InfoModal: FC<InfoModalProps> = function ({ message }) {
|
|||||||
|
|
||||||
const backgroundColor = useThemeColor('info')
|
const backgroundColor = useThemeColor('info')
|
||||||
const iconColor = useThemeColor('textDim')
|
const iconColor = useThemeColor('textDim')
|
||||||
|
const insets = useSafeAreaInsets()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomModal
|
<BottomModal
|
||||||
isVisible={isInfoVisible}
|
isVisible={isInfoVisible}
|
||||||
onBackdropPress={() => onClose()}
|
onBackdropPress={() => onClose()}
|
||||||
onBackButtonPress={() => onClose()}
|
onBackButtonPress={() => onClose()}
|
||||||
|
style={{ marginBottom: Platform.OS === 'android' ? -(insets.top + insets.bottom) : 0 }}
|
||||||
ContentComponent={
|
ContentComponent={
|
||||||
<View style={{ padding: spacing.small, flexDirection: 'row', alignItems: 'center', marginRight: spacing.medium}}>
|
<View style={{ padding: spacing.small, flexDirection: 'row', alignItems: 'center', marginRight: spacing.medium}}>
|
||||||
<Icon icon="faInfoCircle" size={spacing.large} color={iconColor} />
|
<Icon icon="faInfoCircle" size={spacing.large} color={iconColor} />
|
||||||
|
|||||||
@@ -1,23 +1,21 @@
|
|||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import React, {useEffect, useRef, useState} from 'react'
|
import React, {useEffect, useRef, useState} from 'react'
|
||||||
import {FlatList, TextInput, TextStyle, View, ViewStyle} from 'react-native'
|
import {FlatList, Platform, TextInput, TextStyle, View, ViewStyle} from 'react-native'
|
||||||
import {verticalScale} from '@gocodingnow/rn-size-matters'
|
import {verticalScale} from '@gocodingnow/rn-size-matters'
|
||||||
import {colors, spacing, useThemeColor} from '../../theme'
|
import {colors, spacing, useThemeColor} from '../../theme'
|
||||||
import {BottomModal, Button, Card, ErrorModal, Icon, InfoModal, ListItem, Loading, Screen, Text} from '../../components'
|
import {BottomModal, Button, Card, ErrorModal, Icon, InfoModal, ListItem, Loading, Screen, Text} from '../../components'
|
||||||
import {useStores} from '../../models'
|
import {useStores} from '../../models'
|
||||||
import { MinibitsClient, NostrClient, NostrProfile } from '../../services'
|
import { NostrClient, NostrProfile } from '../../services'
|
||||||
import AppError, { Err } from '../../utils/AppError'
|
import AppError, { Err } from '../../utils/AppError'
|
||||||
import {MINIBITS_NIP05_DOMAIN} from '@env'
|
import {MINIBITS_NIP05_DOMAIN} from '@env'
|
||||||
import { log } from '../../services/logService'
|
import { log } from '../../services/logService'
|
||||||
import { ContactListItem } from './ContactListItem'
|
import { ContactListItem } from './ContactListItem'
|
||||||
import { Contact, ContactType } from '../../models/Contact'
|
import { Contact, ContactType } from '../../models/Contact'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
|
||||||
import { ReceiveOption } from '../ReceiveScreen'
|
import { ReceiveOption } from '../ReceiveScreen'
|
||||||
import { SendOption } from '../SendScreen'
|
import { SendOption } from '../SendScreen'
|
||||||
import { infoMessage, warningMessage } from '../../utils/utils'
|
|
||||||
import { IncomingDataType, IncomingParser } from '../../services/incomingParser'
|
import { IncomingDataType, IncomingParser } from '../../services/incomingParser'
|
||||||
import { translate } from '../../i18n'
|
import { translate } from '../../i18n'
|
||||||
import { RouteProp, useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { toJS } from 'mobx'
|
import { toJS } from 'mobx'
|
||||||
import { TransferOption } from '../TransferScreen'
|
import { TransferOption } from '../TransferScreen'
|
||||||
|
|
||||||
@@ -32,11 +30,12 @@ export const PrivateContacts = observer(function (props: {
|
|||||||
const contactNameInputRef = useRef<TextInput>(null)
|
const contactNameInputRef = useRef<TextInput>(null)
|
||||||
|
|
||||||
const [info, setInfo] = useState('')
|
const [info, setInfo] = useState('')
|
||||||
const [newContactName, setNewContactName] = useState<string>('')
|
const [newContactName, setNewContactName] = useState<string>('')
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [isExternalDomain, setIsExternalDomain] = useState(false)
|
const [isExternalDomain, setIsExternalDomain] = useState(false)
|
||||||
const [isNewContactModalVisible, setIsNewContactModalVisible] = useState(false)
|
const [isNewContactModalVisible, setIsNewContactModalVisible] = useState(false)
|
||||||
const [error, setError] = useState<AppError | undefined>()
|
const [error, setError] = useState<AppError | undefined>()
|
||||||
|
const [pendingError, setPendingError] = useState<AppError | undefined>()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { paymentOption } = props
|
const { paymentOption } = props
|
||||||
@@ -127,7 +126,11 @@ export const PrivateContacts = observer(function (props: {
|
|||||||
} catch(e: any) {
|
} catch(e: any) {
|
||||||
setNewContactName('')
|
setNewContactName('')
|
||||||
setIsExternalDomain(false)
|
setIsExternalDomain(false)
|
||||||
handleError(e)
|
if (Platform.OS === 'ios') {
|
||||||
|
setPendingError(e)
|
||||||
|
} else {
|
||||||
|
handleError(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,6 +311,12 @@ export const PrivateContacts = observer(function (props: {
|
|||||||
</View>
|
</View>
|
||||||
<BottomModal
|
<BottomModal
|
||||||
isVisible={isNewContactModalVisible ? true : false}
|
isVisible={isNewContactModalVisible ? true : false}
|
||||||
|
onModalHide={() => {
|
||||||
|
if (pendingError) {
|
||||||
|
handleError(pendingError)
|
||||||
|
setPendingError(undefined)
|
||||||
|
}
|
||||||
|
}}
|
||||||
ContentComponent={
|
ContentComponent={
|
||||||
<View style={$newContainer}>
|
<View style={$newContainer}>
|
||||||
<Text tx="contactsScreen_newTitle" preset="subheading" />
|
<Text tx="contactsScreen_newTitle" preset="subheading" />
|
||||||
|
|||||||
@@ -219,11 +219,8 @@ export const PublicContactsNew = observer(function (props: {
|
|||||||
setFollowingProfiles(following)
|
setFollowingProfiles(following)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
InteractionManager.runAfterInteractions(async () => {
|
loadProfiles()
|
||||||
loadProfiles()
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
}, [followingPubkeys])
|
}, [followingPubkeys])
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export const MintsScreen = observer(function MintsScreen({ route }: Props) {
|
|||||||
const [selectedMint, setSelectedMint] = useState<Mint | undefined>()
|
const [selectedMint, setSelectedMint] = useState<Mint | undefined>()
|
||||||
const [info, setInfo] = useState('')
|
const [info, setInfo] = useState('')
|
||||||
const [error, setError] = useState<AppError | undefined>()
|
const [error, setError] = useState<AppError | undefined>()
|
||||||
|
const [pendingError, setPendingError] = useState<AppError | undefined>()
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [isAddMintVisible, setIsAddMintVisible] = useState(false)
|
const [isAddMintVisible, setIsAddMintVisible] = useState(false)
|
||||||
const [isMintMenuVisible, setIsMintMenuVisible] = useState(false)
|
const [isMintMenuVisible, setIsMintMenuVisible] = useState(false)
|
||||||
@@ -119,7 +120,11 @@ export const MintsScreen = observer(function MintsScreen({ route }: Props) {
|
|||||||
setInfo(translate('mintsScreen_mintAdded'))
|
setInfo(translate('mintsScreen_mintAdded'))
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
setMintUrl('')
|
setMintUrl('')
|
||||||
handleError(e)
|
if (Platform.OS === 'ios') {
|
||||||
|
setPendingError(e)
|
||||||
|
} else {
|
||||||
|
handleError(e)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setMintUrl('')
|
setMintUrl('')
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
@@ -425,6 +430,12 @@ export const MintsScreen = observer(function MintsScreen({ route }: Props) {
|
|||||||
/>
|
/>
|
||||||
<BottomModal
|
<BottomModal
|
||||||
isVisible={isAddMintVisible ? true : false}
|
isVisible={isAddMintVisible ? true : false}
|
||||||
|
onModalHide={() => {
|
||||||
|
if (pendingError) {
|
||||||
|
handleError(pendingError)
|
||||||
|
setPendingError(undefined)
|
||||||
|
}
|
||||||
|
}}
|
||||||
ContentComponent={
|
ContentComponent={
|
||||||
<View style={$bottomModal}>
|
<View style={$bottomModal}>
|
||||||
<Text
|
<Text
|
||||||
|
|||||||
+7
-22
@@ -27,40 +27,25 @@ export const getImageSource = function(img: string) {
|
|||||||
|
|
||||||
|
|
||||||
export const infoMessage = function(message: string, description?: string) {
|
export const infoMessage = function(message: string, description?: string) {
|
||||||
const backgroundColor = colors.palette.success300
|
const backgroundColor = colors.palette.neutral500
|
||||||
const textColor = 'white'
|
const textColor = 'white'
|
||||||
|
|
||||||
return showMessage({
|
return showMessage({
|
||||||
message,
|
message,
|
||||||
description,
|
description,
|
||||||
|
duration: 3000,
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
color: textColor,
|
color: textColor,
|
||||||
style: {
|
style: {
|
||||||
minHeight: spacing.screenHeight * 0.15,
|
minHeight: spacing.screenHeight * 0.05,
|
||||||
borderTopLeftRadius: spacing.medium,
|
borderRadius: spacing.medium,
|
||||||
borderTopRightRadius: spacing.medium
|
margin: spacing.large,
|
||||||
|
marginBottom: spacing.large * 4,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const warningMessage = function(message: string, description?: string) {
|
|
||||||
const backgroundColor = colors.palette.accent500
|
|
||||||
|
|
||||||
return showMessage({
|
|
||||||
message,
|
|
||||||
description,
|
|
||||||
duration: 3000,
|
|
||||||
backgroundColor,
|
|
||||||
color: 'white',
|
|
||||||
style: {
|
|
||||||
minHeight: spacing.screenHeight * 0.15,
|
|
||||||
borderTopLeftRadius: spacing.medium,
|
|
||||||
borderTopRightRadius: spacing.medium
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const generateId = function (lengthInBytes: number) {
|
export const generateId = function (lengthInBytes: number) {
|
||||||
const random = QuickCrypto.randomBytes(lengthInBytes)
|
const random = QuickCrypto.randomBytes(lengthInBytes)
|
||||||
const uint8Array = new Uint8Array(random)
|
const uint8Array = new Uint8Array(random)
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
name: Minibits Wallet
|
name: Minibits Wallet
|
||||||
summary: Minibits is an ecash and Lightning wallet exploring how ₿-backed ecash can enable instant, cheap, and private value transfer.
|
summary: Minibits is a Bitcoin Lightning and ecash wallet that deliver instant, low-cost, and private value transfers — even when the payer is offline.
|
||||||
repository: https://github.com/minibits-cash/minibits_wallet
|
repository: https://github.com/minibits-cash/minibits_wallet
|
||||||
homepage: https://minibits.cash/
|
homepage: https://minibits.cash/
|
||||||
assets:
|
assets:
|
||||||
|
|||||||
Reference in New Issue
Block a user