mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-10-05 19:28:26 +00:00
294 lines
10 KiB
TypeScript
294 lines
10 KiB
TypeScript
import {observer} from 'mobx-react-lite'
|
|
import React, {FC, useEffect, useState} from 'react'
|
|
import {Linking, Platform, ScrollView, Switch, TextStyle, View, ViewStyle} from 'react-native'
|
|
import {
|
|
APP_ENV,
|
|
} from '@env'
|
|
// import codePush from "react-native-code-push"
|
|
import {colors, spacing, useThemeColor} from '../theme'
|
|
import {
|
|
Icon,
|
|
ListItem,
|
|
Screen,
|
|
Text,
|
|
Card,
|
|
Loading,
|
|
ErrorModal,
|
|
InfoModal,
|
|
Button,
|
|
BottomModal,
|
|
} from '../components'
|
|
import {useHeader} from '../utils/useHeader'
|
|
import AppError, { Err } from '../utils/AppError'
|
|
import { log } from '../services'
|
|
import {Env} from '../utils/envtypes'
|
|
import { CommonActions, StaticScreenProps, useNavigation } from '@react-navigation/native'
|
|
import { translate } from '../i18n'
|
|
import { HotUpdater, useHotUpdaterStore } from '@hot-updater/react-native'
|
|
import { ResultModalInfo } from './Wallet/ResultModalInfo'
|
|
|
|
|
|
type Props = StaticScreenProps<{
|
|
isNativeUpdateAvailable: boolean,
|
|
isUpdateAvailable: boolean,
|
|
updateDescription: string,
|
|
updateSize: string
|
|
prevScreen: 'Settings' | 'Wallet'
|
|
}>
|
|
|
|
export const UpdateScreen = observer(function UpdateScreen({ route }: Props) {
|
|
const navigation = useNavigation()
|
|
const {
|
|
isUpdateAvailable,
|
|
isNativeUpdateAvailable,
|
|
updateDescription,
|
|
updateSize,
|
|
prevScreen
|
|
} = route.params
|
|
|
|
// Select only the rounded percentage (0-100) instead of the raw progress float.
|
|
// The native download fires onProgress events very rapidly; subscribing to the raw
|
|
// value re-renders this screen (and its Portal-hosted modal) on every tick, faster
|
|
// than the JS thread can commit. useSyncExternalStore then forces nested sync
|
|
// re-renders to stay consistent and trips "Maximum update depth exceeded". Rounding
|
|
// here means a re-render only when the integer percent actually changes.
|
|
const progressPercent = useHotUpdaterStore(state => Math.round(state.progress * 100))
|
|
|
|
useHeader({
|
|
leftIcon: 'faArrowLeft',
|
|
onLeftPress: () => {
|
|
//@ts-ignore
|
|
navigation.setParams({isUpdateAvailable: undefined})
|
|
//@ts-ignore
|
|
navigation.setParams({isNativeUpdateAvailable: undefined})
|
|
//@ts-ignore
|
|
navigation.setParams({updateDescription: undefined})
|
|
//@ts-ignore
|
|
navigation.setParams({updateSize: undefined})
|
|
|
|
if(prevScreen === 'Settings') {
|
|
navigation.goBack()
|
|
} else {
|
|
navigation.dispatch(
|
|
CommonActions.reset({
|
|
index: 1,
|
|
routes: [{
|
|
name: 'WalletNavigator'
|
|
}]
|
|
})
|
|
)
|
|
}
|
|
},
|
|
})
|
|
|
|
const [info, setInfo] = useState('')
|
|
const [isLoading, setIsLoading] = useState(false)
|
|
const [isUpdateModalVisible, setIsUpdateModalVisible] = useState(false)
|
|
const [error, setError] = useState<AppError | undefined>()
|
|
|
|
const toggleUpdateModal = () => {
|
|
setIsUpdateModalVisible(previousState => !previousState)
|
|
}
|
|
|
|
const handleUpdate = async function () {
|
|
try {
|
|
setIsUpdateModalVisible(true)
|
|
const updateInfo = await HotUpdater.checkForUpdate({
|
|
updateStrategy: "fingerprint",
|
|
})
|
|
|
|
if (!updateInfo) {
|
|
throw new AppError(Err.NETWORK_ERROR, translate('updateScreen_couldNotRetrieveUpdate'))
|
|
}
|
|
|
|
const isDownloaded = await updateInfo.updateBundle()
|
|
if(isDownloaded) {
|
|
await HotUpdater.reload()
|
|
} else {
|
|
throw new AppError(Err.NETWORK_ERROR, 'Could not download update bundle')
|
|
}
|
|
|
|
} catch(e: any) {
|
|
handleError(e)
|
|
}
|
|
}
|
|
|
|
|
|
const gotoPlayStore = () => {
|
|
const packageName = 'com.minibits_wallet'; // Replace with your app's package name
|
|
|
|
// Determine the URL based on the user's platform (Android or iOS)
|
|
let url = ''
|
|
if (Platform.OS === 'android') {
|
|
url = `market://details?id=${packageName}`
|
|
} else if (Platform.OS === 'ios') {
|
|
url = `itms-apps://itunes.apple.com/app/${packageName}`
|
|
}
|
|
|
|
// Open the URL in the device's default app for handling URLs (e.g., Play Store)
|
|
Linking.openURL(url)
|
|
}
|
|
|
|
const gotoGithub = () => {
|
|
Linking.openURL('https://github.com/minibits-cash/minibits_wallet/releases')
|
|
}
|
|
|
|
|
|
const handleError = function (e: AppError): void {
|
|
setIsLoading(false)
|
|
setIsUpdateModalVisible(false)
|
|
setError(e)
|
|
}
|
|
|
|
const headerBg = useThemeColor('header')
|
|
const headerTitle = useThemeColor('headerTitle')
|
|
|
|
return (
|
|
<Screen contentContainerStyle={$screen} preset='fixed'>
|
|
<View style={[$headerContainer, {backgroundColor: headerBg}]}>
|
|
<Text preset="heading" tx="updateScreen_updateManagerTitle" style={{color: headerTitle}} />
|
|
</View>
|
|
<ScrollView style={$contentContainer}>
|
|
<Card
|
|
style={$card}
|
|
ContentComponent={
|
|
<>
|
|
{isUpdateAvailable && (
|
|
<>
|
|
<ListItem
|
|
tx='updateScreen_updateAvailable'
|
|
subTx='updateScreen_updateAvailableDesc'
|
|
leftIcon='faWandMagicSparkles'
|
|
leftIconColor={colors.palette.iconMagenta200}
|
|
leftIconInverse={true}
|
|
style={$item}
|
|
/>
|
|
<ListItem
|
|
tx='updateScreen_updateNew'
|
|
subText={updateDescription || translate("updateScreen_defaultUpdateDesc")}
|
|
leftIcon='faInfoCircle'
|
|
leftIconColor={colors.palette.neutral500}
|
|
topSeparator={true}
|
|
style={$item}
|
|
/>
|
|
{/*<ListItem
|
|
tx='updateScreen_updateSize'
|
|
subText={updateSize || '1.0MB'}
|
|
leftIcon='faDownload'
|
|
leftIconColor={colors.palette.neutral500}
|
|
topSeparator={true}
|
|
style={$item}
|
|
/>*/}
|
|
</>
|
|
)}
|
|
{isNativeUpdateAvailable && (
|
|
<ListItem
|
|
tx='updateScreen_updateAvailable'
|
|
subTx={'updateScreen_nativeUpdateAvailableDesc'}
|
|
leftIcon='faWandMagicSparkles'
|
|
leftIconColor={colors.palette.iconMagenta200}
|
|
leftIconInverse={true}
|
|
style={$item}
|
|
/>)}
|
|
{(!isUpdateAvailable && !isNativeUpdateAvailable) && (
|
|
<ListItem
|
|
tx='updateScreen_updateNotAvailable'
|
|
subTx={'updateScreen_updateNotAvailableDesc'}
|
|
leftIcon='faInfoCircle'
|
|
leftIconColor={colors.palette.neutral500}
|
|
style={$item}
|
|
/>
|
|
)}
|
|
</>
|
|
}
|
|
FooterComponent={
|
|
<>
|
|
{isUpdateAvailable && (
|
|
<Button
|
|
onPress={handleUpdate}
|
|
tx='updateScreen_updateNow'
|
|
style={{alignSelf: 'center', marginTop: spacing.medium}}
|
|
/>
|
|
)}
|
|
{isNativeUpdateAvailable && (
|
|
<View style={$buttonContainer}>
|
|
<Button
|
|
onPress={gotoPlayStore}
|
|
tx='updateScreen_gotoPlayStore'
|
|
style={{marginTop: spacing.medium, marginRight: spacing.small}}
|
|
/>
|
|
<Button
|
|
onPress={gotoGithub}
|
|
tx="updateScreen_apkOnGithub"
|
|
preset='secondary'
|
|
style={{marginTop: spacing.medium}}
|
|
/>
|
|
</View>
|
|
)}
|
|
</>
|
|
}
|
|
/>
|
|
{isLoading && <Loading />}
|
|
</ScrollView>
|
|
<BottomModal
|
|
isVisible={isUpdateModalVisible ? true : false}
|
|
style={{alignItems: 'stretch'}}
|
|
ContentComponent={
|
|
<ResultModalInfo
|
|
icon='faDownload'
|
|
iconColor={colors.palette.accent400}
|
|
title={`${progressPercent}%`}
|
|
message={translate('updateScreen_updateInProgress')}
|
|
/>
|
|
}
|
|
onBackButtonPress={toggleUpdateModal}
|
|
onBackdropPress={toggleUpdateModal}
|
|
/>
|
|
{error && <ErrorModal error={error} />}
|
|
{info && <InfoModal message={info} />}
|
|
</Screen>
|
|
)
|
|
})
|
|
|
|
const $screen: ViewStyle = {}
|
|
|
|
const $headerContainer: TextStyle = {
|
|
alignItems: 'center',
|
|
paddingBottom: spacing.medium,
|
|
height: spacing.screenHeight * 0.15,
|
|
}
|
|
|
|
const $buttonContainer: ViewStyle = {
|
|
flexDirection: 'row',
|
|
alignSelf: 'center',
|
|
marginTop: spacing.medium,
|
|
}
|
|
|
|
const $contentContainer: TextStyle = {
|
|
flex: 1,
|
|
marginTop: -spacing.extraLarge * 2,
|
|
padding: spacing.extraSmall,
|
|
}
|
|
|
|
const $card: ViewStyle = {
|
|
marginBottom: 0,
|
|
}
|
|
|
|
const $bottomModal: ViewStyle = {
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
paddingVertical: spacing.large,
|
|
paddingHorizontal: spacing.small,
|
|
}
|
|
|
|
const $item: ViewStyle = {
|
|
paddingHorizontal: spacing.small,
|
|
paddingLeft: 0,
|
|
}
|
|
|
|
const $rightContainer: ViewStyle = {
|
|
// padding: spacing.extraSmall,
|
|
alignSelf: 'center',
|
|
//marginLeft: spacing.small,
|
|
}
|