diff --git a/__tests__/LoadingApp.snapshot.tsx b/__tests__/LoadingApp.snapshot.tsx index 0c291fdbc..5515fd500 100644 --- a/__tests__/LoadingApp.snapshot.tsx +++ b/__tests__/LoadingApp.snapshot.tsx @@ -125,6 +125,7 @@ describe('Component LoadingApp - test', () => { batches: 0, date: 0, }; + const firstLaunchingMessage = false; const receive = render( { privacy={privacy} mode={mode} background={background} + firstLaunchingMessage={firstLaunchingMessage} />, ); expect(receive.toJSON()).toMatchSnapshot(); diff --git a/__tests__/__snapshots__/LoadingApp.snapshot.tsx.snap b/__tests__/__snapshots__/LoadingApp.snapshot.tsx.snap index b72faa022..94262d30e 100644 --- a/__tests__/__snapshots__/LoadingApp.snapshot.tsx.snap +++ b/__tests__/__snapshots__/LoadingApp.snapshot.tsx.snap @@ -13,37 +13,102 @@ exports[`Component LoadingApp - test LoadingApp - snapshot 1`] = ` } > - - - text traslated - - - text traslated - - + > + text traslated + + + text traslated + + + + text traslated + + + text traslated + + + text traslated + + + + `; diff --git a/android/app/build.gradle b/android/app/build.gradle index 5dd305148..c53d08a18 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -144,9 +144,9 @@ android { //applicationId 'com.ZingoMobile' // @Test minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 130 // Real + versionCode 131 // Real //versionCode 117 // @Test - versionName "zingo-1.3.2" // Real + versionName "zingo-1.3.3" // Real missingDimensionStrategy 'react-native-camera', 'general' testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/app/AppState/AppStateLoading.ts b/app/AppState/AppStateLoading.ts index 2753a4bc4..f2db5a13f 100644 --- a/app/AppState/AppStateLoading.ts +++ b/app/AppState/AppStateLoading.ts @@ -48,5 +48,7 @@ export default interface AppStateLoading { snackbars: SnackbarType[]; addLastSnackbar: (snackbar: SnackbarType) => void; + firstLaunchingMessage: boolean; + // eslint-disable-next-line semi } diff --git a/app/AppState/classes/SettingsFileClass.ts b/app/AppState/classes/SettingsFileClass.ts index 96250b280..3762fc7e1 100644 --- a/app/AppState/classes/SettingsFileClass.ts +++ b/app/AppState/classes/SettingsFileClass.ts @@ -9,6 +9,11 @@ export default class SettingsFileClass { mode: 'basic' | 'advanced'; firstInstall: boolean; basicFirstViewSeed: boolean; + version: string | null; + // three values: + // - '': means the prior version doesn't have this field in settings + // - null: means is a fresh install + // - string: means it have a normal value constructor( server: ServerType, @@ -19,6 +24,7 @@ export default class SettingsFileClass { mode: 'basic' | 'advanced', firstInstall: boolean, basicFirstViewSeed: boolean, + version: string, ) { this.server = server; this.currency = currency; @@ -28,5 +34,6 @@ export default class SettingsFileClass { this.mode = mode; this.firstInstall = firstInstall; this.basicFirstViewSeed = basicFirstViewSeed; + this.version = version; } } diff --git a/app/LoadedApp/LoadedApp.tsx b/app/LoadedApp/LoadedApp.tsx index 67fbbe1b6..9707effca 100644 --- a/app/LoadedApp/LoadedApp.tsx +++ b/app/LoadedApp/LoadedApp.tsx @@ -11,6 +11,7 @@ import { NativeEventSubscription, Platform, Linking, + SafeAreaView, } from 'react-native'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; @@ -56,6 +57,7 @@ import { createAlert } from '../createAlert'; import Snackbars from '../../components/Components/Snackbars'; import SnackbarType from '../AppState/types/SnackbarType'; import { RPCSeedType } from '../rpc/types/RPCSeedType'; +import { Launching } from '../LoadingApp'; const History = React.lazy(() => import('../../components/History')); const Send = React.lazy(() => import('../../components/Send')); @@ -78,6 +80,9 @@ const es = require('../translations/es.json'); const Tab = createBottomTabNavigator(); +// for testing +//const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + type LoadedAppProps = { navigation: StackScreenProps['navigation']; route: StackScreenProps['route']; @@ -120,8 +125,18 @@ export default function LoadedApp(props: LoadedAppProps) { // update layout direction I18nManager.forceRTL(isRTL); + // If the App is mounting this component, I know I have to reset the firstInstall prop in settings. + await SettingsFileImpl.writeSettings('firstInstall', false); + + // If the App is mounting this component, I know I have to update the version prop in settings. + await SettingsFileImpl.writeSettings('version', translate('version') as string); + //I have to check what language is in the settings const settings = await SettingsFileImpl.readSettings(); + + // for testing + //await delay(5000); + if (settings.language) { setLanguage(settings.language); i18n.locale = settings.language; @@ -180,16 +195,15 @@ export default function LoadedApp(props: LoadedAppProps) { if (loading) { return ( - - {translate('zingo') as string} - {translate('version') as string} - + + ); } else { return ( @@ -274,14 +288,11 @@ export class LoadedAppClass extends Component { this.clearToAddr(); - // If the App is mounting this component, I know I have to reset the firstInstall props in settings. - (async () => { - await SettingsFileImpl.writeSettings('firstInstall', false); - })(); - - // Configure the RPC to start doing refreshes (async () => { + // Configure the RPC to start doing refreshes await this.rpc.configure(); + + //console.log(await SettingsFileImpl.readSettings()); })(); this.appstate = AppState.addEventListener('change', async nextAppState => { @@ -1149,7 +1160,7 @@ export class LoadedAppClass extends Component diff --git a/app/LoadingApp/Launching.tsx b/app/LoadingApp/Launching.tsx new file mode 100644 index 000000000..dc98a8504 --- /dev/null +++ b/app/LoadingApp/Launching.tsx @@ -0,0 +1,95 @@ +/* eslint-disable react-native/no-inline-styles */ +import React from 'react'; +import { SafeAreaView, Text, View, ActivityIndicator } from 'react-native'; +import { useTheme } from '@react-navigation/native'; + +import { ThemeType } from '../types'; +import { TranslateType } from '../AppState'; + +type LaunchingProps = { + translate: (key: string) => TranslateType; + firstLaunchingMessage: boolean; + message?: string; +}; + +const Launching: React.FunctionComponent = props => { + const { colors } = useTheme() as unknown as ThemeType; + + return ( + + + + {props.translate('zingo') as string} + + {props.translate('version') as string} + + {!!props.message && ( + + {props.message} + + )} + {props.firstLaunchingMessage && } + + {props.translate('firstlaunchingmessage-title') as string} + + + {props.translate('firstlaunchingmessage-body') as string} + + + {props.translate('firstlaunchingmessage-footer') as string} + + + + + ); +}; + +export default Launching; diff --git a/app/LoadingApp/LoadingApp.tsx b/app/LoadingApp/LoadingApp.tsx index 49d58f944..6d5795d8a 100644 --- a/app/LoadingApp/LoadingApp.tsx +++ b/app/LoadingApp/LoadingApp.tsx @@ -41,6 +41,7 @@ import { isEqual } from 'lodash'; import Snackbars from '../../components/Components/Snackbars'; import SnackbarType from '../AppState/types/SnackbarType'; import { RPCSeedType } from '../rpc/types/RPCSeedType'; +import Launching from './Launching'; const BoldText = React.lazy(() => import('../../components/Components/BoldText')); const Button = React.lazy(() => import('../../components/Components/Button')); @@ -51,6 +52,9 @@ const ChainTypeToggle = React.lazy(() => import('../../components/Components/Cha const en = require('../translations/en.json'); const es = require('../translations/es.json'); +// for testing +//const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + type LoadingAppProps = { navigation: StackScreenProps['navigation']; route: StackScreenProps['route']; @@ -68,6 +72,7 @@ export default function LoadingApp(props: LoadingAppProps) { const [privacy, setPrivacy] = useState(false); const [mode, setMode] = useState<'basic' | 'advanced'>('advanced'); // by default advanced const [background, setBackground] = useState({ batches: 0, date: 0 }); + const [firstLaunchingMessage, setFirstLaunchingMessage] = useState(false); const [loading, setLoading] = useState(true); const file = useMemo( () => ({ @@ -93,12 +98,23 @@ export default function LoadingApp(props: LoadingAppProps) { // update layout direction I18nManager.forceRTL(isRTL); - //I have to check what language is in the settings + //I have to check what language and other things are in the settings const settings = await SettingsFileImpl.readSettings(); //console.log(settings); + // checking the version of the App in settings + //console.log('versions, old:', settings.version, ' new:', translate('version') as string); + if (settings.version === null) { + // this is a fresh install + setFirstLaunchingMessage(false); + } else if (settings.version === '' || settings.version !== (translate('version') as string)) { + // this is an update + setFirstLaunchingMessage(true); + } + // first I need to know if this launch is a fresh install... // if firstInstall is true -> 100% is the first time. + //console.log('first install', settings.firstInstall); if (settings.firstInstall) { // basic mode setMode('basic'); @@ -148,6 +164,9 @@ export default function LoadingApp(props: LoadingAppProps) { await SettingsFileImpl.writeSettings('privacy', privacy); } + // for testing + //await delay(5000); + // reading background task info if (Platform.OS === 'ios') { // this file only exists in IOS BS. @@ -161,20 +180,19 @@ export default function LoadingApp(props: LoadingAppProps) { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - //console.log('render loadingApp - 2'); + //console.log('render loadingApp - 2', translate('version')); if (loading) { return ( - - {translate('zingo') as string} - {translate('version') as string} - + + ); } else { return ( @@ -189,6 +207,7 @@ export default function LoadingApp(props: LoadingAppProps) { privacy={privacy} mode={mode} background={background} + firstLaunchingMessage={firstLaunchingMessage} /> ); } @@ -206,6 +225,7 @@ type LoadingAppClassProps = { privacy: boolean; mode: 'basic' | 'advanced'; background: BackgroundType; + firstLaunchingMessage: boolean; }; export class LoadingAppClass extends Component { @@ -244,6 +264,7 @@ export class LoadingAppClass extends Component { + this.setState({ actionButtonsDisabled: true }); (async () => { // First, check if a wallet exists. Do it async so the basic screen has time to render await AsyncStorage.setItem('@background', 'no'); @@ -264,6 +286,9 @@ export class LoadingAppClass extends Component ({ screen: state.screen === 3 ? 3 : 1, walletExists: false })); + this.setState(state => ({ + screen: state.screen === 3 ? 3 : 1, + walletExists: false, + actionButtonsDisabled: false, + })); } } })(); @@ -639,11 +670,12 @@ export class LoadingAppClass extends Component @@ -657,20 +689,7 @@ export class LoadingAppClass extends Component - {screen === 0 && ( - - - {translate('zingo') as string} - - {translate('version') as string} - - )} + {screen === 0 && } {screen === 1 && ( <> - {netInfo.isConnected && ( + {netInfo.isConnected && !actionButtonsDisabled && ( <> {mode === 'basic' ? ( {}, + firstLaunchingMessage: false, }; export const ContextAppLoading = React.createContext(defaultAppStateLoading); diff --git a/app/translations/en.json b/app/translations/en.json index 06aaad369..2cdf17bc2 100644 --- a/app/translations/en.json +++ b/app/translations/en.json @@ -1,10 +1,13 @@ { "zingo": "Zingo!", - "version": "zingo-1.3.2 (130)", + "version": "zingo-1.3.3 (131)", "loading": "loading...", "connectingserver": "Connecting to the server...", "wait": "Please wait...", "workingonit": "We are working on this feature.", + "firstlaunchingmessage-title": "Optimizing Zingo!", + "firstlaunchingmessage-body": "This may take a while, depending on the transaction history. Please keep the app open until the process finishes.", + "firstlaunchingmessage-footer": "Sorry for the inconvenience.", "close": "Close", "cancel": "Cancel", "save": "Save", diff --git a/app/translations/es.json b/app/translations/es.json index 791f8c783..ff15d6d7d 100644 --- a/app/translations/es.json +++ b/app/translations/es.json @@ -1,10 +1,13 @@ { "zingo": "Zingo!", - "version": "zingo-1.3.2 (130)", + "version": "zingo-1.3.3 (131)", "loading": "cargando...", "connectingserver": "Conectando con el servidor...", "wait": "Por favor espere...", "workingonit": "Estamos trabajando en esta funcionalidad", + "firstlaunchingmessage-title": "¡Optimizando Zingo!", + "firstlaunchingmessage-body": "Esto puede tardar un poco, dependiendo del historial de transacciones. Mantenga la aplicación abierta hasta que finalice el proceso.", + "firstlaunchingmessage-footer": "Disculpe las molestias.", "close": "Cerrar", "cancel": "Cancelar", "save": "Grabar", @@ -33,7 +36,7 @@ "createnewwallet": "Crear Monedero Nuevo\n(nueva semilla)", "opencurrentwallet": "Abrir Monedero Activo", "restorewalletseed": "Restaurar Monedero\ncon la Semilla", - "restorewalletufvk": "Restaurar Monedero con\nla Clave de Visualización", + "restorewalletufvk": "Restaurar Monedero\ncon la Clave de Visualización", "creatingwallet-label": "Error Creando el Monedero", "readingwallet-label": "Error Leyendo el Monedero", "invalidseed-label": "Semilla incorrecta", diff --git a/components/Settings/SettingsFileImpl.ts b/components/Settings/SettingsFileImpl.ts index 6dadab982..dcef5424d 100644 --- a/components/Settings/SettingsFileImpl.ts +++ b/components/Settings/SettingsFileImpl.ts @@ -11,7 +11,16 @@ export default class SettingsFileImpl { // Write the server setting static async writeSettings( - name: 'server' | 'currency' | 'language' | 'sendAll' | 'privacy' | 'mode' | 'firstInstall' | 'basicFirstViewSeed', + name: + | 'server' + | 'currency' + | 'language' + | 'sendAll' + | 'privacy' + | 'mode' + | 'firstInstall' + | 'basicFirstViewSeed' + | 'version', value: string | boolean | ServerType, ) { const fileName = await this.getFileName(); @@ -61,12 +70,17 @@ export default class SettingsFileImpl { // this means when the user have funds, the seed screen will show up. settings.basicFirstViewSeed = true; } + if (!settings.hasOwnProperty('version')) { + // here we know the user is updating the App, for sure. + // from some version before. + settings.version = ''; + } return settings; } catch (err) { // The File doesn't exist, so return nothing // Here I know 100% it is a fresh install or the user cleaned the device staorage - //console.log('settings read file:', err); - const settings: SettingsFileClass = { firstInstall: true } as SettingsFileClass; + console.log('settings read file:', err); + const settings: SettingsFileClass = { firstInstall: true, version: null } as SettingsFileClass; return settings; } } diff --git a/ios/ZingoMobile.xcodeproj/project.pbxproj b/ios/ZingoMobile.xcodeproj/project.pbxproj index 5e25ed931..3c53c5f3a 100644 --- a/ios/ZingoMobile.xcodeproj/project.pbxproj +++ b/ios/ZingoMobile.xcodeproj/project.pbxproj @@ -521,7 +521,7 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ZingoMobile/ZingoMobile.entitlements; - CURRENT_PROJECT_VERSION = 130; + CURRENT_PROJECT_VERSION = 131; DEVELOPMENT_TEAM = 788KRST4S8; ENABLE_BITCODE = NO; EXCLUDED_ARCHS = ""; @@ -538,7 +538,7 @@ "$(PROJECT_DIR)", "$(inherited)", ); - MARKETING_VERSION = 1.3.2; + MARKETING_VERSION = 1.3.3; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -561,7 +561,7 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ZingoMobile/ZingoMobile.entitlements; - CURRENT_PROJECT_VERSION = 130; + CURRENT_PROJECT_VERSION = 131; DEVELOPMENT_TEAM = 788KRST4S8; ENABLE_BITCODE = NO; EXCLUDED_ARCHS = ""; @@ -578,7 +578,7 @@ "$(PROJECT_DIR)", "$(inherited)", ); - MARKETING_VERSION = 1.3.2; + MARKETING_VERSION = 1.3.3; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC",