From 988e4b1bd3f6e2be6926fd9cfa5ab3b32db694ee Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Fri, 25 Apr 2025 10:50:16 +0500 Subject: [PATCH 01/81] Implemented the Personal Information screen for the business account flow --- .../PersonalInformation.tsx | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 app/screens/business-account-flow/PersonalInformation.tsx diff --git a/app/screens/business-account-flow/PersonalInformation.tsx b/app/screens/business-account-flow/PersonalInformation.tsx new file mode 100644 index 000000000..704d2b2cf --- /dev/null +++ b/app/screens/business-account-flow/PersonalInformation.tsx @@ -0,0 +1,69 @@ +import React from "react" +import { View } from "react-native" +import { makeStyles } from "@rneui/themed" +import { StackScreenProps } from "@react-navigation/stack" +import { RootStackParamList } from "@app/navigation/stack-param-lists" + +// components +import { Screen } from "@app/components/screen" +import { PrimaryBtn } from "@app/components/buttons" +import { InputField, PhoneNumber } from "@app/components/business-account-flow" + +// store +import { useAppDispatch, useAppSelector } from "@app/store/redux" +import { setPersonalInfo } from "@app/store/redux/slices/businessAccountSlice" + +type Props = StackScreenProps + +const PersonalInformation: React.FC = ({ navigation }) => { + const styles = useStyles() + + const dispatch = useAppDispatch() + const { fullName, countryCode, phoneNumber, email } = useAppSelector( + (state) => state.businessAccount.personalInfo, + ) + + const onPressNext = () => { + navigation.navigate("BusinessInformation") + } + + return ( + + + dispatch(setPersonalInfo({ fullName: val }))} + /> + dispatch(setPersonalInfo({ countryCode: val }))} + setPhoneNumber={(val) => dispatch(setPersonalInfo({ phoneNumber: val }))} + /> + dispatch(setPersonalInfo({ email: val }))} + /> + + + + ) +} + +export default PersonalInformation + +const useStyles = makeStyles(({ colors }) => ({ + container: { + flex: 1, + paddingVertical: 10, + paddingHorizontal: 20, + }, + btn: { + marginBottom: 10, + marginHorizontal: 20, + }, +})) From 99ae190927a271c6350f7ac4bfa4f6c022b145f9 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Fri, 25 Apr 2025 10:52:07 +0500 Subject: [PATCH 02/81] Created reusable InputField and PhoneNumber components, and implemented the Business Information screen for the business account flow. --- .../business-account-flow/InputField.tsx | 39 +++++++ .../business-account-flow/PhoneNumber.tsx | 106 ++++++++++++++++++ .../BusinessInformation.tsx | 64 +++++++++++ 3 files changed, 209 insertions(+) create mode 100644 app/components/business-account-flow/InputField.tsx create mode 100644 app/components/business-account-flow/PhoneNumber.tsx create mode 100644 app/screens/business-account-flow/BusinessInformation.tsx diff --git a/app/components/business-account-flow/InputField.tsx b/app/components/business-account-flow/InputField.tsx new file mode 100644 index 000000000..0c9f45295 --- /dev/null +++ b/app/components/business-account-flow/InputField.tsx @@ -0,0 +1,39 @@ +import React from "react" +import { makeStyles, Text } from "@rneui/themed" +import { TextInput, TextInputProps, View } from "react-native" + +type Props = { + label: string +} & TextInputProps + +const InputField: React.FC = ({ label, placeholder, value, onChangeText }) => { + const styles = useStyles() + + return ( + + + {label} + + + + ) +} + +export default InputField + +const useStyles = makeStyles(({ colors }) => ({ + input: { + padding: 15, + marginTop: 5, + marginBottom: 15, + borderRadius: 10, + backgroundColor: colors.grey5, + fontSize: 16, + fontFamily: "Sora-Regular", + }, +})) diff --git a/app/components/business-account-flow/PhoneNumber.tsx b/app/components/business-account-flow/PhoneNumber.tsx new file mode 100644 index 000000000..7818efb28 --- /dev/null +++ b/app/components/business-account-flow/PhoneNumber.tsx @@ -0,0 +1,106 @@ +import React from "react" +import { TextInput, View } from "react-native" +import { makeStyles, useTheme, Text } from "@rneui/themed" +import { TouchableOpacity } from "react-native-gesture-handler" +import { FlagButtonProps } from "react-native-country-picker-modal/lib/FlagButton" +import { + CountryCode as PhoneNumberCountryCode, + getCountryCallingCode, +} from "libphonenumber-js/mobile" +import CountryPicker, { + CountryCode, + DARK_THEME, + DEFAULT_THEME, + Flag, +} from "react-native-country-picker-modal" + +// hooks +import { useI18nContext } from "@app/i18n/i18n-react" +import { useRequestPhoneCodeLogin } from "@app/screens/phone-auth-screen/request-phone-code-login" + +type Props = { + countryCode: string + phoneNumber: string + setPhoneNumber: (number: string) => void + setCountryCode: (countryCode: PhoneNumberCountryCode) => void +} + +const PhoneNumber: React.FC = ({ + countryCode, + phoneNumber, + setPhoneNumber, + setCountryCode, +}) => { + const styles = useStyles() + const { mode } = useTheme().theme + const { LL } = useI18nContext() + const { supportedCountries } = useRequestPhoneCodeLogin() + + const renderCountryCode = ({ countryCode, onOpen }: FlagButtonProps) => { + return ( + countryCode && ( + + + + +{getCountryCallingCode(countryCode as PhoneNumberCountryCode)} + + + ) + ) + } + + return ( + + + Phone Number + + + setCountryCode(country.cca2 as PhoneNumberCountryCode)} + renderFlagButton={renderCountryCode} + withCallingCodeButton={true} + withFilter={true} + filterProps={{ autoFocus: true }} + withCallingCode={true} + /> + + + + ) +} + +export default PhoneNumber + +const useStyles = makeStyles(({ colors }) => ({ + container: { + flexDirection: "row", + marginTop: 5, + }, + countryPicker: { + padding: 14.5, + borderRadius: 10, + backgroundColor: colors.grey5, + flexDirection: "row", + alignItems: "center", + }, + input: { + flex: 1, + padding: 15, + marginBottom: 15, + borderRadius: 10, + backgroundColor: colors.grey5, + fontSize: 16, + fontFamily: "Sora-Regular", + marginLeft: 10, + }, +})) diff --git a/app/screens/business-account-flow/BusinessInformation.tsx b/app/screens/business-account-flow/BusinessInformation.tsx new file mode 100644 index 000000000..3c2bb48f8 --- /dev/null +++ b/app/screens/business-account-flow/BusinessInformation.tsx @@ -0,0 +1,64 @@ +import React, { useState } from "react" +import { View } from "react-native" +import { makeStyles } from "@rneui/themed" +import { StackScreenProps } from "@react-navigation/stack" +import { RootStackParamList } from "@app/navigation/stack-param-lists" + +// components +import { Screen } from "@app/components/screen" +import { PrimaryBtn } from "@app/components/buttons" +import { InputField } from "@app/components/business-account-flow" + +// store +import { useAppDispatch, useAppSelector } from "@app/store/redux" +import { setBusinessInfo } from "@app/store/redux/slices/businessAccountSlice" + +type Props = StackScreenProps + +const BusinessInformation: React.FC = ({ navigation }) => { + const styles = useStyles() + + const dispatch = useAppDispatch() + const { businessName, businessAddress } = useAppSelector( + (state) => state.businessAccount.businessInfo, + ) + + const onPressNext = () => { + navigation.navigate("BankInformation") + } + + return ( + + + dispatch(setBusinessInfo({ businessName: val }))} + /> + + dispatch(setBusinessInfo({ businessAddress: val }))} + /> + + + + ) +} + +export default BusinessInformation + +const useStyles = makeStyles(({ colors }) => ({ + container: { + flex: 1, + paddingVertical: 10, + paddingHorizontal: 20, + }, + btn: { + marginBottom: 10, + marginHorizontal: 20, + }, +})) From 78e8b2d678fcba2ce2042218208ae49593eff721 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Fri, 25 Apr 2025 10:54:28 +0500 Subject: [PATCH 03/81] created reusable DropDownField and PhotoUploadField components --- .../business-account-flow/DropDownField.tsx | 58 +++++++++++++++++++ .../PhotoUploadField.tsx | 57 ++++++++++++++++++ app/components/business-account-flow/index.ts | 6 ++ package.json | 1 + yarn.lock | 7 +++ 5 files changed, 129 insertions(+) create mode 100644 app/components/business-account-flow/DropDownField.tsx create mode 100644 app/components/business-account-flow/PhotoUploadField.tsx create mode 100644 app/components/business-account-flow/index.ts diff --git a/app/components/business-account-flow/DropDownField.tsx b/app/components/business-account-flow/DropDownField.tsx new file mode 100644 index 000000000..86cd1a66d --- /dev/null +++ b/app/components/business-account-flow/DropDownField.tsx @@ -0,0 +1,58 @@ +import React from "react" +import { View } from "react-native" +import { makeStyles, Text } from "@rneui/themed" +import { Dropdown } from "react-native-element-dropdown" + +type Props = { + label: string + placeholder: string + data: any[] + value: string + onChange: (val: string) => void +} + +const DropDownField: React.FC = ({ + label, + placeholder, + data, + value, + onChange, +}) => { + const styles = useStyles() + return ( + + + {label} + + onChange(item.value)} + /> + + ) +} + +export default DropDownField + +const useStyles = makeStyles(({ colors }) => ({ + dropdown: { + padding: 15, + marginTop: 5, + marginBottom: 15, + borderRadius: 10, + backgroundColor: colors.grey5, + fontSize: 16, + fontFamily: "Sora-Regular", + }, + container: { + marginTop: 5, + }, +})) diff --git a/app/components/business-account-flow/PhotoUploadField.tsx b/app/components/business-account-flow/PhotoUploadField.tsx new file mode 100644 index 000000000..d504ad050 --- /dev/null +++ b/app/components/business-account-flow/PhotoUploadField.tsx @@ -0,0 +1,57 @@ +import React from "react" +import { Alert, TouchableOpacity, View } from "react-native" +import { Icon, makeStyles, Text, useTheme } from "@rneui/themed" +import { launchImageLibrary } from "react-native-image-picker" +import { toastShow } from "@app/utils/toast" + +type Props = { + label: string +} + +const PhotoUploadField: React.FC = ({ label }) => { + const styles = useStyles() + const { colors } = useTheme().theme + + const showImagePicker = async () => { + try { + const result = await launchImageLibrary({ mediaType: "photo" }) + if (result.errorCode === "permission") { + toastShow({ + message: (translations) => + translations.ScanningQRCodeScreen.imageLibraryPermissionsNotGranted(), + }) + } + console.log(">>>>>>>>>>>>>>", result) + } catch (err: unknown) { + if (err instanceof Error) { + Alert.alert(err.toString()) + } + } + } + + return ( + + + {label} + + + + + + ) +} + +export default PhotoUploadField + +const useStyles = makeStyles(({ colors }) => ({ + container: { + width: "100%", + height: 150, + alignItems: "center", + justifyContent: "center", + marginTop: 5, + marginBottom: 15, + borderRadius: 10, + backgroundColor: colors.grey5, + }, +})) diff --git a/app/components/business-account-flow/index.ts b/app/components/business-account-flow/index.ts new file mode 100644 index 000000000..6b0fb221e --- /dev/null +++ b/app/components/business-account-flow/index.ts @@ -0,0 +1,6 @@ +import PhoneNumber from "./PhoneNumber" +import InputField from "./InputField" +import DropDownField from "./DropDownField" +import PhotoUploadField from "./PhotoUploadField" + +export { PhoneNumber, InputField, DropDownField, PhotoUploadField } diff --git a/package.json b/package.json index eade69e6e..166e3bcd9 100644 --- a/package.json +++ b/package.json @@ -137,6 +137,7 @@ "react-native-device-info": "^10.11.0", "react-native-document-picker": "^9.0.1", "react-native-dotenv": "^3.4.9", + "react-native-element-dropdown": "^2.12.4", "react-native-error-boundary": "^1.2.3", "react-native-file-viewer": "^2.1.5", "react-native-fingerprint-scanner": "git+https://github.com/hieuvp/react-native-fingerprint-scanner.git#9cecc0db326471c571553ea85f7c016fee2f803d", diff --git a/yarn.lock b/yarn.lock index e9f3afdd7..37e121161 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21214,6 +21214,13 @@ react-native-dotenv@^3.4.9: dependencies: dotenv "^16.4.5" +react-native-element-dropdown@^2.12.4: + version "2.12.4" + resolved "https://registry.yarnpkg.com/react-native-element-dropdown/-/react-native-element-dropdown-2.12.4.tgz#259db84e673b9f4fc03090a54760efd4f4a1456c" + integrity sha512-abZc5SVji9FIt7fjojRYrbuvp03CoeZJrgvezQoDoSOrpiTqkX69ix5m+j06W2AVncA0VWvbT+vCMam8SoVadw== + dependencies: + lodash "^4.17.21" + react-native-error-boundary@^1.2.3: version "1.2.9" resolved "https://registry.yarnpkg.com/react-native-error-boundary/-/react-native-error-boundary-1.2.9.tgz#ac504db7c0eeb95ec06a22d50de3674059a651bd" From 04f3ad013136ef995dee88f565b7d68843a6249b Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Fri, 25 Apr 2025 10:56:40 +0500 Subject: [PATCH 04/81] implemented Bank Information screen --- .../business-account-flow/BankInformation.tsx | 97 +++++++++++++++++++ app/screens/business-account-flow/index.ts | 5 + 2 files changed, 102 insertions(+) create mode 100644 app/screens/business-account-flow/BankInformation.tsx create mode 100644 app/screens/business-account-flow/index.ts diff --git a/app/screens/business-account-flow/BankInformation.tsx b/app/screens/business-account-flow/BankInformation.tsx new file mode 100644 index 000000000..36f3d7dd6 --- /dev/null +++ b/app/screens/business-account-flow/BankInformation.tsx @@ -0,0 +1,97 @@ +import React from "react" +import { ScrollView } from "react-native" +import { makeStyles } from "@rneui/themed" + +// components +import { + DropDownField, + InputField, + PhotoUploadField, +} from "@app/components/business-account-flow" +import { Screen } from "@app/components/screen" +import { PrimaryBtn } from "@app/components/buttons" + +// store +import { useAppDispatch, useAppSelector } from "@app/store/redux" +import { setBankInfo } from "@app/store/redux/slices/businessAccountSlice" + +const accountTypes = [ + { label: "Select account type", value: null }, + { label: "Checking", value: "checking" }, + { label: "Savings", value: "savings" }, +] + +const currencies = [ + { label: "Select currency", value: null }, + { label: "USD - US Dollar", value: "usd" }, + { label: "EUR - Euro", value: "eur" }, + { label: "JMD - Jamaican Dollar", value: "jmd" }, + { label: "KYD - Cayman Islands Dollar", value: "kyd" }, + { label: "ANG - Netherlands Antillean Guilder", value: "ang" }, + { label: "XCG - Caribbean Guilder", value: "xcg" }, +] + +const BankInformation = () => { + const styles = useStyles() + + const dispatch = useAppDispatch() + const { bankName, bankBranch, accountType, currency, accountNumber, document } = + useAppSelector((state) => state.businessAccount.bankInfo) + + const onPressNext = () => {} + + return ( + + + dispatch(setBankInfo({ bankName: val }))} + /> + dispatch(setBankInfo({ bankBranch: val }))} + /> + dispatch(setBankInfo({ accountType: val }))} + /> + dispatch(setBankInfo({ currency: val }))} + /> + dispatch(setBankInfo({ accountNumber: val }))} + /> + + + + + ) +} + +export default BankInformation + +const useStyles = makeStyles(({ colors }) => ({ + container: { + flex: 1, + paddingVertical: 10, + paddingHorizontal: 20, + }, + btn: { + marginVertical: 10, + marginHorizontal: 20, + }, +})) diff --git a/app/screens/business-account-flow/index.ts b/app/screens/business-account-flow/index.ts new file mode 100644 index 000000000..acd218672 --- /dev/null +++ b/app/screens/business-account-flow/index.ts @@ -0,0 +1,5 @@ +import PersonalInformation from "./PersonalInformation" +import BusinessInformation from "./BusinessInformation" +import BankInformation from "./BankInformation" + +export { BusinessInformation, PersonalInformation, BankInformation } From b6ca4aca82acc9284dc643c9ff790720957eb680 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Fri, 25 Apr 2025 10:59:36 +0500 Subject: [PATCH 05/81] added bank account flow screens in root navigation --- app/navigation/root-navigator.tsx | 20 ++++++++++++++++++++ app/navigation/stack-param-lists.ts | 3 +++ 2 files changed, 23 insertions(+) diff --git a/app/navigation/root-navigator.tsx b/app/navigation/root-navigator.tsx index e431abb43..df2132f30 100644 --- a/app/navigation/root-navigator.tsx +++ b/app/navigation/root-navigator.tsx @@ -118,6 +118,11 @@ import { NostrSettingsScreen } from "@app/screens/settings-screen/nostr-settings import ContactDetailsScreen from "@app/screens/chat/contactDetailsScreen" import { SupportGroupChatScreen } from "@app/screens/chat/GroupChat/SupportGroupChat" import Contacts from "@app/screens/chat/contacts" +import { + PersonalInformation, + BusinessInformation, + BankInformation, +} from "@app/screens/business-account-flow" const useStyles = makeStyles(({ colors }) => ({ bottomNavigatorStyle: { @@ -586,6 +591,21 @@ export const RootStack = () => { component={SupportGroupChatScreen} options={{ title: "Group Chat" }} /> + + + ) } diff --git a/app/navigation/stack-param-lists.ts b/app/navigation/stack-param-lists.ts index cb8e8d321..b1c55d5e7 100644 --- a/app/navigation/stack-param-lists.ts +++ b/app/navigation/stack-param-lists.ts @@ -154,6 +154,9 @@ export type RootStackParamList = { Contacts: { userPrivateKey: string } SignInViaQRCode: undefined Nip29GroupChat: { groupId: string } + PersonalInformation: undefined + BusinessInformation: undefined + BankInformation: undefined } export type ChatStackParamList = { From 426611b87cbb8a66b769929fe01310d142ac640e Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Fri, 25 Apr 2025 11:02:44 +0500 Subject: [PATCH 06/81] implemented businessAccountSlice to handle business account states globally --- app/store/redux/reducers.ts | 2 + .../redux/slices/businessAccountSlice.ts | 87 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 app/store/redux/slices/businessAccountSlice.ts diff --git a/app/store/redux/reducers.ts b/app/store/redux/reducers.ts index 48668b47e..529d739c7 100644 --- a/app/store/redux/reducers.ts +++ b/app/store/redux/reducers.ts @@ -2,7 +2,9 @@ import { combineReducers } from "@reduxjs/toolkit" // slices import userSlice from "./slices/userSlice" +import businessAccountSlice from "./slices/businessAccountSlice" export default combineReducers({ user: userSlice, + businessAccount: businessAccountSlice, }) diff --git a/app/store/redux/slices/businessAccountSlice.ts b/app/store/redux/slices/businessAccountSlice.ts new file mode 100644 index 000000000..883ac006f --- /dev/null +++ b/app/store/redux/slices/businessAccountSlice.ts @@ -0,0 +1,87 @@ +import { createSlice } from "@reduxjs/toolkit" + +interface BusinessAccountSlice { + personalInfo: { + fullName: string + countryCode: string + phoneNumber: string + email: string + } + businessInfo: { + businessName: string + businessAddress: string + } + bankInfo: { + bankName: string + bankBranch: string + accountType: string + currency: string + accountNumber: string + document: string + } + loading: boolean + error: string +} + +const initialState: BusinessAccountSlice = { + personalInfo: { + fullName: null, + countryCode: "JM", + phoneNumber: null, + email: null, + }, + businessInfo: { + businessName: null, + businessAddress: null, + }, + bankInfo: { + bankName: null, + bankBranch: null, + accountType: null, + currency: null, + accountNumber: null, + document: null, + }, + loading: false, + error: "", +} + +export const businessAccountSlice = createSlice({ + name: "businessAccount", + initialState, + reducers: { + setPersonalInfo: (state, action) => ({ + ...state, + personalInfo: { ...state.personalInfo, ...action.payload }, + }), + setBusinessInfo: (state, action) => ({ + ...state, + businessInfo: { ...state.businessInfo, ...action.payload }, + }), + setBankInfo: (state, action) => ({ + ...state, + bankInfo: { ...state.bankInfo, ...action.payload }, + }), + setLoading: (state, action) => ({ + ...state, + loading: action.payload, + }), + setError: (state, action) => ({ + ...state, + error: action.payload, + }), + resetUserSlice: () => ({ + ...initialState, + }), + }, +}) + +export const { + setPersonalInfo, + setBusinessInfo, + setBankInfo, + setLoading, + setError, + resetUserSlice, +} = businessAccountSlice.actions +export default businessAccountSlice.reducer From 309ae881a5d79a21561644cfd28972cd18c87733 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Fri, 25 Apr 2025 11:19:10 +0500 Subject: [PATCH 07/81] rename business-account-flow to account-upgrade-flow --- .../DropDownField.tsx | 0 .../InputField.tsx | 0 .../PhoneNumber.tsx | 0 .../PhotoUploadField.tsx | 0 .../index.ts | 0 app/navigation/root-navigator.tsx | 2 +- .../BankInformation.tsx | 6 +++--- .../BusinessInformation.tsx | 6 +++--- .../PersonalInformation.tsx | 6 +++--- .../index.ts | 0 app/store/redux/reducers.ts | 4 ++-- ...usinessAccountSlice.ts => accountUpgradeSlice.ts} | 12 ++++++------ 12 files changed, 18 insertions(+), 18 deletions(-) rename app/components/{business-account-flow => account-upgrade-flow}/DropDownField.tsx (100%) rename app/components/{business-account-flow => account-upgrade-flow}/InputField.tsx (100%) rename app/components/{business-account-flow => account-upgrade-flow}/PhoneNumber.tsx (100%) rename app/components/{business-account-flow => account-upgrade-flow}/PhotoUploadField.tsx (100%) rename app/components/{business-account-flow => account-upgrade-flow}/index.ts (100%) rename app/screens/{business-account-flow => account-upgrade-flow}/BankInformation.tsx (93%) rename app/screens/{business-account-flow => account-upgrade-flow}/BusinessInformation.tsx (89%) rename app/screens/{business-account-flow => account-upgrade-flow}/PersonalInformation.tsx (89%) rename app/screens/{business-account-flow => account-upgrade-flow}/index.ts (100%) rename app/store/redux/slices/{businessAccountSlice.ts => accountUpgradeSlice.ts} (87%) diff --git a/app/components/business-account-flow/DropDownField.tsx b/app/components/account-upgrade-flow/DropDownField.tsx similarity index 100% rename from app/components/business-account-flow/DropDownField.tsx rename to app/components/account-upgrade-flow/DropDownField.tsx diff --git a/app/components/business-account-flow/InputField.tsx b/app/components/account-upgrade-flow/InputField.tsx similarity index 100% rename from app/components/business-account-flow/InputField.tsx rename to app/components/account-upgrade-flow/InputField.tsx diff --git a/app/components/business-account-flow/PhoneNumber.tsx b/app/components/account-upgrade-flow/PhoneNumber.tsx similarity index 100% rename from app/components/business-account-flow/PhoneNumber.tsx rename to app/components/account-upgrade-flow/PhoneNumber.tsx diff --git a/app/components/business-account-flow/PhotoUploadField.tsx b/app/components/account-upgrade-flow/PhotoUploadField.tsx similarity index 100% rename from app/components/business-account-flow/PhotoUploadField.tsx rename to app/components/account-upgrade-flow/PhotoUploadField.tsx diff --git a/app/components/business-account-flow/index.ts b/app/components/account-upgrade-flow/index.ts similarity index 100% rename from app/components/business-account-flow/index.ts rename to app/components/account-upgrade-flow/index.ts diff --git a/app/navigation/root-navigator.tsx b/app/navigation/root-navigator.tsx index df2132f30..924168517 100644 --- a/app/navigation/root-navigator.tsx +++ b/app/navigation/root-navigator.tsx @@ -122,7 +122,7 @@ import { PersonalInformation, BusinessInformation, BankInformation, -} from "@app/screens/business-account-flow" +} from "@app/screens/account-upgrade-flow" const useStyles = makeStyles(({ colors }) => ({ bottomNavigatorStyle: { diff --git a/app/screens/business-account-flow/BankInformation.tsx b/app/screens/account-upgrade-flow/BankInformation.tsx similarity index 93% rename from app/screens/business-account-flow/BankInformation.tsx rename to app/screens/account-upgrade-flow/BankInformation.tsx index 36f3d7dd6..190bd92cd 100644 --- a/app/screens/business-account-flow/BankInformation.tsx +++ b/app/screens/account-upgrade-flow/BankInformation.tsx @@ -7,13 +7,13 @@ import { DropDownField, InputField, PhotoUploadField, -} from "@app/components/business-account-flow" +} from "@app/components/account-upgrade-flow" import { Screen } from "@app/components/screen" import { PrimaryBtn } from "@app/components/buttons" // store import { useAppDispatch, useAppSelector } from "@app/store/redux" -import { setBankInfo } from "@app/store/redux/slices/businessAccountSlice" +import { setBankInfo } from "@app/store/redux/slices/accountUpgradeSlice" const accountTypes = [ { label: "Select account type", value: null }, @@ -36,7 +36,7 @@ const BankInformation = () => { const dispatch = useAppDispatch() const { bankName, bankBranch, accountType, currency, accountNumber, document } = - useAppSelector((state) => state.businessAccount.bankInfo) + useAppSelector((state) => state.accountUpgrade.bankInfo) const onPressNext = () => {} diff --git a/app/screens/business-account-flow/BusinessInformation.tsx b/app/screens/account-upgrade-flow/BusinessInformation.tsx similarity index 89% rename from app/screens/business-account-flow/BusinessInformation.tsx rename to app/screens/account-upgrade-flow/BusinessInformation.tsx index 3c2bb48f8..09ded1ac2 100644 --- a/app/screens/business-account-flow/BusinessInformation.tsx +++ b/app/screens/account-upgrade-flow/BusinessInformation.tsx @@ -7,11 +7,11 @@ import { RootStackParamList } from "@app/navigation/stack-param-lists" // components import { Screen } from "@app/components/screen" import { PrimaryBtn } from "@app/components/buttons" -import { InputField } from "@app/components/business-account-flow" +import { InputField } from "@app/components/account-upgrade-flow" // store import { useAppDispatch, useAppSelector } from "@app/store/redux" -import { setBusinessInfo } from "@app/store/redux/slices/businessAccountSlice" +import { setBusinessInfo } from "@app/store/redux/slices/accountUpgradeSlice" type Props = StackScreenProps @@ -20,7 +20,7 @@ const BusinessInformation: React.FC = ({ navigation }) => { const dispatch = useAppDispatch() const { businessName, businessAddress } = useAppSelector( - (state) => state.businessAccount.businessInfo, + (state) => state.accountUpgrade.businessInfo, ) const onPressNext = () => { diff --git a/app/screens/business-account-flow/PersonalInformation.tsx b/app/screens/account-upgrade-flow/PersonalInformation.tsx similarity index 89% rename from app/screens/business-account-flow/PersonalInformation.tsx rename to app/screens/account-upgrade-flow/PersonalInformation.tsx index 704d2b2cf..4a311701b 100644 --- a/app/screens/business-account-flow/PersonalInformation.tsx +++ b/app/screens/account-upgrade-flow/PersonalInformation.tsx @@ -7,11 +7,11 @@ import { RootStackParamList } from "@app/navigation/stack-param-lists" // components import { Screen } from "@app/components/screen" import { PrimaryBtn } from "@app/components/buttons" -import { InputField, PhoneNumber } from "@app/components/business-account-flow" +import { InputField, PhoneNumber } from "@app/components/account-upgrade-flow" // store import { useAppDispatch, useAppSelector } from "@app/store/redux" -import { setPersonalInfo } from "@app/store/redux/slices/businessAccountSlice" +import { setPersonalInfo } from "@app/store/redux/slices/accountUpgradeSlice" type Props = StackScreenProps @@ -20,7 +20,7 @@ const PersonalInformation: React.FC = ({ navigation }) => { const dispatch = useAppDispatch() const { fullName, countryCode, phoneNumber, email } = useAppSelector( - (state) => state.businessAccount.personalInfo, + (state) => state.accountUpgrade.personalInfo, ) const onPressNext = () => { diff --git a/app/screens/business-account-flow/index.ts b/app/screens/account-upgrade-flow/index.ts similarity index 100% rename from app/screens/business-account-flow/index.ts rename to app/screens/account-upgrade-flow/index.ts diff --git a/app/store/redux/reducers.ts b/app/store/redux/reducers.ts index 529d739c7..ec018442d 100644 --- a/app/store/redux/reducers.ts +++ b/app/store/redux/reducers.ts @@ -2,9 +2,9 @@ import { combineReducers } from "@reduxjs/toolkit" // slices import userSlice from "./slices/userSlice" -import businessAccountSlice from "./slices/businessAccountSlice" +import accountUpgradeSlice from "./slices/accountUpgradeSlice" export default combineReducers({ user: userSlice, - businessAccount: businessAccountSlice, + accountUpgrade: accountUpgradeSlice, }) diff --git a/app/store/redux/slices/businessAccountSlice.ts b/app/store/redux/slices/accountUpgradeSlice.ts similarity index 87% rename from app/store/redux/slices/businessAccountSlice.ts rename to app/store/redux/slices/accountUpgradeSlice.ts index 883ac006f..550097c70 100644 --- a/app/store/redux/slices/businessAccountSlice.ts +++ b/app/store/redux/slices/accountUpgradeSlice.ts @@ -1,6 +1,6 @@ import { createSlice } from "@reduxjs/toolkit" -interface BusinessAccountSlice { +interface AccountUpgradeSlice { personalInfo: { fullName: string countryCode: string @@ -23,7 +23,7 @@ interface BusinessAccountSlice { error: string } -const initialState: BusinessAccountSlice = { +const initialState: AccountUpgradeSlice = { personalInfo: { fullName: null, countryCode: "JM", @@ -46,8 +46,8 @@ const initialState: BusinessAccountSlice = { error: "", } -export const businessAccountSlice = createSlice({ - name: "businessAccount", +export const accountUpgradeSlice = createSlice({ + name: "accountUpgrade", initialState, reducers: { setPersonalInfo: (state, action) => ({ @@ -83,5 +83,5 @@ export const { setLoading, setError, resetUserSlice, -} = businessAccountSlice.actions -export default businessAccountSlice.reducer +} = accountUpgradeSlice.actions +export default accountUpgradeSlice.reducer From 2207af5e86861fe181709f182d6720612ff3faa1 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Sat, 26 Apr 2025 11:38:54 +0500 Subject: [PATCH 08/81] Reusable AddressField component is added to auto complete entered address --- .../account-upgrade-flow/AddressField.tsx | 59 +++++++++++++++++++ app/components/account-upgrade-flow/index.ts | 3 +- package.json | 1 + yarn.lock | 20 +++++++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 app/components/account-upgrade-flow/AddressField.tsx diff --git a/app/components/account-upgrade-flow/AddressField.tsx b/app/components/account-upgrade-flow/AddressField.tsx new file mode 100644 index 000000000..a9c1e98d6 --- /dev/null +++ b/app/components/account-upgrade-flow/AddressField.tsx @@ -0,0 +1,59 @@ +import React, { useState } from "react" +import { View } from "react-native" +import { makeStyles, Text, useTheme } from "@rneui/themed" +import { GooglePlacesAutocomplete } from "react-native-google-places-autocomplete" + +type Props = { + label: string + placeholder: string +} + +const AddressField: React.FC = ({ label, placeholder }) => { + const styles = useStyles() + const { colors } = useTheme().theme + + const [isFocused, setIsFocused] = useState(false) + + return ( + + + {label} + + { + // 'details' is provided when fetchDetails = true + console.log(data, details) + }} + query={{ + key: "YOUR API KEY", + language: "en", + }} + textInputProps={{ + onFocus: () => setIsFocused(true), + onBlur: () => setIsFocused(false), + }} + /> + + ) +} + +export default AddressField + +const useStyles = makeStyles(({ colors }) => ({ + container: { + paddingVertical: 25, + paddingHorizontal: 15, + marginTop: 5, + marginBottom: 15, + borderWidth: 1, + borderRadius: 10, + borderColor: colors.grey4, + backgroundColor: colors.grey5, + fontSize: 16, + fontFamily: "Sora-Regular", + }, +})) diff --git a/app/components/account-upgrade-flow/index.ts b/app/components/account-upgrade-flow/index.ts index 6b0fb221e..bb1bc2fe5 100644 --- a/app/components/account-upgrade-flow/index.ts +++ b/app/components/account-upgrade-flow/index.ts @@ -2,5 +2,6 @@ import PhoneNumber from "./PhoneNumber" import InputField from "./InputField" import DropDownField from "./DropDownField" import PhotoUploadField from "./PhotoUploadField" +import AddressField from "./AddressField" -export { PhoneNumber, InputField, DropDownField, PhotoUploadField } +export { PhoneNumber, InputField, DropDownField, PhotoUploadField, AddressField } diff --git a/package.json b/package.json index 166e3bcd9..dd09fa006 100644 --- a/package.json +++ b/package.json @@ -144,6 +144,7 @@ "react-native-fs": "^2.20.0", "react-native-gesture-handler": "2.12.1", "react-native-get-random-values": "^1.9.0", + "react-native-google-places-autocomplete": "^2.5.7", "react-native-gradle-plugin": "^0.71.19", "react-native-haptic-feedback": "^2.0.3", "react-native-html-to-pdf": "^0.12.0", diff --git a/yarn.lock b/yarn.lock index 37e121161..d4bc41c7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20844,6 +20844,11 @@ qs@^6.10.0, qs@^6.12.3, qs@~6.14.0: dependencies: side-channel "^1.1.0" +qs@~6.9.1: + version "6.9.7" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" + integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== + query-selector-shadow-dom@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz#1c7b0058eff4881ac44f45d8f84ede32e9a2f349" @@ -21261,6 +21266,16 @@ react-native-get-random-values@^1.9.0: dependencies: fast-base64-decode "^1.0.0" +react-native-google-places-autocomplete@^2.5.7: + version "2.5.7" + resolved "https://registry.yarnpkg.com/react-native-google-places-autocomplete/-/react-native-google-places-autocomplete-2.5.7.tgz#92a7ded4466a46fff70fa428f9f8dd60c19f9edb" + integrity sha512-yWEJ31gsRptStEZDele9no0J6Z+MxtNpQKKwjvGmFrWmpYDEAzpRtxy1Xq0u+ckMepJfrg/EU+OL5LgtMbnCDA== + dependencies: + lodash.debounce "^4.0.8" + prop-types "^15.7.2" + qs "~6.9.1" + uuid "^10.0.0" + react-native-gradle-plugin@^0.71.19: version "0.71.19" resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.19.tgz#3379e28341fcd189bc1f4691cefc84c1a4d7d232" @@ -25303,6 +25318,11 @@ uuid@3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" From 66515dd26812ddecfe232bb8c83feeda4b4270f0 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Sat, 26 Apr 2025 11:42:48 +0500 Subject: [PATCH 09/81] update InputField and PhoneNumber components to change border color when focused --- .../account-upgrade-flow/InputField.tsx | 28 ++++++++++++++++--- .../account-upgrade-flow/PhoneNumber.tsx | 14 ++++++++-- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/app/components/account-upgrade-flow/InputField.tsx b/app/components/account-upgrade-flow/InputField.tsx index 0c9f45295..153e4655c 100644 --- a/app/components/account-upgrade-flow/InputField.tsx +++ b/app/components/account-upgrade-flow/InputField.tsx @@ -1,24 +1,42 @@ -import React from "react" -import { makeStyles, Text } from "@rneui/themed" +import React, { useState } from "react" +import { makeStyles, Text, useTheme } from "@rneui/themed" import { TextInput, TextInputProps, View } from "react-native" type Props = { label: string + isOptional?: boolean } & TextInputProps -const InputField: React.FC = ({ label, placeholder, value, onChangeText }) => { +const InputField: React.FC = ({ + label, + isOptional, + placeholder, + value, + onChangeText, +}) => { const styles = useStyles() + const { colors } = useTheme().theme + + const [isFocused, setIsFocused] = useState(false) return ( {label} + {isOptional && ( + + {" "} + (Optional) + + )} setIsFocused(true)} + onBlur={() => setIsFocused(false)} /> ) @@ -32,6 +50,8 @@ const useStyles = makeStyles(({ colors }) => ({ marginTop: 5, marginBottom: 15, borderRadius: 10, + borderWidth: 1, + borderColor: colors.grey4, backgroundColor: colors.grey5, fontSize: 16, fontFamily: "Sora-Regular", diff --git a/app/components/account-upgrade-flow/PhoneNumber.tsx b/app/components/account-upgrade-flow/PhoneNumber.tsx index 7818efb28..b2ca3cd9b 100644 --- a/app/components/account-upgrade-flow/PhoneNumber.tsx +++ b/app/components/account-upgrade-flow/PhoneNumber.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useState } from "react" import { TextInput, View } from "react-native" import { makeStyles, useTheme, Text } from "@rneui/themed" import { TouchableOpacity } from "react-native-gesture-handler" @@ -32,10 +32,12 @@ const PhoneNumber: React.FC = ({ setCountryCode, }) => { const styles = useStyles() - const { mode } = useTheme().theme + const { mode, colors } = useTheme().theme const { LL } = useI18nContext() const { supportedCountries } = useRequestPhoneCodeLogin() + const [isFocused, setIsFocused] = useState(false) + const renderCountryCode = ({ countryCode, onOpen }: FlagButtonProps) => { return ( countryCode && ( @@ -68,11 +70,13 @@ const PhoneNumber: React.FC = ({ /> setIsFocused(true)} + onBlur={() => setIsFocused(false)} /> @@ -89,6 +93,8 @@ const useStyles = makeStyles(({ colors }) => ({ countryPicker: { padding: 14.5, borderRadius: 10, + borderWidth: 1, + borderColor: colors.grey4, backgroundColor: colors.grey5, flexDirection: "row", alignItems: "center", @@ -98,6 +104,8 @@ const useStyles = makeStyles(({ colors }) => ({ padding: 15, marginBottom: 15, borderRadius: 10, + borderWidth: 1, + borderColor: colors.grey4, backgroundColor: colors.grey5, fontSize: 16, fontFamily: "Sora-Regular", From 3ebdd237b6420744cd4003727a8fca839d045446 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Sat, 26 Apr 2025 11:46:00 +0500 Subject: [PATCH 10/81] implement AccountType screen and update upgradeAccountSlice --- app/navigation/root-navigator.tsx | 6 ++ app/navigation/stack-param-lists.ts | 1 + .../account-upgrade-flow/AccountType.tsx | 84 +++++++++++++++++++ app/screens/account-upgrade-flow/index.ts | 3 +- app/store/redux/slices/accountUpgradeSlice.ts | 7 ++ 5 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 app/screens/account-upgrade-flow/AccountType.tsx diff --git a/app/navigation/root-navigator.tsx b/app/navigation/root-navigator.tsx index 924168517..8c2b2ed9d 100644 --- a/app/navigation/root-navigator.tsx +++ b/app/navigation/root-navigator.tsx @@ -122,6 +122,7 @@ import { PersonalInformation, BusinessInformation, BankInformation, + AccountType, } from "@app/screens/account-upgrade-flow" const useStyles = makeStyles(({ colors }) => ({ @@ -591,6 +592,11 @@ export const RootStack = () => { component={SupportGroupChatScreen} options={{ title: "Group Chat" }} /> + + +const AccountType: React.FC = ({ navigation }) => { + const dispatch = useAppDispatch() + const styles = useStyles() + const { colors } = useTheme().theme + + const onPress = (accountType: string) => { + dispatch(setAccountUpgrade({ accountType })) + navigation.navigate("PersonalInformation") + } + + return ( + + onPress("personal")}> + + + + Personal + + + For individual use, no additional info needed + + + + + onPress("pro")}> + + + + Pro + + + Accept payments as a Pro Flashpoint. Business Name & Address required + + + + + onPress("merchant")}> + + + + Merchant + + + Give rewards as a Merchant Flashpoint. ID and Bank account info required + + + + + + ) +} + +export default AccountType + +const useStyles = makeStyles(({ colors }) => ({ + card: { + flexDirection: "row", + alignItems: "center", + backgroundColor: colors.grey5, + padding: 15, + marginHorizontal: 20, + marginVertical: 10, + borderRadius: 20, + }, + textWrapper: { + flex: 1, + marginHorizontal: 15, + }, +})) diff --git a/app/screens/account-upgrade-flow/index.ts b/app/screens/account-upgrade-flow/index.ts index acd218672..1697ad7df 100644 --- a/app/screens/account-upgrade-flow/index.ts +++ b/app/screens/account-upgrade-flow/index.ts @@ -1,5 +1,6 @@ +import AccountType from "./AccountType" import PersonalInformation from "./PersonalInformation" import BusinessInformation from "./BusinessInformation" import BankInformation from "./BankInformation" -export { BusinessInformation, PersonalInformation, BankInformation } +export { AccountType, BusinessInformation, PersonalInformation, BankInformation } diff --git a/app/store/redux/slices/accountUpgradeSlice.ts b/app/store/redux/slices/accountUpgradeSlice.ts index 550097c70..6e7477dc5 100644 --- a/app/store/redux/slices/accountUpgradeSlice.ts +++ b/app/store/redux/slices/accountUpgradeSlice.ts @@ -1,6 +1,7 @@ import { createSlice } from "@reduxjs/toolkit" interface AccountUpgradeSlice { + accountType: "personal" | "pro" | "merchant" personalInfo: { fullName: string countryCode: string @@ -24,6 +25,7 @@ interface AccountUpgradeSlice { } const initialState: AccountUpgradeSlice = { + accountType: null, personalInfo: { fullName: null, countryCode: "JM", @@ -50,6 +52,10 @@ export const accountUpgradeSlice = createSlice({ name: "accountUpgrade", initialState, reducers: { + setAccountUpgrade: (state, action) => ({ + ...state, + ...action.payload, + }), setPersonalInfo: (state, action) => ({ ...state, personalInfo: { ...state.personalInfo, ...action.payload }, @@ -77,6 +83,7 @@ export const accountUpgradeSlice = createSlice({ }) export const { + setAccountUpgrade, setPersonalInfo, setBusinessInfo, setBankInfo, From 10f4f9fbab1dd6aeebb1095b5c87b22cf85e97ef Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Sat, 26 Apr 2025 11:47:34 +0500 Subject: [PATCH 11/81] Use address auto complete in BusinessInformation screen --- .../account-upgrade-flow/BusinessInformation.tsx | 11 ++++------- .../account-upgrade-flow/PersonalInformation.tsx | 1 + 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/screens/account-upgrade-flow/BusinessInformation.tsx b/app/screens/account-upgrade-flow/BusinessInformation.tsx index 09ded1ac2..e0d8d4d7f 100644 --- a/app/screens/account-upgrade-flow/BusinessInformation.tsx +++ b/app/screens/account-upgrade-flow/BusinessInformation.tsx @@ -7,7 +7,7 @@ import { RootStackParamList } from "@app/navigation/stack-param-lists" // components import { Screen } from "@app/components/screen" import { PrimaryBtn } from "@app/components/buttons" -import { InputField } from "@app/components/account-upgrade-flow" +import { AddressField, InputField } from "@app/components/account-upgrade-flow" // store import { useAppDispatch, useAppSelector } from "@app/store/redux" @@ -32,16 +32,13 @@ const BusinessInformation: React.FC = ({ navigation }) => { dispatch(setBusinessInfo({ businessName: val }))} /> - - dispatch(setBusinessInfo({ businessAddress: val }))} + placeholder={"Enter your business address"} /> diff --git a/app/screens/account-upgrade-flow/PersonalInformation.tsx b/app/screens/account-upgrade-flow/PersonalInformation.tsx index 4a311701b..a004e1a9d 100644 --- a/app/screens/account-upgrade-flow/PersonalInformation.tsx +++ b/app/screens/account-upgrade-flow/PersonalInformation.tsx @@ -44,6 +44,7 @@ const PersonalInformation: React.FC = ({ navigation }) => { /> dispatch(setPersonalInfo({ email: val }))} From 8991c3bdfb479df16fd3bb25ce4539eeda9d572d Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Sat, 26 Apr 2025 11:54:37 +0500 Subject: [PATCH 12/81] change text to in Quick start section --- app/i18n/en/index.ts | 2 +- app/i18n/i18n-types.ts | 4 ++-- app/i18n/raw-i18n/source/en.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/i18n/en/index.ts b/app/i18n/en/index.ts index 8ef6c6245..dbea9d71a 100644 --- a/app/i18n/en/index.ts +++ b/app/i18n/en/index.ts @@ -550,7 +550,7 @@ const en: BaseTranslation = { bitcoin: "Bitcoin", flashcard: "Flashcard", addFlashcard: "Add Flashcard", - upgradeTitle: "Add your phone number", + upgradeTitle: "Upgrade your account", upgradeDesc: "Backup your cash wallet and increase transaction limits.", currencyTitle:"Change to your local currency", currencyDesc: "Review our available currency list and select your currency.", diff --git a/app/i18n/i18n-types.ts b/app/i18n/i18n-types.ts index 3aed41fed..a6b4bf1f3 100644 --- a/app/i18n/i18n-types.ts +++ b/app/i18n/i18n-types.ts @@ -1721,7 +1721,7 @@ type RootTranslation = { */ addFlashcard: string /** - * A​d​d​ ​y​o​u​r​ ​p​h​o​n​e​ ​n​u​m​b​e​r + * Upgrade your account */ upgradeTitle: string /** @@ -6479,7 +6479,7 @@ export type TranslationFunctions = { */ addFlashcard: () => LocalizedString /** - * Add your phone number + * Upgrade your account */ upgradeTitle: () => LocalizedString /** diff --git a/app/i18n/raw-i18n/source/en.json b/app/i18n/raw-i18n/source/en.json index ea9650313..d2dcd416b 100644 --- a/app/i18n/raw-i18n/source/en.json +++ b/app/i18n/raw-i18n/source/en.json @@ -519,7 +519,7 @@ "bitcoin": "Bitcoin", "flashcard": "Flashcard", "addFlashcard": "Add Flashcard", - "upgradeTitle": "Add your phone number", + "upgradeTitle": "Upgrade your account", "upgradeDesc": "Backup your cash wallet and increase transaction limits.", "currencyTitle": "Change to your local currency", "currencyDesc": "Review our available currency list and select your currency.", From 4a81d44027af2635e7b41806c049b702cc5d026b Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Sat, 26 Apr 2025 11:57:54 +0500 Subject: [PATCH 13/81] update QuickStart section to navigate to Account Upgrade flow --- app/components/home-screen/QuickStart.tsx | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/app/components/home-screen/QuickStart.tsx b/app/components/home-screen/QuickStart.tsx index 129de243d..fcd72d122 100644 --- a/app/components/home-screen/QuickStart.tsx +++ b/app/components/home-screen/QuickStart.tsx @@ -15,8 +15,7 @@ import GoldWallet from "@app/assets/illustrations/gold-wallet.svg" import SecureWallet from "@app/assets/illustrations/secure-wallet.svg" // components -import { UpgradeAccountModal } from "../upgrade-account-modal" -import { AdvancedModeModal } from "../advanced-mode-modal" +import { QuickStartAdvancedMode } from "../advanced-mode-modal" // hooks import { useI18nContext } from "@app/i18n/i18n-react" @@ -49,7 +48,6 @@ const QuickStart = () => { const ref = useRef(null) const [advanceModalVisible, setAdvanceModalVisible] = useState(false) - const [upgradeAccountModalVisible, setUpgradeAccountModalVisible] = useState(false) const [hasRecoveryPhrase, setHasRecoveryPhrase] = useState(false) const { data, loading } = useHomeAuthedQuery() @@ -69,7 +67,7 @@ const QuickStart = () => { title: LL.HomeScreen.upgradeTitle(), description: LL.HomeScreen.upgradeDesc(), image: Account, - onPress: () => setUpgradeAccountModalVisible(true), + onPress: () => navigation.navigate("AccountType"), }, { type: "currency", @@ -124,7 +122,7 @@ const QuickStart = () => { data?.me?.defaultAccount.level !== AccountLevel.Zero || persistentState?.closedQuickStartTypes?.includes("upgrade") ) { - carouselData = carouselData.filter((el) => el.type !== "upgrade") + // carouselData = carouselData.filter((el) => el.type !== "upgrade") } if ( persistentState.currencyChanged || @@ -207,11 +205,7 @@ const QuickStart = () => { loop={carouselData.length !== 1} containerStyle={{ marginTop: 10 }} /> - setUpgradeAccountModalVisible(false)} - /> - Date: Tue, 29 Apr 2025 11:13:21 +0500 Subject: [PATCH 14/81] Account Upgrade Validation screen is added to validate phone number --- .../account-upgrade-flow/Validation.tsx | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 app/screens/account-upgrade-flow/Validation.tsx diff --git a/app/screens/account-upgrade-flow/Validation.tsx b/app/screens/account-upgrade-flow/Validation.tsx new file mode 100644 index 000000000..8b7fa5472 --- /dev/null +++ b/app/screens/account-upgrade-flow/Validation.tsx @@ -0,0 +1,109 @@ +import React, { useCallback, useState } from "react" +import { RootStackParamList } from "@app/navigation/stack-param-lists" +import { StackScreenProps } from "@react-navigation/stack" +import { makeStyles, Text } from "@rneui/themed" +import { View } from "react-native" + +// components +import { Screen } from "@app/components/screen" +import { InputField } from "@app/components/account-upgrade-flow" + +// hooks +import { useAppConfig } from "@app/hooks" +import { useUserLoginUpgradeMutation } from "@app/graphql/generated" +import { useI18nContext } from "@app/i18n/i18n-react" +import { useAppSelector } from "@app/store/redux" + +// utils +import { PhoneCodeChannelToFriendlyName } from "../phone-auth-screen/request-phone-code-login" + +type Props = StackScreenProps + +const Validation: React.FC = ({ navigation, route }) => { + const { phone, channel } = route.params + const { accountType } = useAppSelector((state) => state.accountUpgrade) + const { LL } = useI18nContext() + const { saveToken } = useAppConfig() + const styles = useStyles() + + const [code, setCode] = useState() + const [errorMsg, setErrorMsg] = useState() + + const [userLoginUpgradeMutation] = useUserLoginUpgradeMutation({ + fetchPolicy: "no-cache", + }) + + const send = useCallback( + async (code: string) => { + try { + const { data } = await userLoginUpgradeMutation({ + variables: { input: { phone, code } }, + }) + + const success = data?.userLoginUpgrade?.success + const authToken = data?.userLoginUpgrade?.authToken + if (success) { + if (authToken) { + saveToken(authToken) + } + if (accountType === "personal") { + navigation.replace("AccountUpgradeSuccess") + } else { + navigation.replace("BusinessInformation") + } + } else { + console.log(">>>>>>>>>>>>>>>>>", data.userLoginUpgrade) + setErrorMsg(data.userLoginUpgrade.errors[0].message) + } + setCode("") + } catch (err) { + console.log("ERROR>>>>>>>>>", err) + setCode("") + } + }, + [userLoginUpgradeMutation, saveToken, phone], + ) + + const onChangeText = (code: string) => { + if (code.length > 6) { + return + } + + setCode(code) + if (code.length === 6) { + send(code) + } + } + + return ( + + + + {LL.PhoneLoginValidationScreen.header({ + channel: PhoneCodeChannelToFriendlyName[channel], + phoneNumber: phone, + })} + + + + + ) +} + +export default Validation + +const useStyles = makeStyles(() => ({ + wrapper: { + paddingVertical: 10, + paddingHorizontal: 20, + }, + header: { + marginBottom: 30, + }, +})) From 9f0fcd96cebc834d84b203964e707a645ab9ff1f Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 29 Apr 2025 11:16:06 +0500 Subject: [PATCH 15/81] Account Upgrade Success screen is implemented and added in root navigation --- app/navigation/root-navigator.tsx | 12 ++++ app/navigation/stack-param-lists.ts | 5 ++ app/screens/account-upgrade-flow/Success.tsx | 70 ++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 app/screens/account-upgrade-flow/Success.tsx diff --git a/app/navigation/root-navigator.tsx b/app/navigation/root-navigator.tsx index 8c2b2ed9d..066e8f6b5 100644 --- a/app/navigation/root-navigator.tsx +++ b/app/navigation/root-navigator.tsx @@ -123,6 +123,8 @@ import { BusinessInformation, BankInformation, AccountType, + Validation, + Success, } from "@app/screens/account-upgrade-flow" const useStyles = makeStyles(({ colors }) => ({ @@ -612,6 +614,16 @@ export const RootStack = () => { component={BankInformation} options={{ title: "Banking Information" }} /> + + ) } diff --git a/app/navigation/stack-param-lists.ts b/app/navigation/stack-param-lists.ts index 004073b62..bf6aae75e 100644 --- a/app/navigation/stack-param-lists.ts +++ b/app/navigation/stack-param-lists.ts @@ -158,6 +158,11 @@ export type RootStackParamList = { PersonalInformation: undefined BusinessInformation: undefined BankInformation: undefined + Validation: { + phone: string + channel: PhoneCodeChannelType + } + AccountUpgradeSuccess: undefined } export type ChatStackParamList = { diff --git a/app/screens/account-upgrade-flow/Success.tsx b/app/screens/account-upgrade-flow/Success.tsx new file mode 100644 index 000000000..5af60882a --- /dev/null +++ b/app/screens/account-upgrade-flow/Success.tsx @@ -0,0 +1,70 @@ +import React from "react" +import { View } from "react-native" +import { makeStyles, Text, useTheme } from "@rneui/themed" +import { StackScreenProps } from "@react-navigation/stack" +import { RootStackParamList } from "@app/navigation/stack-param-lists" + +// components +import { Screen } from "@app/components/screen" +import { PrimaryBtn } from "@app/components/buttons" + +// assets +import Account from "@app/assets/illustrations/account.svg" + +// store +import { useAppDispatch, useAppSelector } from "@app/store/redux" +import { resetAccountUpgrade } from "@app/store/redux/slices/accountUpgradeSlice" + +type Props = StackScreenProps + +const Success: React.FC = ({ navigation }) => { + const styles = useStyles() + const { colors } = useTheme().theme + + const dispatch = useAppDispatch() + const { accountType } = useAppSelector((state) => state.accountUpgrade) + + const onComplete = () => { + dispatch(resetAccountUpgrade()) + navigation.reset({ + index: 0, + routes: [{ name: "Primary" }], + }) + } + + return ( + + + + {`You successfully upgraded your account to ${accountType.toUpperCase()}`} + + + + + + ) +} + +export default Success + +const useStyles = makeStyles(() => ({ + wrapper: { + flex: 1, + alignItems: "center", + justifyContent: "center", + }, + header: { + textAlign: "center", + color: "#fff", + }, + btn: { + backgroundColor: "#fff", + marginHorizontal: 20, + marginBottom: 10, + }, +})) From 6a3e6c816b5e5bfcb6abdb5b84275026a7501087 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 29 Apr 2025 11:23:28 +0500 Subject: [PATCH 16/81] update InputField, AddressField, DropDownField and PhoneNumber components to show error message --- .../account-upgrade-flow/AddressField.tsx | 8 +++++++- .../account-upgrade-flow/DropDownField.tsx | 20 ++++++++++++++++--- .../account-upgrade-flow/InputField.tsx | 14 +++++++++++-- .../account-upgrade-flow/PhoneNumber.tsx | 14 +++++++++++-- 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/app/components/account-upgrade-flow/AddressField.tsx b/app/components/account-upgrade-flow/AddressField.tsx index a9c1e98d6..9a45467a8 100644 --- a/app/components/account-upgrade-flow/AddressField.tsx +++ b/app/components/account-upgrade-flow/AddressField.tsx @@ -6,9 +6,10 @@ import { GooglePlacesAutocomplete } from "react-native-google-places-autocomplet type Props = { label: string placeholder: string + errorMsg: string } -const AddressField: React.FC = ({ label, placeholder }) => { +const AddressField: React.FC = ({ label, placeholder, errorMsg }) => { const styles = useStyles() const { colors } = useTheme().theme @@ -37,6 +38,11 @@ const AddressField: React.FC = ({ label, placeholder }) => { onBlur: () => setIsFocused(false), }} /> + {!!errorMsg && ( + + {errorMsg} + + )} ) } diff --git a/app/components/account-upgrade-flow/DropDownField.tsx b/app/components/account-upgrade-flow/DropDownField.tsx index 86cd1a66d..8c734389d 100644 --- a/app/components/account-upgrade-flow/DropDownField.tsx +++ b/app/components/account-upgrade-flow/DropDownField.tsx @@ -1,6 +1,6 @@ import React from "react" import { View } from "react-native" -import { makeStyles, Text } from "@rneui/themed" +import { makeStyles, Text, useTheme } from "@rneui/themed" import { Dropdown } from "react-native-element-dropdown" type Props = { @@ -8,6 +8,7 @@ type Props = { placeholder: string data: any[] value: string + errorMsg?: string onChange: (val: string) => void } @@ -16,11 +17,14 @@ const DropDownField: React.FC = ({ placeholder, data, value, + errorMsg, onChange, }) => { const styles = useStyles() + const { colors } = useTheme().theme + return ( - + {label} @@ -36,6 +40,11 @@ const DropDownField: React.FC = ({ value={value} onChange={(item) => onChange(item.value)} /> + {!!errorMsg && ( + + {errorMsg} + + )} ) } @@ -43,11 +52,16 @@ const DropDownField: React.FC = ({ export default DropDownField const useStyles = makeStyles(({ colors }) => ({ + wrapper: { + marginBottom: 15, + }, dropdown: { padding: 15, marginTop: 5, - marginBottom: 15, + marginBottom: 2, borderRadius: 10, + borderWidth: 1, + borderColor: colors.grey4, backgroundColor: colors.grey5, fontSize: 16, fontFamily: "Sora-Regular", diff --git a/app/components/account-upgrade-flow/InputField.tsx b/app/components/account-upgrade-flow/InputField.tsx index 153e4655c..24953eef7 100644 --- a/app/components/account-upgrade-flow/InputField.tsx +++ b/app/components/account-upgrade-flow/InputField.tsx @@ -4,11 +4,13 @@ import { TextInput, TextInputProps, View } from "react-native" type Props = { label: string + errorMsg?: string isOptional?: boolean } & TextInputProps const InputField: React.FC = ({ label, + errorMsg, isOptional, placeholder, value, @@ -20,7 +22,7 @@ const InputField: React.FC = ({ const [isFocused, setIsFocused] = useState(false) return ( - + {label} {isOptional && ( @@ -38,6 +40,11 @@ const InputField: React.FC = ({ onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)} /> + {!!errorMsg && ( + + {errorMsg} + + )} ) } @@ -45,10 +52,13 @@ const InputField: React.FC = ({ export default InputField const useStyles = makeStyles(({ colors }) => ({ + container: { + marginBottom: 15, + }, input: { padding: 15, marginTop: 5, - marginBottom: 15, + marginBottom: 2, borderRadius: 10, borderWidth: 1, borderColor: colors.grey4, diff --git a/app/components/account-upgrade-flow/PhoneNumber.tsx b/app/components/account-upgrade-flow/PhoneNumber.tsx index b2ca3cd9b..a903de1ac 100644 --- a/app/components/account-upgrade-flow/PhoneNumber.tsx +++ b/app/components/account-upgrade-flow/PhoneNumber.tsx @@ -21,6 +21,7 @@ import { useRequestPhoneCodeLogin } from "@app/screens/phone-auth-screen/request type Props = { countryCode: string phoneNumber: string + errorMsg?: string setPhoneNumber: (number: string) => void setCountryCode: (countryCode: PhoneNumberCountryCode) => void } @@ -28,6 +29,7 @@ type Props = { const PhoneNumber: React.FC = ({ countryCode, phoneNumber, + errorMsg, setPhoneNumber, setCountryCode, }) => { @@ -52,7 +54,7 @@ const PhoneNumber: React.FC = ({ } return ( - + Phone Number @@ -79,6 +81,11 @@ const PhoneNumber: React.FC = ({ onBlur={() => setIsFocused(false)} /> + {!!errorMsg && ( + + {errorMsg} + + )} ) } @@ -86,9 +93,13 @@ const PhoneNumber: React.FC = ({ export default PhoneNumber const useStyles = makeStyles(({ colors }) => ({ + wrapper: { + marginBottom: 15, + }, container: { flexDirection: "row", marginTop: 5, + marginBottom: 2, }, countryPicker: { padding: 14.5, @@ -102,7 +113,6 @@ const useStyles = makeStyles(({ colors }) => ({ input: { flex: 1, padding: 15, - marginBottom: 15, borderRadius: 10, borderWidth: 1, borderColor: colors.grey4, From 63fa0ac01cea2367d88cbd8935da744fd84b7399 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 29 Apr 2025 11:56:35 +0500 Subject: [PATCH 17/81] Implemented logic and UI for taking a photo and selecting one from the gallery in the PhotoUploadField component. --- .../account-upgrade-flow/PhotoUploadField.tsx | 176 +++++++++++++++++- 1 file changed, 169 insertions(+), 7 deletions(-) diff --git a/app/components/account-upgrade-flow/PhotoUploadField.tsx b/app/components/account-upgrade-flow/PhotoUploadField.tsx index d504ad050..470421c84 100644 --- a/app/components/account-upgrade-flow/PhotoUploadField.tsx +++ b/app/components/account-upgrade-flow/PhotoUploadField.tsx @@ -1,16 +1,62 @@ -import React from "react" -import { Alert, TouchableOpacity, View } from "react-native" +import React, { useCallback, useEffect, useRef, useState } from "react" +import { Alert, Linking, Modal, TouchableOpacity, View } from "react-native" import { Icon, makeStyles, Text, useTheme } from "@rneui/themed" import { launchImageLibrary } from "react-native-image-picker" +import { + Camera, + CameraRuntimeError, + useCameraDevice, + useCameraPermission, +} from "react-native-vision-camera" + +// component +import { Screen } from "../screen" +import { PrimaryBtn } from "../buttons" + +// hooks +import { useI18nContext } from "@app/i18n/i18n-react" +import { useSafeAreaInsets } from "react-native-safe-area-context" + +// assets +import PhotoAdd from "@app/assets/icons/photo-add.svg" + +// utils import { toastShow } from "@app/utils/toast" type Props = { label: string + errorMsg?: string } -const PhotoUploadField: React.FC = ({ label }) => { +const PhotoUploadField: React.FC = ({ label, errorMsg }) => { const styles = useStyles() const { colors } = useTheme().theme + const { LL } = useI18nContext() + const { top, bottom } = useSafeAreaInsets() + + const { hasPermission, requestPermission } = useCameraPermission() + const device = useCameraDevice("back", { + physicalDevices: ["wide-angle-camera", "telephoto-camera"], + }) + + const camera = useRef(null) + const [isCameraVisible, setIsCameraVisible] = useState(false) + + useEffect(() => { + if (!hasPermission) { + requestPermission() + } + }, [hasPermission, requestPermission]) + + const openSettings = () => { + Linking.openSettings().catch(() => { + Alert.alert(LL.ScanningQRCodeScreen.unableToOpenSettings()) + }) + } + + const onError = useCallback((error: CameraRuntimeError) => { + console.error(error) + }, []) const showImagePicker = async () => { try { @@ -21,7 +67,9 @@ const PhotoUploadField: React.FC = ({ label }) => { translations.ScanningQRCodeScreen.imageLibraryPermissionsNotGranted(), }) } - console.log(">>>>>>>>>>>>>>", result) + if (result.assets && result.assets.length > 0 && result.assets[0].uri) { + console.log(">>>>>>>>>>>>>>>>", result) + } } catch (err: unknown) { if (err instanceof Error) { Alert.alert(err.toString()) @@ -29,14 +77,81 @@ const PhotoUploadField: React.FC = ({ label }) => { } } + const takePhoto = async () => { + const photo = await camera?.current?.takePhoto() + console.log(">>>>>>>>>>>>>>>", photo) + } + return ( - + {label} - + setIsCameraVisible(true)}> + {!!errorMsg && ( + + {errorMsg} + + )} + setIsCameraVisible(false)} + > + + + setIsCameraVisible(false)} + > + + + + {!hasPermission ? ( + <> + + + {LL.ScanningQRCodeScreen.permissionCamera()} + + + + + ) : device === null || device === undefined ? ( + + {LL.ScanningQRCodeScreen.noCamera()} + + ) : ( + + + + + + + + + )} + + + + + + + ) } @@ -44,14 +159,61 @@ const PhotoUploadField: React.FC = ({ label }) => { export default PhotoUploadField const useStyles = makeStyles(({ colors }) => ({ + wrapper: { + marginBottom: 35, + }, container: { width: "100%", height: 150, alignItems: "center", justifyContent: "center", marginTop: 5, - marginBottom: 15, + marginBottom: 2, borderRadius: 10, + borderWidth: 1, + borderColor: colors.grey4, backgroundColor: colors.grey5, }, + row: { + flexDirection: "row", + justifyContent: "flex-end", + backgroundColor: "#000", + }, + close: { + paddingHorizontal: 20, + paddingVertical: 10, + }, + center: { + flex: 1, + alignItems: "center", + justifyContent: "center", + }, + camera: { + flex: 1, + }, + captureWrapper: { + position: "absolute", + width: "100%", + alignItems: "center", + bottom: 20, + }, + captureOutline: { + borderWidth: 3, + borderRadius: 100, + borderColor: "#fff", + padding: 5, + }, + capture: { + width: 50, + height: 50, + borderRadius: 100, + backgroundColor: "#FFF", + }, + permissionMissingText: { + width: "80%", + textAlign: "center", + }, + photoLibrary: { + padding: 20, + }, })) From cb4bcb22286c74fb9fff64bab67a59be3e0147d5 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 29 Apr 2025 12:02:24 +0500 Subject: [PATCH 18/81] Added logic to display the 'Personal' account type in the AccountType screen for trial users. --- .../account-upgrade-flow/AccountType.tsx | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/app/screens/account-upgrade-flow/AccountType.tsx b/app/screens/account-upgrade-flow/AccountType.tsx index 330c6b4f4..f02c3a816 100644 --- a/app/screens/account-upgrade-flow/AccountType.tsx +++ b/app/screens/account-upgrade-flow/AccountType.tsx @@ -7,9 +7,13 @@ import { RootStackParamList } from "@app/navigation/stack-param-lists" // components import { Screen } from "@app/components/screen" +// hooks +import { useLevel } from "@app/graphql/level-context" + // store import { useAppDispatch } from "@app/store/redux" import { setAccountUpgrade } from "@app/store/redux/slices/accountUpgradeSlice" +import { AccountLevel } from "@app/graphql/generated" type Props = StackScreenProps @@ -17,6 +21,7 @@ const AccountType: React.FC = ({ navigation }) => { const dispatch = useAppDispatch() const styles = useStyles() const { colors } = useTheme().theme + const { currentLevel } = useLevel() const onPress = (accountType: string) => { dispatch(setAccountUpgrade({ accountType })) @@ -25,18 +30,20 @@ const AccountType: React.FC = ({ navigation }) => { return ( - onPress("personal")}> - - - - Personal - - - For individual use, no additional info needed - - - - + {currentLevel === AccountLevel.Zero && ( + onPress("personal")}> + + + + Personal + + + For individual use, no additional info needed + + + + + )} onPress("pro")}> From 5d8fd7ce882070a83d6fa8521fca14ebd9a9220d Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 29 Apr 2025 12:13:49 +0500 Subject: [PATCH 19/81] Added logic to validate full name and phone number, and send a verification code in the PersonalInformation screen. --- .../PersonalInformation.tsx | 118 ++++++++++++++++-- 1 file changed, 111 insertions(+), 7 deletions(-) diff --git a/app/screens/account-upgrade-flow/PersonalInformation.tsx b/app/screens/account-upgrade-flow/PersonalInformation.tsx index a004e1a9d..2bb21cdd0 100644 --- a/app/screens/account-upgrade-flow/PersonalInformation.tsx +++ b/app/screens/account-upgrade-flow/PersonalInformation.tsx @@ -1,8 +1,10 @@ -import React from "react" +import React, { useEffect, useState } from "react" import { View } from "react-native" import { makeStyles } from "@rneui/themed" import { StackScreenProps } from "@react-navigation/stack" import { RootStackParamList } from "@app/navigation/stack-param-lists" +import { AccountLevel, PhoneCodeChannelType } from "@app/graphql/generated" +import { parsePhoneNumber } from "libphonenumber-js/mobile" // components import { Screen } from "@app/components/screen" @@ -13,18 +15,83 @@ import { InputField, PhoneNumber } from "@app/components/account-upgrade-flow" import { useAppDispatch, useAppSelector } from "@app/store/redux" import { setPersonalInfo } from "@app/store/redux/slices/accountUpgradeSlice" +// hooks +import { + RequestPhoneCodeStatus, + useRequestPhoneCodeLogin, +} from "../phone-auth-screen/request-phone-code-login" +import { useLevel } from "@app/graphql/level-context" +import { useI18nContext } from "@app/i18n/i18n-react" +import { useActivityIndicator } from "@app/hooks" + type Props = StackScreenProps const PersonalInformation: React.FC = ({ navigation }) => { const styles = useStyles() - + const { currentLevel } = useLevel() + const { LL } = useI18nContext() + const { toggleActivityIndicator } = useActivityIndicator() const dispatch = useAppDispatch() const { fullName, countryCode, phoneNumber, email } = useAppSelector( (state) => state.accountUpgrade.personalInfo, ) - const onPressNext = () => { - navigation.navigate("BusinessInformation") + const [fullNameErr, setFullNameErr] = useState() + const [phoneNumberErr, setPhoneNumberErr] = useState() + + const { + submitPhoneNumber, + captchaLoading, + status, + setPhoneNumber, + isSmsSupported, + isWhatsAppSupported, + phoneCodeChannel, + error, + validatedPhoneNumber, + setCountryCode, + } = useRequestPhoneCodeLogin() + + console.log("PERSONAL INFO STATUS >>>>>>>>>>>>", status) + console.log("PERSONAL INFO ERROR >>>>>>>>>>>>", error) + + useEffect(() => { + if ( + status === RequestPhoneCodeStatus.CompletingCaptcha || + status === RequestPhoneCodeStatus.RequestingCode + ) { + toggleActivityIndicator(true) + } else { + toggleActivityIndicator(false) + } + }, [status]) + + useEffect(() => { + if (status === RequestPhoneCodeStatus.SuccessRequestingCode) { + navigation.navigate("Validation", { + phone: validatedPhoneNumber || "", + channel: phoneCodeChannel, + }) + } + }, [status, phoneCodeChannel, validatedPhoneNumber, navigation]) + + const onPressNext = async (channel?: PhoneCodeChannelType) => { + try { + const parsedPhoneNumber = parsePhoneNumber(phoneNumber, countryCode) + if (fullName.length < 2) { + setFullNameErr("Name must be at least 2 characters") + } else if (!parsedPhoneNumber?.isValid()) { + setPhoneNumberErr("Please enter a valid phone number") + } else { + if (currentLevel === AccountLevel.Zero) { + submitPhoneNumber(channel) + } else { + navigation.navigate("BusinessInformation") + } + } + } catch (err) { + console.log("Personal information error: ", err) + } } return ( @@ -34,13 +101,21 @@ const PersonalInformation: React.FC = ({ navigation }) => { label="Full Name" placeholder="John Doe" value={fullName} + errorMsg={fullNameErr} onChangeText={(val) => dispatch(setPersonalInfo({ fullName: val }))} /> dispatch(setPersonalInfo({ countryCode: val }))} - setPhoneNumber={(val) => dispatch(setPersonalInfo({ phoneNumber: val }))} + errorMsg={phoneNumberErr} + setCountryCode={(val) => { + setCountryCode(val) + dispatch(setPersonalInfo({ countryCode: val })) + }} + setPhoneNumber={(val) => { + setPhoneNumber(val) + dispatch(setPersonalInfo({ phoneNumber: val })) + }} /> = ({ navigation }) => { onChangeText={(val) => dispatch(setPersonalInfo({ email: val }))} /> - + + {currentLevel === AccountLevel.Zero ? ( + <> + {isSmsSupported && ( + onPressNext(PhoneCodeChannelType.Sms)} + btnStyle={isWhatsAppSupported ? { marginBottom: 10 } : {}} + /> + )} + {isWhatsAppSupported && ( + onPressNext(PhoneCodeChannelType.Whatsapp)} + /> + )} + + ) : ( + + )} + ) } From 11290d388ca4fcb5fea0805a83806a5dbc3a9a68 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 29 Apr 2025 16:16:31 +0500 Subject: [PATCH 20/81] add a validation and navigation logic in BusinessInformation screen --- .../BusinessInformation.tsx | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/app/screens/account-upgrade-flow/BusinessInformation.tsx b/app/screens/account-upgrade-flow/BusinessInformation.tsx index e0d8d4d7f..7b2a1b5f2 100644 --- a/app/screens/account-upgrade-flow/BusinessInformation.tsx +++ b/app/screens/account-upgrade-flow/BusinessInformation.tsx @@ -19,12 +19,26 @@ const BusinessInformation: React.FC = ({ navigation }) => { const styles = useStyles() const dispatch = useAppDispatch() - const { businessName, businessAddress } = useAppSelector( - (state) => state.accountUpgrade.businessInfo, - ) + const { + accountType, + businessInfo: { businessName, businessAddress }, + } = useAppSelector((state) => state.accountUpgrade) + + const [businessNameErr, setBusinessNameErr] = useState() + const [businessAddressErr, setBusinessAddressErr] = useState() const onPressNext = () => { - navigation.navigate("BankInformation") + if (businessName.length < 2) { + setBusinessNameErr("Business name must be at least 2 characters") + } else if (businessAddress.split(",").length <= 1) { + setBusinessAddressErr("Please enter a valid address") + } else { + if (accountType === "pro") { + navigation.navigate("AccountUpgradeSuccess") + } else { + navigation.navigate("BankInformation") + } + } } return ( @@ -34,14 +48,21 @@ const BusinessInformation: React.FC = ({ navigation }) => { label="Business Name" placeholder={"Your business name"} value={businessName} + errorMsg={businessNameErr} onChangeText={(val) => dispatch(setBusinessInfo({ businessName: val }))} /> - + ) } From 5a4a549a0b258c7d7676030be1fb7dc06dda5ca6 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Wed, 30 Apr 2025 13:47:38 +0500 Subject: [PATCH 21/81] improve validation check logic in BusinessInformation and PersonalInformation screens --- .../account-upgrade-flow/AccountType.tsx | 2 +- .../BusinessInformation.tsx | 22 +++++++++---- .../PersonalInformation.tsx | 32 +++++++++++++------ 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/app/screens/account-upgrade-flow/AccountType.tsx b/app/screens/account-upgrade-flow/AccountType.tsx index f02c3a816..ca2bdf175 100644 --- a/app/screens/account-upgrade-flow/AccountType.tsx +++ b/app/screens/account-upgrade-flow/AccountType.tsx @@ -3,6 +3,7 @@ import { TouchableOpacity, View } from "react-native" import { StackScreenProps } from "@react-navigation/stack" import { Icon, makeStyles, Text, useTheme } from "@rneui/themed" import { RootStackParamList } from "@app/navigation/stack-param-lists" +import { AccountLevel } from "@app/graphql/generated" // components import { Screen } from "@app/components/screen" @@ -13,7 +14,6 @@ import { useLevel } from "@app/graphql/level-context" // store import { useAppDispatch } from "@app/store/redux" import { setAccountUpgrade } from "@app/store/redux/slices/accountUpgradeSlice" -import { AccountLevel } from "@app/graphql/generated" type Props = StackScreenProps diff --git a/app/screens/account-upgrade-flow/BusinessInformation.tsx b/app/screens/account-upgrade-flow/BusinessInformation.tsx index 7b2a1b5f2..287b6a85c 100644 --- a/app/screens/account-upgrade-flow/BusinessInformation.tsx +++ b/app/screens/account-upgrade-flow/BusinessInformation.tsx @@ -16,23 +16,28 @@ import { setBusinessInfo } from "@app/store/redux/slices/accountUpgradeSlice" type Props = StackScreenProps const BusinessInformation: React.FC = ({ navigation }) => { + const dispatch = useAppDispatch() const styles = useStyles() - const dispatch = useAppDispatch() + const [businessNameErr, setBusinessNameErr] = useState() + const [businessAddressErr, setBusinessAddressErr] = useState() const { accountType, businessInfo: { businessName, businessAddress }, } = useAppSelector((state) => state.accountUpgrade) - const [businessNameErr, setBusinessNameErr] = useState() - const [businessAddressErr, setBusinessAddressErr] = useState() - const onPressNext = () => { + let hasError = false + if (businessName.length < 2) { setBusinessNameErr("Business name must be at least 2 characters") - } else if (businessAddress.split(",").length <= 1) { + hasError = true + } + if (businessAddress.split(",").length <= 1) { setBusinessAddressErr("Please enter a valid address") - } else { + hasError = true + } + if (!hasError) { if (accountType === "pro") { navigation.navigate("AccountUpgradeSuccess") } else { @@ -49,7 +54,10 @@ const BusinessInformation: React.FC = ({ navigation }) => { placeholder={"Your business name"} value={businessName} errorMsg={businessNameErr} - onChangeText={(val) => dispatch(setBusinessInfo({ businessName: val }))} + onChangeText={(val) => { + setBusinessNameErr(undefined) + dispatch(setBusinessInfo({ businessName: val })) + }} /> const PersonalInformation: React.FC = ({ navigation }) => { + const dispatch = useAppDispatch() const styles = useStyles() const { currentLevel } = useLevel() const { LL } = useI18nContext() const { toggleActivityIndicator } = useActivityIndicator() - const dispatch = useAppDispatch() - const { fullName, countryCode, phoneNumber, email } = useAppSelector( - (state) => state.accountUpgrade.personalInfo, - ) const [fullNameErr, setFullNameErr] = useState() const [phoneNumberErr, setPhoneNumberErr] = useState() + const { fullName, countryCode, phoneNumber, email } = useAppSelector( + (state) => state.accountUpgrade.personalInfo, + ) const { submitPhoneNumber, @@ -77,13 +77,18 @@ const PersonalInformation: React.FC = ({ navigation }) => { const onPressNext = async (channel?: PhoneCodeChannelType) => { try { + let hasError = false const parsedPhoneNumber = parsePhoneNumber(phoneNumber, countryCode) - if (fullName.length < 2) { + if (fullName?.length < 2) { setFullNameErr("Name must be at least 2 characters") - } else if (!parsedPhoneNumber?.isValid()) { + hasError = true + } + if (!parsedPhoneNumber?.isValid()) { setPhoneNumberErr("Please enter a valid phone number") - } else { - if (currentLevel === AccountLevel.Zero) { + hasError = true + } + if (!hasError) { + if (currentLevel === AccountLevel.Zero && channel) { submitPhoneNumber(channel) } else { navigation.navigate("BusinessInformation") @@ -91,6 +96,8 @@ const PersonalInformation: React.FC = ({ navigation }) => { } } catch (err) { console.log("Personal information error: ", err) + setFullNameErr("Name must be at least 2 characters") + setPhoneNumberErr("Please enter a valid phone number") } } @@ -102,17 +109,22 @@ const PersonalInformation: React.FC = ({ navigation }) => { placeholder="John Doe" value={fullName} errorMsg={fullNameErr} - onChangeText={(val) => dispatch(setPersonalInfo({ fullName: val }))} + onChangeText={(val) => { + setFullNameErr(undefined) + dispatch(setPersonalInfo({ fullName: val })) + }} /> { + setPhoneNumberErr(undefined) setCountryCode(val) dispatch(setPersonalInfo({ countryCode: val })) }} setPhoneNumber={(val) => { + setPhoneNumberErr(undefined) setPhoneNumber(val) dispatch(setPersonalInfo({ phoneNumber: val })) }} @@ -131,6 +143,7 @@ const PersonalInformation: React.FC = ({ navigation }) => { {isSmsSupported && ( onPressNext(PhoneCodeChannelType.Sms)} btnStyle={isWhatsAppSupported ? { marginBottom: 10 } : {}} @@ -140,6 +153,7 @@ const PersonalInformation: React.FC = ({ navigation }) => { Date: Wed, 30 Apr 2025 13:48:30 +0500 Subject: [PATCH 22/81] add a loading spinner in validation screen api request --- app/screens/account-upgrade-flow/Validation.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/screens/account-upgrade-flow/Validation.tsx b/app/screens/account-upgrade-flow/Validation.tsx index 8b7fa5472..1c80dd8bd 100644 --- a/app/screens/account-upgrade-flow/Validation.tsx +++ b/app/screens/account-upgrade-flow/Validation.tsx @@ -9,7 +9,7 @@ import { Screen } from "@app/components/screen" import { InputField } from "@app/components/account-upgrade-flow" // hooks -import { useAppConfig } from "@app/hooks" +import { useActivityIndicator, useAppConfig } from "@app/hooks" import { useUserLoginUpgradeMutation } from "@app/graphql/generated" import { useI18nContext } from "@app/i18n/i18n-react" import { useAppSelector } from "@app/store/redux" @@ -21,10 +21,11 @@ type Props = StackScreenProps const Validation: React.FC = ({ navigation, route }) => { const { phone, channel } = route.params + const styles = useStyles() const { accountType } = useAppSelector((state) => state.accountUpgrade) const { LL } = useI18nContext() const { saveToken } = useAppConfig() - const styles = useStyles() + const { toggleActivityIndicator } = useActivityIndicator() const [code, setCode] = useState() const [errorMsg, setErrorMsg] = useState() @@ -36,9 +37,11 @@ const Validation: React.FC = ({ navigation, route }) => { const send = useCallback( async (code: string) => { try { + toggleActivityIndicator(true) const { data } = await userLoginUpgradeMutation({ variables: { input: { phone, code } }, }) + toggleActivityIndicator(false) const success = data?.userLoginUpgrade?.success const authToken = data?.userLoginUpgrade?.authToken @@ -52,8 +55,8 @@ const Validation: React.FC = ({ navigation, route }) => { navigation.replace("BusinessInformation") } } else { - console.log(">>>>>>>>>>>>>>>>>", data.userLoginUpgrade) - setErrorMsg(data.userLoginUpgrade.errors[0].message) + console.log(">>>>>>>>>>>>>>>>>", data?.userLoginUpgrade) + setErrorMsg(data?.userLoginUpgrade.errors[0].message) } setCode("") } catch (err) { From bdf1872288af16650128a3ac5824a96e74c45d27 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Wed, 30 Apr 2025 13:52:50 +0500 Subject: [PATCH 23/81] add a input validation in BankInformation screen --- .../account-upgrade-flow/BankInformation.tsx | 81 ++++++++++++++++--- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/app/screens/account-upgrade-flow/BankInformation.tsx b/app/screens/account-upgrade-flow/BankInformation.tsx index 190bd92cd..ff36b585e 100644 --- a/app/screens/account-upgrade-flow/BankInformation.tsx +++ b/app/screens/account-upgrade-flow/BankInformation.tsx @@ -1,6 +1,8 @@ -import React from "react" +import React, { useState } from "react" import { ScrollView } from "react-native" import { makeStyles } from "@rneui/themed" +import { StackScreenProps } from "@react-navigation/stack" +import { RootStackParamList } from "@app/navigation/stack-param-lists" // components import { @@ -31,14 +33,51 @@ const currencies = [ { label: "XCG - Caribbean Guilder", value: "xcg" }, ] -const BankInformation = () => { - const styles = useStyles() +type Props = StackScreenProps +const BankInformation: React.FC = ({ navigation }) => { const dispatch = useAppDispatch() - const { bankName, bankBranch, accountType, currency, accountNumber, document } = + const styles = useStyles() + + const [nameErr, setNameErr] = useState() + const [branchErr, setBranchErr] = useState() + const [accountTypeErr, setAccountTypeErr] = useState() + const [currencyErr, setCurrencyErr] = useState() + const [accountNumErr, setAccountNumErr] = useState() + const [idDocumentErr, setIdDocumentErr] = useState() + const { bankName, bankBranch, accountType, currency, accountNumber, idDocument } = useAppSelector((state) => state.accountUpgrade.bankInfo) - const onPressNext = () => {} + const onPressNext = () => { + let hasError = false + if (bankName.length < 2) { + setNameErr("Bank name is required") + hasError = true + } + if (bankBranch.length < 2) { + setBranchErr("Branch is required") + hasError = true + } + if (!accountType) { + setAccountTypeErr("Account type is required") + hasError = true + } + if (!currency) { + setCurrencyErr("Currency is required") + hasError = true + } + if (accountNumber.length < 4) { + setAccountNumErr("Account number is required") + hasError = true + } + if (!idDocument) { + setIdDocumentErr("You must upload an ID document before proceeding") + hasError = true + } + if (!hasError) { + navigation.navigate("AccountUpgradeSuccess") + } + } return ( @@ -47,35 +86,55 @@ const BankInformation = () => { label="Bank Name" placeholder="Enter your bank name" value={bankName} - onChangeText={(val) => dispatch(setBankInfo({ bankName: val }))} + errorMsg={nameErr} + onChangeText={(val) => { + setNameErr(undefined) + dispatch(setBankInfo({ bankName: val })) + }} /> dispatch(setBankInfo({ bankBranch: val }))} + errorMsg={branchErr} + onChangeText={(val) => { + setBranchErr(undefined) + dispatch(setBankInfo({ bankBranch: val })) + }} /> dispatch(setBankInfo({ accountType: val }))} + errorMsg={accountTypeErr} + onChange={(val) => { + setAccountTypeErr(undefined) + dispatch(setBankInfo({ accountType: val })) + }} /> dispatch(setBankInfo({ currency: val }))} + errorMsg={currencyErr} + onChange={(val) => { + setCurrencyErr(undefined) + dispatch(setBankInfo({ currency: val })) + }} /> dispatch(setBankInfo({ accountNumber: val }))} + errorMsg={accountNumErr} + onChangeText={(val) => { + setAccountNumErr(undefined) + dispatch(setBankInfo({ accountNumber: val })) + }} /> - + From a339fffc15fcae92491552810214102509f83234 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Wed, 30 Apr 2025 14:07:39 +0500 Subject: [PATCH 24/81] fix PhoneNumber component UI and update accountUpgradeSlice --- .../account-upgrade-flow/PhoneNumber.tsx | 7 ++-- app/screens/account-upgrade-flow/Success.tsx | 2 +- app/screens/account-upgrade-flow/index.ts | 11 +++++- app/store/redux/slices/accountUpgradeSlice.ts | 35 ++++++++++--------- 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/app/components/account-upgrade-flow/PhoneNumber.tsx b/app/components/account-upgrade-flow/PhoneNumber.tsx index a903de1ac..96c32ae2b 100644 --- a/app/components/account-upgrade-flow/PhoneNumber.tsx +++ b/app/components/account-upgrade-flow/PhoneNumber.tsx @@ -19,8 +19,8 @@ import { useI18nContext } from "@app/i18n/i18n-react" import { useRequestPhoneCodeLogin } from "@app/screens/phone-auth-screen/request-phone-code-login" type Props = { - countryCode: string - phoneNumber: string + countryCode?: string + phoneNumber?: string errorMsg?: string setPhoneNumber: (number: string) => void setCountryCode: (countryCode: PhoneNumberCountryCode) => void @@ -102,7 +102,8 @@ const useStyles = makeStyles(({ colors }) => ({ marginBottom: 2, }, countryPicker: { - padding: 14.5, + flex: 1, + paddingHorizontal: 15, borderRadius: 10, borderWidth: 1, borderColor: colors.grey4, diff --git a/app/screens/account-upgrade-flow/Success.tsx b/app/screens/account-upgrade-flow/Success.tsx index 5af60882a..243533dc3 100644 --- a/app/screens/account-upgrade-flow/Success.tsx +++ b/app/screens/account-upgrade-flow/Success.tsx @@ -36,7 +36,7 @@ const Success: React.FC = ({ navigation }) => { - {`You successfully upgraded your account to ${accountType.toUpperCase()}`} + {`You successfully upgraded your account to ${accountType?.toUpperCase()}`} diff --git a/app/screens/account-upgrade-flow/index.ts b/app/screens/account-upgrade-flow/index.ts index 1697ad7df..372d50471 100644 --- a/app/screens/account-upgrade-flow/index.ts +++ b/app/screens/account-upgrade-flow/index.ts @@ -2,5 +2,14 @@ import AccountType from "./AccountType" import PersonalInformation from "./PersonalInformation" import BusinessInformation from "./BusinessInformation" import BankInformation from "./BankInformation" +import Validation from "./Validation" +import Success from "./Success" -export { AccountType, BusinessInformation, PersonalInformation, BankInformation } +export { + AccountType, + BusinessInformation, + PersonalInformation, + BankInformation, + Validation, + Success, +} diff --git a/app/store/redux/slices/accountUpgradeSlice.ts b/app/store/redux/slices/accountUpgradeSlice.ts index 6e7477dc5..7573ae939 100644 --- a/app/store/redux/slices/accountUpgradeSlice.ts +++ b/app/store/redux/slices/accountUpgradeSlice.ts @@ -1,10 +1,11 @@ import { createSlice } from "@reduxjs/toolkit" +import { CountryCode } from "libphonenumber-js" interface AccountUpgradeSlice { - accountType: "personal" | "pro" | "merchant" + accountType?: "personal" | "pro" | "merchant" personalInfo: { fullName: string - countryCode: string + countryCode: CountryCode phoneNumber: string email: string } @@ -18,31 +19,31 @@ interface AccountUpgradeSlice { accountType: string currency: string accountNumber: string - document: string + idDocument: string } loading: boolean error: string } const initialState: AccountUpgradeSlice = { - accountType: null, + accountType: undefined, personalInfo: { - fullName: null, + fullName: "", countryCode: "JM", - phoneNumber: null, - email: null, + phoneNumber: "", + email: "", }, businessInfo: { - businessName: null, - businessAddress: null, + businessName: "", + businessAddress: "", }, bankInfo: { - bankName: null, - bankBranch: null, - accountType: null, - currency: null, - accountNumber: null, - document: null, + bankName: "", + bankBranch: "", + accountType: "", + currency: "", + accountNumber: "", + idDocument: "", }, loading: false, error: "", @@ -76,7 +77,7 @@ export const accountUpgradeSlice = createSlice({ ...state, error: action.payload, }), - resetUserSlice: () => ({ + resetAccountUpgrade: () => ({ ...initialState, }), }, @@ -89,6 +90,6 @@ export const { setBankInfo, setLoading, setError, - resetUserSlice, + resetAccountUpgrade, } = accountUpgradeSlice.actions export default accountUpgradeSlice.reducer From 8036d5bc0ecaf641b5b2052d6906e200a85fb0ae Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Mon, 5 May 2025 20:31:08 +0500 Subject: [PATCH 25/81] Add modal to show searched addresses in AddressField component --- .../account-upgrade-flow/AddressField.tsx | 115 ++++++++++++++---- 1 file changed, 89 insertions(+), 26 deletions(-) diff --git a/app/components/account-upgrade-flow/AddressField.tsx b/app/components/account-upgrade-flow/AddressField.tsx index 9a45467a8..0d4cad90d 100644 --- a/app/components/account-upgrade-flow/AddressField.tsx +++ b/app/components/account-upgrade-flow/AddressField.tsx @@ -1,48 +1,95 @@ -import React, { useState } from "react" -import { View } from "react-native" +import React, { useEffect, useRef, useState } from "react" +import { Modal, Platform, TouchableOpacity, View } from "react-native" import { makeStyles, Text, useTheme } from "@rneui/themed" -import { GooglePlacesAutocomplete } from "react-native-google-places-autocomplete" +import { + GooglePlacesAutocomplete, + GooglePlacesAutocompleteRef, +} from "react-native-google-places-autocomplete" + +// components +import { PrimaryBtn } from "../buttons" + +// env +import { GOOGLE_PLACE_API_KEY } from "@env" type Props = { label: string placeholder: string - errorMsg: string + value: string + errorMsg?: string + onAddressSelect: (val: string) => void } -const AddressField: React.FC = ({ label, placeholder, errorMsg }) => { +const AddressField: React.FC = ({ + label, + placeholder, + value, + errorMsg, + onAddressSelect, +}) => { const styles = useStyles() const { colors } = useTheme().theme + const ref = useRef(null) const [isFocused, setIsFocused] = useState(false) + const [isVisible, setIsVisible] = useState(false) + + useEffect(() => { + if (isVisible && ref.current) { + ref.current.focus() + } + }, [isVisible, ref.current]) return ( {label} - { - // 'details' is provided when fetchDetails = true - console.log(data, details) - }} - query={{ - key: "YOUR API KEY", - language: "en", - }} - textInputProps={{ - onFocus: () => setIsFocused(true), - onBlur: () => setIsFocused(false), - }} - /> + setIsVisible(true)}> + + {!!value ? value : placeholder} + + {!!errorMsg && ( - + {errorMsg} )} + setIsVisible(false)} + > + + console.log("Google places auto complete", err)} + onNotFound={() => console.log("Google places auto complete not found")} + onPress={(data) => { + setIsVisible(false) + onAddressSelect(data.description) + }} + query={{ + key: GOOGLE_PLACE_API_KEY, + language: "en", + }} + styles={{ + textInput: [ + styles.googlePlace, + isFocused ? { borderColor: colors.primary } : {}, + ], + }} + textInputProps={{ + onFocus: () => setIsFocused(true), + onBlur: () => setIsFocused(false), + }} + /> + setIsVisible(false)} /> + + ) } @@ -50,9 +97,25 @@ const AddressField: React.FC = ({ label, placeholder, errorMsg }) => { export default AddressField const useStyles = makeStyles(({ colors }) => ({ - container: { - paddingVertical: 25, + input: { + paddingHorizontal: 15, + paddingVertical: 20, + marginTop: 5, + marginBottom: 2, + borderRadius: 10, + borderWidth: 1, + borderColor: colors.grey4, + backgroundColor: colors.grey5, + }, + modal: { + flex: 1, + backgroundColor: colors.white, + padding: 20, + }, + googlePlace: { + height: Platform.OS === "ios" ? 51 : 60, paddingHorizontal: 15, + padding: 20, marginTop: 5, marginBottom: 15, borderWidth: 1, From 117b92ddfdae610967cef855b5f3a6b78511e5b2 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Mon, 5 May 2025 20:34:15 +0500 Subject: [PATCH 26/81] add validation check for photo upload and add a logic to upload photo to supabase storage --- .../account-upgrade-flow/InputField.tsx | 1 + .../account-upgrade-flow/PhotoUploadField.tsx | 72 ++++++++++++++++--- app/store/redux/slices/accountUpgradeSlice.ts | 5 +- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/app/components/account-upgrade-flow/InputField.tsx b/app/components/account-upgrade-flow/InputField.tsx index 24953eef7..8d74e4f89 100644 --- a/app/components/account-upgrade-flow/InputField.tsx +++ b/app/components/account-upgrade-flow/InputField.tsx @@ -35,6 +35,7 @@ const InputField: React.FC = ({ setIsFocused(true)} diff --git a/app/components/account-upgrade-flow/PhotoUploadField.tsx b/app/components/account-upgrade-flow/PhotoUploadField.tsx index 470421c84..f898a2419 100644 --- a/app/components/account-upgrade-flow/PhotoUploadField.tsx +++ b/app/components/account-upgrade-flow/PhotoUploadField.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useRef, useState } from "react" -import { Alert, Linking, Modal, TouchableOpacity, View } from "react-native" +import { Alert, Image, Linking, Modal, TouchableOpacity, View } from "react-native" import { Icon, makeStyles, Text, useTheme } from "@rneui/themed" -import { launchImageLibrary } from "react-native-image-picker" +import { Asset, launchImageLibrary } from "react-native-image-picker" import { Camera, CameraRuntimeError, @@ -23,12 +23,24 @@ import PhotoAdd from "@app/assets/icons/photo-add.svg" // utils import { toastShow } from "@app/utils/toast" +const MAX_FILE_SIZE = 5 * 1024 * 1024 // File size limit in bytes (5MB) +const ALLOWED_FILE_TYPES = ["image/jpeg", "image/png", "image/jpg", "image/heic"] + type Props = { label: string + photo?: Asset errorMsg?: string + onPhotoUpload: (val: Asset) => void + setErrorMsg: (val: string) => void } -const PhotoUploadField: React.FC = ({ label, errorMsg }) => { +const PhotoUploadField: React.FC = ({ + label, + photo, + errorMsg, + onPhotoUpload, + setErrorMsg, +}) => { const styles = useStyles() const { colors } = useTheme().theme const { LL } = useI18nContext() @@ -61,25 +73,58 @@ const PhotoUploadField: React.FC = ({ label, errorMsg }) => { const showImagePicker = async () => { try { const result = await launchImageLibrary({ mediaType: "photo" }) + setIsCameraVisible(false) if (result.errorCode === "permission") { toastShow({ message: (translations) => translations.ScanningQRCodeScreen.imageLibraryPermissionsNotGranted(), }) } - if (result.assets && result.assets.length > 0 && result.assets[0].uri) { - console.log(">>>>>>>>>>>>>>>>", result) + if ( + result.assets && + result.assets.length > 0 && + result.assets[0].uri && + result.assets[0].type && + result.assets[0].fileSize + ) { + if (!ALLOWED_FILE_TYPES.includes(result.assets[0].type)) { + setErrorMsg("Please upload a valid image (JPG, PNG, or HEIC)") + } else if (result?.assets[0]?.fileSize > MAX_FILE_SIZE) { + setErrorMsg("File size exceeds 5MB limit") + } else { + onPhotoUpload(result.assets[0]) + } } } catch (err: unknown) { - if (err instanceof Error) { - Alert.alert(err.toString()) - } + console.log("Image Picker Error: ", err) } } const takePhoto = async () => { - const photo = await camera?.current?.takePhoto() - console.log(">>>>>>>>>>>>>>>", photo) + try { + const file = await camera?.current?.takePhoto() + setIsCameraVisible(false) + + if (file) { + const result = await fetch(`file://${file?.path}`) + const data = await result.blob() + + if (!ALLOWED_FILE_TYPES.includes(data.type)) { + setErrorMsg("Please upload a valid image (JPG, PNG, or HEIC)") + } else if (data.size > MAX_FILE_SIZE) { + setErrorMsg("File size exceeds 5MB limit") + } else { + onPhotoUpload({ + uri: `file://${file?.path}`, + type: data.type, + fileSize: data.size, + fileName: file.path.split("/").pop(), + }) + } + } + } catch (err) { + console.log("Take Photo Error", err) + } } return ( @@ -88,6 +133,7 @@ const PhotoUploadField: React.FC = ({ label, errorMsg }) => { {label} setIsCameraVisible(true)}> + {!!photo && } {!!errorMsg && ( @@ -173,6 +219,12 @@ const useStyles = makeStyles(({ colors }) => ({ borderWidth: 1, borderColor: colors.grey4, backgroundColor: colors.grey5, + overflow: "hidden", + }, + img: { + position: "absolute", + width: "100%", + height: "100%", }, row: { flexDirection: "row", diff --git a/app/store/redux/slices/accountUpgradeSlice.ts b/app/store/redux/slices/accountUpgradeSlice.ts index 7573ae939..1f0e07f5c 100644 --- a/app/store/redux/slices/accountUpgradeSlice.ts +++ b/app/store/redux/slices/accountUpgradeSlice.ts @@ -1,5 +1,6 @@ import { createSlice } from "@reduxjs/toolkit" import { CountryCode } from "libphonenumber-js" +import { Asset } from "react-native-image-picker" interface AccountUpgradeSlice { accountType?: "personal" | "pro" | "merchant" @@ -19,7 +20,7 @@ interface AccountUpgradeSlice { accountType: string currency: string accountNumber: string - idDocument: string + idDocument?: Asset } loading: boolean error: string @@ -43,7 +44,7 @@ const initialState: AccountUpgradeSlice = { accountType: "", currency: "", accountNumber: "", - idDocument: "", + idDocument: undefined, }, loading: false, error: "", From d149bd77a3de796e354cd51c8766e423dcaf797a Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Mon, 5 May 2025 20:35:33 +0500 Subject: [PATCH 27/81] update BankInformation screen --- app/screens/account-upgrade-flow/BankInformation.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/screens/account-upgrade-flow/BankInformation.tsx b/app/screens/account-upgrade-flow/BankInformation.tsx index ff36b585e..e1e349a1b 100644 --- a/app/screens/account-upgrade-flow/BankInformation.tsx +++ b/app/screens/account-upgrade-flow/BankInformation.tsx @@ -134,7 +134,13 @@ const BankInformation: React.FC = ({ navigation }) => { dispatch(setBankInfo({ accountNumber: val })) }} /> - + dispatch(setBankInfo({ idDocument: val }))} + setErrorMsg={setIdDocumentErr} + /> From 07d50cae6294737df3b2db58b340dc2ce2b3fa1f Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Mon, 5 May 2025 20:36:18 +0500 Subject: [PATCH 28/81] install @supabase/supabase-js package --- package.json | 1 + yarn.lock | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/package.json b/package.json index dd09fa006..8db496676 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "@sinonjs/text-encoding": "^0.7.2", "@snort/system": "^1.1.8", "@stablelib/xchacha20": "^1.0.1", + "@supabase/supabase-js": "^2.49.4", "apollo3-cache-persist": "^0.14.1", "axios": "^1.6.2", "bech32": "^2.0.0", diff --git a/yarn.lock b/yarn.lock index d4bc41c7a..bfe49fb64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6397,6 +6397,63 @@ regenerator-runtime "^0.13.7" resolve-from "^5.0.0" +"@supabase/auth-js@2.69.1": + version "2.69.1" + resolved "https://registry.yarnpkg.com/@supabase/auth-js/-/auth-js-2.69.1.tgz#fcf310d24dfab823ffbf22191e6ceaef933360d8" + integrity sha512-FILtt5WjCNzmReeRLq5wRs3iShwmnWgBvxHfqapC/VoljJl+W8hDAyFmf1NVw3zH+ZjZ05AKxiKxVeb0HNWRMQ== + dependencies: + "@supabase/node-fetch" "^2.6.14" + +"@supabase/functions-js@2.4.4": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@supabase/functions-js/-/functions-js-2.4.4.tgz#45fcd94d546bdfa66d01f93a796ca0304ec154b8" + integrity sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA== + dependencies: + "@supabase/node-fetch" "^2.6.14" + +"@supabase/node-fetch@2.6.15", "@supabase/node-fetch@^2.6.14": + version "2.6.15" + resolved "https://registry.yarnpkg.com/@supabase/node-fetch/-/node-fetch-2.6.15.tgz#731271430e276983191930816303c44159e7226c" + integrity sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ== + dependencies: + whatwg-url "^5.0.0" + +"@supabase/postgrest-js@1.19.4": + version "1.19.4" + resolved "https://registry.yarnpkg.com/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz#41de1e4310ce8ddba87becd7e0878f4ad7a659a2" + integrity sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw== + dependencies: + "@supabase/node-fetch" "^2.6.14" + +"@supabase/realtime-js@2.11.2": + version "2.11.2" + resolved "https://registry.yarnpkg.com/@supabase/realtime-js/-/realtime-js-2.11.2.tgz#7f7399c326be717eadc9d5e259f9e2690fbf83dd" + integrity sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w== + dependencies: + "@supabase/node-fetch" "^2.6.14" + "@types/phoenix" "^1.5.4" + "@types/ws" "^8.5.10" + ws "^8.18.0" + +"@supabase/storage-js@2.7.1": + version "2.7.1" + resolved "https://registry.yarnpkg.com/@supabase/storage-js/-/storage-js-2.7.1.tgz#761482f237deec98a59e5af1ace18c7a5e0a69af" + integrity sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA== + dependencies: + "@supabase/node-fetch" "^2.6.14" + +"@supabase/supabase-js@^2.49.4": + version "2.49.4" + resolved "https://registry.yarnpkg.com/@supabase/supabase-js/-/supabase-js-2.49.4.tgz#5be79f0bf5e6ba9aa5be0775fb0073871834e4d3" + integrity sha512-jUF0uRUmS8BKt37t01qaZ88H9yV1mbGYnqLeuFWLcdV+x1P4fl0yP9DGtaEhFPZcwSom7u16GkLEH9QJZOqOkw== + dependencies: + "@supabase/auth-js" "2.69.1" + "@supabase/functions-js" "2.4.4" + "@supabase/node-fetch" "2.6.15" + "@supabase/postgrest-js" "1.19.4" + "@supabase/realtime-js" "2.11.2" + "@supabase/storage-js" "2.7.1" + "@svgr/babel-plugin-add-jsx-attribute@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22" @@ -7035,6 +7092,11 @@ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== +"@types/phoenix@^1.5.4": + version "1.6.6" + resolved "https://registry.yarnpkg.com/@types/phoenix/-/phoenix-1.6.6.tgz#3c1ab53fd5a23634b8e37ea72ccacbf07fbc7816" + integrity sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A== + "@types/prettier@^2.1.5": version "2.7.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" @@ -7282,6 +7344,13 @@ dependencies: "@types/node" "*" +"@types/ws@^8.5.10": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" + integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" @@ -26331,6 +26400,11 @@ ws@8.13.0, ws@8.16.0, "ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^6.2.2, ws@^6.2.3, ws@ resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== +ws@^8.18.0: + version "8.18.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" + integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== + x-default-browser@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/x-default-browser/-/x-default-browser-0.4.0.tgz#70cf0da85da7c0ab5cb0f15a897f2322a6bdd481" From cda5725f90dae6530f2db07d1827130ccdb5a123 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Mon, 5 May 2025 20:42:01 +0500 Subject: [PATCH 29/81] Google place api key, supabase url and supabase key are added in .env file --- app/types/declaration.d.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/types/declaration.d.ts b/app/types/declaration.d.ts index ab0a2eafe..1212e4399 100644 --- a/app/types/declaration.d.ts +++ b/app/types/declaration.d.ts @@ -30,4 +30,7 @@ declare module "@env" { export const API_KEY: string export const GREENLIGHT_PARTNER_CERT: string export const GREENLIGHT_PARTNER_KEY: string + export const GOOGLE_PLACE_API_KEY: string + export const SUPABASE_URL: string + export const SUPABASE_KEY: string } From 46e329794cb9166957cd2c035f29242cb171d16a Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Mon, 5 May 2025 20:42:56 +0500 Subject: [PATCH 30/81] update BusinessInformation screen --- app/screens/account-upgrade-flow/BusinessInformation.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/screens/account-upgrade-flow/BusinessInformation.tsx b/app/screens/account-upgrade-flow/BusinessInformation.tsx index 287b6a85c..bbbf60a52 100644 --- a/app/screens/account-upgrade-flow/BusinessInformation.tsx +++ b/app/screens/account-upgrade-flow/BusinessInformation.tsx @@ -62,7 +62,9 @@ const BusinessInformation: React.FC = ({ navigation }) => { dispatch(setBusinessInfo({ businessAddress: val }))} /> Date: Mon, 5 May 2025 20:43:32 +0500 Subject: [PATCH 31/81] add a logic to set up supabase client and upload file to supabase storage --- app/supabase/index.ts | 70 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 app/supabase/index.ts diff --git a/app/supabase/index.ts b/app/supabase/index.ts new file mode 100644 index 000000000..1ec648d82 --- /dev/null +++ b/app/supabase/index.ts @@ -0,0 +1,70 @@ +import { createClient } from "@supabase/supabase-js" + +// env +import { SUPABASE_URL, SUPABASE_KEY } from "@env" + +// Create Supabase client +const supabase = createClient(SUPABASE_URL, SUPABASE_KEY) + +// Upload file using standard upload +export async function uploadFile(file: any) { + try { + // Create a cryptographically secure unique file name using UUID v4 pattern + const fileExt = file.fileName.split(".").pop() + // Generate a UUID-like string with timestamp for better security + const timestamp = Date.now().toString(36) + const random1 = crypto + .getRandomValues(new Uint8Array(8)) + .reduce((acc, val) => acc + val.toString(16).padStart(2, "0"), "") + const random2 = crypto + .getRandomValues(new Uint8Array(4)) + .reduce((acc, val) => acc + val.toString(16).padStart(2, "0"), "") + const random3 = crypto + .getRandomValues(new Uint8Array(4)) + .reduce((acc, val) => acc + val.toString(16).padStart(2, "0"), "") + const random4 = crypto + .getRandomValues(new Uint8Array(12)) + .reduce((acc, val) => acc + val.toString(16).padStart(2, "0"), "") + const secureFileName = `${random1}-${random2}-${random3}-${random4}-${timestamp}` + const fileName = `${secureFileName}.${fileExt}` + const filePath = `${fileName}` + + console.log(">>>>>>>>>>", filePath) + + const { data, error } = await supabase.storage + .from("id-documents") + .upload(filePath, file) + if (error) { + // Handle error + console.log("UPLOAD ERROR>>>>>>>", error) + } else { + // Handle success + console.log("UPLOAD SUCCESS>>>>>>>", data) + } + return + } catch (err) { + console.log("Upload File Err", err) + } +} + +export async function insertUser() { + try { + const { data, error } = await supabase.from("signups").insert([ + { + name: "test", + phone: "+821024734353", + email: "test@example.com", + account_type: "personal", + terms_accepted: true, + }, + ]) + console.log(data, error) + if (error) { + console.error("Insert error:", error) + } else { + console.log("Insert success:", data) + } + } catch (err) { + console.log("????????>>>>>>>", err) + } +} From d77d4cab013d1e8336b9ff98c9fedede4c90797b Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 6 May 2025 12:41:08 +0500 Subject: [PATCH 32/81] Implement a logic to save user data to supabase database in validation screen if user is upgrading to PERSONAL account type --- .../account-upgrade-flow/Validation.tsx | 13 +++- app/supabase/index.ts | 76 +++++++++++++++++-- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/app/screens/account-upgrade-flow/Validation.tsx b/app/screens/account-upgrade-flow/Validation.tsx index 1c80dd8bd..e62ab319c 100644 --- a/app/screens/account-upgrade-flow/Validation.tsx +++ b/app/screens/account-upgrade-flow/Validation.tsx @@ -16,13 +16,14 @@ import { useAppSelector } from "@app/store/redux" // utils import { PhoneCodeChannelToFriendlyName } from "../phone-auth-screen/request-phone-code-login" +import { insertUser } from "@app/supabase" type Props = StackScreenProps const Validation: React.FC = ({ navigation, route }) => { const { phone, channel } = route.params const styles = useStyles() - const { accountType } = useAppSelector((state) => state.accountUpgrade) + const { accountType, personalInfo } = useAppSelector((state) => state.accountUpgrade) const { LL } = useI18nContext() const { saveToken } = useAppConfig() const { toggleActivityIndicator } = useActivityIndicator() @@ -50,7 +51,15 @@ const Validation: React.FC = ({ navigation, route }) => { saveToken(authToken) } if (accountType === "personal") { - navigation.replace("AccountUpgradeSuccess") + // insert user to supabase database + const res = await insertUser({ + account_type: accountType, + name: personalInfo.fullName, + phone: phone, + email: personalInfo.email, + }) + if (res) navigation.replace("AccountUpgradeSuccess") + else alert("Something went wrong. Please, try again later.") } else { navigation.replace("BusinessInformation") } diff --git a/app/supabase/index.ts b/app/supabase/index.ts index 1ec648d82..c8ef07a91 100644 --- a/app/supabase/index.ts +++ b/app/supabase/index.ts @@ -6,6 +6,35 @@ import { SUPABASE_URL, SUPABASE_KEY } from "@env" // Create Supabase client const supabase = createClient(SUPABASE_URL, SUPABASE_KEY) +type User = { + account_currency?: string + account_type?: string + attempt?: string + bank_account_number?: string + bank_account_type?: string + bank_branch?: string + bank_name?: string + business_address?: string + business_name?: string + client_version?: string + created_at?: string + device_info?: string + email?: string + id?: string + id_image_url?: string + latitude?: string + longitude?: string + name: string + phone: string + signup_completed?: boolean + submission_source?: null + submitted_at?: string + terminal_requested?: boolean + terms_accepted?: boolean + timestamp?: string + user_agent?: string +} + // Upload file using standard upload export async function uploadFile(file: any) { try { @@ -47,18 +76,40 @@ export async function uploadFile(file: any) { } } -export async function insertUser() { +export async function insertUser(userData: any) { try { const { data, error } = await supabase.from("signups").insert([ { - name: "test", - phone: "+821024734353", - email: "test@example.com", - account_type: "personal", + ...userData, terms_accepted: true, }, ]) console.log(data, error) + if (error) { + console.error("Insert error:", error) + return false + } else { + console.log("Insert success:", data) + return true + } + } catch (err) { + console.log("????????>>>>>>>", err) + } +} + +export async function updateUser() { + try { + const { data, error } = await supabase + .from("signups") + .update({ email: "new@email.com" }) + .eq("phone", "+1234567890") + + if (error) { + console.error("Update error:", error) + } else { + console.log("Updated user:", data) + } + console.log(data, error) if (error) { console.error("Insert error:", error) } else { @@ -68,3 +119,18 @@ export async function insertUser() { console.log("????????>>>>>>>", err) } } + +export async function fetchUser(phone: string) { + const { data, error } = await supabase + .from("signups") // your table name + .select("*") // or specify fields: 'id, name, email' + .eq("phone", phone) // phone number to match + .single() // if expecting only one match + + if (error) { + console.error("Fetch error:", error) + } else { + console.log("User data:", data) + return data + } +} From 8ae04e1c9383c51e4fe333925885eb9954ee6e57 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 6 May 2025 12:46:38 +0500 Subject: [PATCH 33/81] Add a logic to fetch user data from supabase database and pre-populate personal informations and show PRO account type only for level zero and one users --- .../account-upgrade-flow/AccountType.tsx | 62 ++++++++++++++----- app/supabase/index.ts | 2 +- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/app/screens/account-upgrade-flow/AccountType.tsx b/app/screens/account-upgrade-flow/AccountType.tsx index ca2bdf175..e39f4a0bb 100644 --- a/app/screens/account-upgrade-flow/AccountType.tsx +++ b/app/screens/account-upgrade-flow/AccountType.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useEffect } from "react" import { TouchableOpacity, View } from "react-native" import { StackScreenProps } from "@react-navigation/stack" import { Icon, makeStyles, Text, useTheme } from "@rneui/themed" @@ -12,8 +12,15 @@ import { Screen } from "@app/components/screen" import { useLevel } from "@app/graphql/level-context" // store -import { useAppDispatch } from "@app/store/redux" -import { setAccountUpgrade } from "@app/store/redux/slices/accountUpgradeSlice" +import { useAppDispatch, useAppSelector } from "@app/store/redux" +import { + setAccountUpgrade, + setPersonalInfo, +} from "@app/store/redux/slices/accountUpgradeSlice" + +// utils +import { fetchUser } from "@app/supabase" +import { parsePhoneNumber } from "libphonenumber-js" type Props = StackScreenProps @@ -23,6 +30,29 @@ const AccountType: React.FC = ({ navigation }) => { const { colors } = useTheme().theme const { currentLevel } = useLevel() + const { userData } = useAppSelector((state) => state.user) + + useEffect(() => { + fetchUserFromSupabase() + }, []) + + const fetchUserFromSupabase = async () => { + if (userData.phone) { + const res = await fetchUser(userData.phone) + const parsedPhone = parsePhoneNumber(userData.phone) + + dispatch(setAccountUpgrade({ id: res.id })) + dispatch( + setPersonalInfo({ + fullName: res.name, + countryCode: parsedPhone.country, + phoneNumber: parsedPhone.nationalNumber, + email: res.email, + }), + ) + } + } + const onPress = (accountType: string) => { dispatch(setAccountUpgrade({ accountType })) navigation.navigate("PersonalInformation") @@ -44,18 +74,20 @@ const AccountType: React.FC = ({ navigation }) => { )} - onPress("pro")}> - - - - Pro - - - Accept payments as a Pro Flashpoint. Business Name & Address required - - - - + {(currentLevel === AccountLevel.Zero || currentLevel === AccountLevel.One) && ( + onPress("pro")}> + + + + Pro + + + Accept payments as a Pro Flashpoint. Business Name & Address required + + + + + )} onPress("merchant")}> diff --git a/app/supabase/index.ts b/app/supabase/index.ts index c8ef07a91..f0fd58179 100644 --- a/app/supabase/index.ts +++ b/app/supabase/index.ts @@ -126,7 +126,7 @@ export async function fetchUser(phone: string) { .select("*") // or specify fields: 'id, name, email' .eq("phone", phone) // phone number to match .single() // if expecting only one match - + console.log(data, error) if (error) { console.error("Fetch error:", error) } else { From da5fe3cb56b9acab514c10ab3975e9f6e9ba83d9 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 6 May 2025 12:48:22 +0500 Subject: [PATCH 34/81] disable phone input field in PersonalInformation screen if user is already level one --- app/components/account-upgrade-flow/PhoneNumber.tsx | 11 +++++++++-- .../account-upgrade-flow/PersonalInformation.tsx | 8 +++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/components/account-upgrade-flow/PhoneNumber.tsx b/app/components/account-upgrade-flow/PhoneNumber.tsx index 96c32ae2b..dd411d30c 100644 --- a/app/components/account-upgrade-flow/PhoneNumber.tsx +++ b/app/components/account-upgrade-flow/PhoneNumber.tsx @@ -22,6 +22,7 @@ type Props = { countryCode?: string phoneNumber?: string errorMsg?: string + disabled?: boolean setPhoneNumber: (number: string) => void setCountryCode: (countryCode: PhoneNumberCountryCode) => void } @@ -30,6 +31,7 @@ const PhoneNumber: React.FC = ({ countryCode, phoneNumber, errorMsg, + disabled, setPhoneNumber, setCountryCode, }) => { @@ -43,9 +45,13 @@ const PhoneNumber: React.FC = ({ const renderCountryCode = ({ countryCode, onOpen }: FlagButtonProps) => { return ( countryCode && ( - + - + +{getCountryCallingCode(countryCode as PhoneNumberCountryCode)} @@ -76,6 +82,7 @@ const PhoneNumber: React.FC = ({ textContentType="telephoneNumber" keyboardType="phone-pad" value={phoneNumber} + editable={!disabled} onChangeText={setPhoneNumber} onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)} diff --git a/app/screens/account-upgrade-flow/PersonalInformation.tsx b/app/screens/account-upgrade-flow/PersonalInformation.tsx index 655f00cab..8e03ee85e 100644 --- a/app/screens/account-upgrade-flow/PersonalInformation.tsx +++ b/app/screens/account-upgrade-flow/PersonalInformation.tsx @@ -35,9 +35,10 @@ const PersonalInformation: React.FC = ({ navigation }) => { const [fullNameErr, setFullNameErr] = useState() const [phoneNumberErr, setPhoneNumberErr] = useState() - const { fullName, countryCode, phoneNumber, email } = useAppSelector( - (state) => state.accountUpgrade.personalInfo, - ) + const { + id, + personalInfo: { fullName, countryCode, phoneNumber, email }, + } = useAppSelector((state) => state.accountUpgrade) const { submitPhoneNumber, @@ -118,6 +119,7 @@ const PersonalInformation: React.FC = ({ navigation }) => { countryCode={countryCode} phoneNumber={phoneNumber} errorMsg={phoneNumberErr} + disabled={!!id} setCountryCode={(val) => { setPhoneNumberErr(undefined) setCountryCode(val) From d3b544a5ccb8e5f602063e2351fb75cb62d81382 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 6 May 2025 12:50:33 +0500 Subject: [PATCH 35/81] add a id in accountUpgradeSlice --- app/store/redux/slices/accountUpgradeSlice.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/store/redux/slices/accountUpgradeSlice.ts b/app/store/redux/slices/accountUpgradeSlice.ts index 1f0e07f5c..48fce57fe 100644 --- a/app/store/redux/slices/accountUpgradeSlice.ts +++ b/app/store/redux/slices/accountUpgradeSlice.ts @@ -3,6 +3,7 @@ import { CountryCode } from "libphonenumber-js" import { Asset } from "react-native-image-picker" interface AccountUpgradeSlice { + id?: string accountType?: "personal" | "pro" | "merchant" personalInfo: { fullName: string @@ -27,6 +28,7 @@ interface AccountUpgradeSlice { } const initialState: AccountUpgradeSlice = { + id: undefined, accountType: undefined, personalInfo: { fullName: "", From b5eddf63521450b5fdc650c3765f726fa793a764 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Wed, 7 May 2025 13:57:50 +0500 Subject: [PATCH 36/81] save the business address latitude and longitude as well when select the address --- app/components/account-upgrade-flow/AddressField.tsx | 11 ++++++++--- app/store/redux/slices/accountUpgradeSlice.ts | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/components/account-upgrade-flow/AddressField.tsx b/app/components/account-upgrade-flow/AddressField.tsx index 0d4cad90d..398077dae 100644 --- a/app/components/account-upgrade-flow/AddressField.tsx +++ b/app/components/account-upgrade-flow/AddressField.tsx @@ -17,7 +17,7 @@ type Props = { placeholder: string value: string errorMsg?: string - onAddressSelect: (val: string) => void + onAddressSelect: (address: string, lat?: number, lng?: number) => void } const AddressField: React.FC = ({ @@ -68,9 +68,14 @@ const AddressField: React.FC = ({ placeholder={placeholder} onFail={(err) => console.log("Google places auto complete", err)} onNotFound={() => console.log("Google places auto complete not found")} - onPress={(data) => { + fetchDetails={true} + onPress={(data, details) => { setIsVisible(false) - onAddressSelect(data.description) + onAddressSelect( + data.description, + details?.geometry.location.lat, + details?.geometry.location.lng, + ) }} query={{ key: GOOGLE_PLACE_API_KEY, diff --git a/app/store/redux/slices/accountUpgradeSlice.ts b/app/store/redux/slices/accountUpgradeSlice.ts index 48fce57fe..3b3e0fc92 100644 --- a/app/store/redux/slices/accountUpgradeSlice.ts +++ b/app/store/redux/slices/accountUpgradeSlice.ts @@ -14,6 +14,8 @@ interface AccountUpgradeSlice { businessInfo: { businessName: string businessAddress: string + lat?: number + lng?: number } bankInfo: { bankName: string @@ -39,6 +41,8 @@ const initialState: AccountUpgradeSlice = { businessInfo: { businessName: "", businessAddress: "", + lat: undefined, + lng: undefined, }, bankInfo: { bankName: "", From 78223581e348a758bcd03b626080a1a8892eddf4 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Wed, 7 May 2025 14:00:16 +0500 Subject: [PATCH 37/81] supabase integration in BusinessInformation screen: insert or update user data --- .../BusinessInformation.tsx | 46 +++++++++++++++++-- .../PersonalInformation.tsx | 3 -- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/app/screens/account-upgrade-flow/BusinessInformation.tsx b/app/screens/account-upgrade-flow/BusinessInformation.tsx index bbbf60a52..1b33258f1 100644 --- a/app/screens/account-upgrade-flow/BusinessInformation.tsx +++ b/app/screens/account-upgrade-flow/BusinessInformation.tsx @@ -3,30 +3,40 @@ import { View } from "react-native" import { makeStyles } from "@rneui/themed" import { StackScreenProps } from "@react-navigation/stack" import { RootStackParamList } from "@app/navigation/stack-param-lists" +import { parsePhoneNumber } from "libphonenumber-js" // components import { Screen } from "@app/components/screen" import { PrimaryBtn } from "@app/components/buttons" import { AddressField, InputField } from "@app/components/account-upgrade-flow" +// hooks +import { useActivityIndicator } from "@app/hooks" + // store import { useAppDispatch, useAppSelector } from "@app/store/redux" import { setBusinessInfo } from "@app/store/redux/slices/accountUpgradeSlice" +// utils +import { insertUser, updateUser } from "@app/supabase" + type Props = StackScreenProps const BusinessInformation: React.FC = ({ navigation }) => { const dispatch = useAppDispatch() const styles = useStyles() + const { toggleActivityIndicator } = useActivityIndicator() const [businessNameErr, setBusinessNameErr] = useState() const [businessAddressErr, setBusinessAddressErr] = useState() const { + id, accountType, - businessInfo: { businessName, businessAddress }, + personalInfo, + businessInfo: { businessName, businessAddress, lat, lng }, } = useAppSelector((state) => state.accountUpgrade) - const onPressNext = () => { + const onPressNext = async () => { let hasError = false if (businessName.length < 2) { @@ -39,7 +49,33 @@ const BusinessInformation: React.FC = ({ navigation }) => { } if (!hasError) { if (accountType === "pro") { - navigation.navigate("AccountUpgradeSuccess") + toggleActivityIndicator(true) + const data = { + account_type: accountType, + business_name: businessName, + business_address: businessAddress, + latitude: lat, + longitude: lng, + } + let res = null + if (!id) { + const parsedPhoneNumber = parsePhoneNumber( + personalInfo.phoneNumber, + personalInfo.countryCode, + ) + const updatedData = { + ...data, + name: personalInfo.fullName, + phone: parsedPhoneNumber.number, + email: personalInfo.email, + } + res = await insertUser(updatedData) + } else { + res = await updateUser(id, data) + } + toggleActivityIndicator(false) + if (res) navigation.navigate("AccountUpgradeSuccess") + else alert("Something went wrong. Please, try again later.") } else { navigation.navigate("BankInformation") } @@ -64,7 +100,9 @@ const BusinessInformation: React.FC = ({ navigation }) => { placeholder={"Enter your business address"} value={businessAddress} errorMsg={businessAddressErr} - onAddressSelect={(val) => dispatch(setBusinessInfo({ businessAddress: val }))} + onAddressSelect={(val, lat, lng) => + dispatch(setBusinessInfo({ businessAddress: val, lat, lng })) + } /> = ({ navigation }) => { setCountryCode, } = useRequestPhoneCodeLogin() - console.log("PERSONAL INFO STATUS >>>>>>>>>>>>", status) - console.log("PERSONAL INFO ERROR >>>>>>>>>>>>", error) - useEffect(() => { if ( status === RequestPhoneCodeStatus.CompletingCaptcha || From b8b2ad29101145fe6fffc29cddbbb04714270a02 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Wed, 7 May 2025 14:01:45 +0500 Subject: [PATCH 38/81] show loading spinner when saving user data to supabase in Validation screen --- app/screens/account-upgrade-flow/Validation.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/screens/account-upgrade-flow/Validation.tsx b/app/screens/account-upgrade-flow/Validation.tsx index e62ab319c..be5d27c53 100644 --- a/app/screens/account-upgrade-flow/Validation.tsx +++ b/app/screens/account-upgrade-flow/Validation.tsx @@ -51,13 +51,14 @@ const Validation: React.FC = ({ navigation, route }) => { saveToken(authToken) } if (accountType === "personal") { - // insert user to supabase database + toggleActivityIndicator(true) const res = await insertUser({ account_type: accountType, name: personalInfo.fullName, phone: phone, email: personalInfo.email, }) + toggleActivityIndicator(false) if (res) navigation.replace("AccountUpgradeSuccess") else alert("Something went wrong. Please, try again later.") } else { From abcc3a2ba40f68cd601fcbfe690c9a90b3e8171b Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Wed, 7 May 2025 14:03:57 +0500 Subject: [PATCH 39/81] update insertUser and updateUser functions in supabase/index.ts --- app/supabase/index.ts | 68 +++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 44 deletions(-) diff --git a/app/supabase/index.ts b/app/supabase/index.ts index f0fd58179..0aebc7464 100644 --- a/app/supabase/index.ts +++ b/app/supabase/index.ts @@ -7,32 +7,25 @@ import { SUPABASE_URL, SUPABASE_KEY } from "@env" const supabase = createClient(SUPABASE_URL, SUPABASE_KEY) type User = { - account_currency?: string account_type?: string - attempt?: string - bank_account_number?: string - bank_account_type?: string - bank_branch?: string - bank_name?: string - business_address?: string + name?: string + phone?: string + email?: string business_name?: string + business_address?: string + latitude?: number + longitude?: number + bank_name?: string + bank_branch?: string + bank_account_type?: string + account_currency?: string + bank_account_number?: string + id_image_url?: string + terms_accepted?: boolean + terminal_requested?: boolean client_version?: string - created_at?: string device_info?: string - email?: string - id?: string - id_image_url?: string - latitude?: string - longitude?: string - name: string - phone: string signup_completed?: boolean - submission_source?: null - submitted_at?: string - terminal_requested?: boolean - terms_accepted?: boolean - timestamp?: string - user_agent?: string } // Upload file using standard upload @@ -76,15 +69,11 @@ export async function uploadFile(file: any) { } } -export async function insertUser(userData: any) { +export async function insertUser(userData: User) { try { - const { data, error } = await supabase.from("signups").insert([ - { - ...userData, - terms_accepted: true, - }, - ]) - console.log(data, error) + const { data, error } = await supabase + .from("signups") + .insert([{ ...userData, terms_accepted: true }]) if (error) { console.error("Insert error:", error) return false @@ -93,30 +82,22 @@ export async function insertUser(userData: any) { return true } } catch (err) { - console.log("????????>>>>>>>", err) + console.log("SUPABASE INSERT USER CATCH ERR: ", err) } } -export async function updateUser() { +export async function updateUser(id: string, userData: User) { try { - const { data, error } = await supabase - .from("signups") - .update({ email: "new@email.com" }) - .eq("phone", "+1234567890") - + const { data, error } = await supabase.from("signups").update(userData).eq("id", id) if (error) { console.error("Update error:", error) + return false } else { console.log("Updated user:", data) - } - console.log(data, error) - if (error) { - console.error("Insert error:", error) - } else { - console.log("Insert success:", data) + return true } } catch (err) { - console.log("????????>>>>>>>", err) + console.log("SUPABASE UPDATE USER CATCH ERR: ", err) } } @@ -126,7 +107,6 @@ export async function fetchUser(phone: string) { .select("*") // or specify fields: 'id, name, email' .eq("phone", phone) // phone number to match .single() // if expecting only one match - console.log(data, error) if (error) { console.error("Fetch error:", error) } else { From e42040ecfde187cfd7c61caa182c1540ed22fd95 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Wed, 7 May 2025 18:51:08 +0500 Subject: [PATCH 40/81] implemented useAccountUpgrade hook to submit the account upgrade data to supabase and fetch the account upgrade data from supabase and set to redux global state --- app/hooks/index.ts | 1 + app/hooks/useAccountUpgrade.tsx | 94 +++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 app/hooks/useAccountUpgrade.tsx diff --git a/app/hooks/index.ts b/app/hooks/index.ts index a9ffe87ce..c457b91ff 100644 --- a/app/hooks/index.ts +++ b/app/hooks/index.ts @@ -8,3 +8,4 @@ export * from "./useIbexFee" export * from "./useFlashcard" export * from "./useSwap" export * from "./use-unauthed-price-conversion" +export * from "./useAccountUpgrade" diff --git a/app/hooks/useAccountUpgrade.tsx b/app/hooks/useAccountUpgrade.tsx new file mode 100644 index 000000000..d788bf0a3 --- /dev/null +++ b/app/hooks/useAccountUpgrade.tsx @@ -0,0 +1,94 @@ +import DeviceInfo from "react-native-device-info" +import { parsePhoneNumber } from "libphonenumber-js" + +// hooks +import { useActivityIndicator } from "./useActivityIndicator" + +// store +import { useAppDispatch, useAppSelector } from "@app/store/redux" +import { + setAccountUpgrade, + setBusinessInfo, + setPersonalInfo, +} from "@app/store/redux/slices/accountUpgradeSlice" + +// supabase +import { fetchUser, insertUser, updateUser } from "@app/supabase" + +export const useAccountUpgrade = () => { + const dispatch = useAppDispatch() + const { userData } = useAppSelector((state) => state.user) + const { id, accountType, personalInfo, businessInfo, bankInfo } = useAppSelector( + (state) => state.accountUpgrade, + ) + + const { toggleActivityIndicator } = useActivityIndicator() + + const fetchAccountUpgrade = async () => { + if (userData.phone) { + toggleActivityIndicator(true) + const res = await fetchUser(userData.phone) + const parsedPhone = parsePhoneNumber(userData.phone) + dispatch(setAccountUpgrade({ id: res.id })) + dispatch( + setPersonalInfo({ + fullName: res.name, + countryCode: parsedPhone.country, + phoneNumber: parsedPhone.nationalNumber, + email: res.email, + }), + ) + dispatch( + setBusinessInfo({ + businessName: res.business_name, + businessAddress: res.business_address, + lat: res.latitude, + lng: res.longitude, + }), + ) + toggleActivityIndicator(false) + } + } + + const submitAccountUpgrade = async () => { + toggleActivityIndicator(true) + const readableVersion = DeviceInfo.getReadableVersion() + const parsedPhoneNumber = parsePhoneNumber( + personalInfo.phoneNumber || "", + personalInfo.countryCode, + ) + + const data = { + account_type: accountType, + name: personalInfo.fullName, + phone: parsedPhoneNumber.number, + email: personalInfo.email, + business_name: businessInfo.businessName, + business_address: businessInfo.businessAddress, + latitude: businessInfo.lat, + longitude: businessInfo.lng, + bank_name: bankInfo.bankName, + bank_branch: bankInfo.bankBranch, + bank_account_type: bankInfo.bankAccountType, + account_currency: bankInfo.currency, + bank_account_number: bankInfo.accountNumber, + id_image_url: "", + terms_accepted: true, + terminal_requested: undefined, + client_version: readableVersion, + device_info: undefined, + signup_completed: undefined, + } + + let res = null + if (!id) { + res = await insertUser(data) + } else { + res = await updateUser(id, data) + } + toggleActivityIndicator(false) + return res + } + + return { fetchAccountUpgrade, submitAccountUpgrade } +} From 6efd8027a085eb80434954a7c5730a7253a91b25 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Wed, 7 May 2025 18:59:15 +0500 Subject: [PATCH 41/81] use useAccountUpgrade hook in AccountType, Validation and BusinessInformation screens to fetch/inser/update user data from/to supabase and, update accountUpgradeSlice data types --- .../account-upgrade-flow/AddressField.tsx | 2 +- .../account-upgrade-flow/AccountType.tsx | 36 +++----------- .../BusinessInformation.tsx | 40 +++------------- .../PersonalInformation.tsx | 4 +- .../account-upgrade-flow/Validation.tsx | 13 ++--- app/store/redux/slices/accountUpgradeSlice.ts | 48 +++++++++---------- 6 files changed, 42 insertions(+), 101 deletions(-) diff --git a/app/components/account-upgrade-flow/AddressField.tsx b/app/components/account-upgrade-flow/AddressField.tsx index 398077dae..98e86ec35 100644 --- a/app/components/account-upgrade-flow/AddressField.tsx +++ b/app/components/account-upgrade-flow/AddressField.tsx @@ -15,7 +15,7 @@ import { GOOGLE_PLACE_API_KEY } from "@env" type Props = { label: string placeholder: string - value: string + value?: string errorMsg?: string onAddressSelect: (address: string, lat?: number, lng?: number) => void } diff --git a/app/screens/account-upgrade-flow/AccountType.tsx b/app/screens/account-upgrade-flow/AccountType.tsx index e39f4a0bb..670acb695 100644 --- a/app/screens/account-upgrade-flow/AccountType.tsx +++ b/app/screens/account-upgrade-flow/AccountType.tsx @@ -10,17 +10,11 @@ import { Screen } from "@app/components/screen" // hooks import { useLevel } from "@app/graphql/level-context" +import { useAccountUpgrade } from "@app/hooks" // store -import { useAppDispatch, useAppSelector } from "@app/store/redux" -import { - setAccountUpgrade, - setPersonalInfo, -} from "@app/store/redux/slices/accountUpgradeSlice" - -// utils -import { fetchUser } from "@app/supabase" -import { parsePhoneNumber } from "libphonenumber-js" +import { useAppDispatch } from "@app/store/redux" +import { setAccountUpgrade } from "@app/store/redux/slices/accountUpgradeSlice" type Props = StackScreenProps @@ -29,30 +23,12 @@ const AccountType: React.FC = ({ navigation }) => { const styles = useStyles() const { colors } = useTheme().theme const { currentLevel } = useLevel() - - const { userData } = useAppSelector((state) => state.user) + const { fetchAccountUpgrade } = useAccountUpgrade() useEffect(() => { - fetchUserFromSupabase() + fetchAccountUpgrade() }, []) - const fetchUserFromSupabase = async () => { - if (userData.phone) { - const res = await fetchUser(userData.phone) - const parsedPhone = parsePhoneNumber(userData.phone) - - dispatch(setAccountUpgrade({ id: res.id })) - dispatch( - setPersonalInfo({ - fullName: res.name, - countryCode: parsedPhone.country, - phoneNumber: parsedPhone.nationalNumber, - email: res.email, - }), - ) - } - } - const onPress = (accountType: string) => { dispatch(setAccountUpgrade({ accountType })) navigation.navigate("PersonalInformation") @@ -75,7 +51,7 @@ const AccountType: React.FC = ({ navigation }) => { )} {(currentLevel === AccountLevel.Zero || currentLevel === AccountLevel.One) && ( - onPress("pro")}> + onPress("business")}> diff --git a/app/screens/account-upgrade-flow/BusinessInformation.tsx b/app/screens/account-upgrade-flow/BusinessInformation.tsx index 1b33258f1..223d66db4 100644 --- a/app/screens/account-upgrade-flow/BusinessInformation.tsx +++ b/app/screens/account-upgrade-flow/BusinessInformation.tsx @@ -3,7 +3,6 @@ import { View } from "react-native" import { makeStyles } from "@rneui/themed" import { StackScreenProps } from "@react-navigation/stack" import { RootStackParamList } from "@app/navigation/stack-param-lists" -import { parsePhoneNumber } from "libphonenumber-js" // components import { Screen } from "@app/components/screen" @@ -11,21 +10,18 @@ import { PrimaryBtn } from "@app/components/buttons" import { AddressField, InputField } from "@app/components/account-upgrade-flow" // hooks -import { useActivityIndicator } from "@app/hooks" +import { useAccountUpgrade } from "@app/hooks" // store import { useAppDispatch, useAppSelector } from "@app/store/redux" import { setBusinessInfo } from "@app/store/redux/slices/accountUpgradeSlice" -// utils -import { insertUser, updateUser } from "@app/supabase" - type Props = StackScreenProps const BusinessInformation: React.FC = ({ navigation }) => { const dispatch = useAppDispatch() const styles = useStyles() - const { toggleActivityIndicator } = useActivityIndicator() + const { submitAccountUpgrade } = useAccountUpgrade() const [businessNameErr, setBusinessNameErr] = useState() const [businessAddressErr, setBusinessAddressErr] = useState() @@ -39,41 +35,17 @@ const BusinessInformation: React.FC = ({ navigation }) => { const onPressNext = async () => { let hasError = false - if (businessName.length < 2) { + if (businessName && businessName.length < 2) { setBusinessNameErr("Business name must be at least 2 characters") hasError = true } - if (businessAddress.split(",").length <= 1) { + if (businessAddress && businessAddress.length < 2) { setBusinessAddressErr("Please enter a valid address") hasError = true } if (!hasError) { - if (accountType === "pro") { - toggleActivityIndicator(true) - const data = { - account_type: accountType, - business_name: businessName, - business_address: businessAddress, - latitude: lat, - longitude: lng, - } - let res = null - if (!id) { - const parsedPhoneNumber = parsePhoneNumber( - personalInfo.phoneNumber, - personalInfo.countryCode, - ) - const updatedData = { - ...data, - name: personalInfo.fullName, - phone: parsedPhoneNumber.number, - email: personalInfo.email, - } - res = await insertUser(updatedData) - } else { - res = await updateUser(id, data) - } - toggleActivityIndicator(false) + if (accountType === "business") { + const res = await submitAccountUpgrade() if (res) navigation.navigate("AccountUpgradeSuccess") else alert("Something went wrong. Please, try again later.") } else { diff --git a/app/screens/account-upgrade-flow/PersonalInformation.tsx b/app/screens/account-upgrade-flow/PersonalInformation.tsx index 5e71e82a9..6afc0cb96 100644 --- a/app/screens/account-upgrade-flow/PersonalInformation.tsx +++ b/app/screens/account-upgrade-flow/PersonalInformation.tsx @@ -76,8 +76,8 @@ const PersonalInformation: React.FC = ({ navigation }) => { const onPressNext = async (channel?: PhoneCodeChannelType) => { try { let hasError = false - const parsedPhoneNumber = parsePhoneNumber(phoneNumber, countryCode) - if (fullName?.length < 2) { + const parsedPhoneNumber = parsePhoneNumber(phoneNumber || "", countryCode) + if (fullName && fullName?.length < 2) { setFullNameErr("Name must be at least 2 characters") hasError = true } diff --git a/app/screens/account-upgrade-flow/Validation.tsx b/app/screens/account-upgrade-flow/Validation.tsx index be5d27c53..cd29f8888 100644 --- a/app/screens/account-upgrade-flow/Validation.tsx +++ b/app/screens/account-upgrade-flow/Validation.tsx @@ -9,14 +9,13 @@ import { Screen } from "@app/components/screen" import { InputField } from "@app/components/account-upgrade-flow" // hooks -import { useActivityIndicator, useAppConfig } from "@app/hooks" +import { useAccountUpgrade, useActivityIndicator, useAppConfig } from "@app/hooks" import { useUserLoginUpgradeMutation } from "@app/graphql/generated" import { useI18nContext } from "@app/i18n/i18n-react" import { useAppSelector } from "@app/store/redux" // utils import { PhoneCodeChannelToFriendlyName } from "../phone-auth-screen/request-phone-code-login" -import { insertUser } from "@app/supabase" type Props = StackScreenProps @@ -27,6 +26,7 @@ const Validation: React.FC = ({ navigation, route }) => { const { LL } = useI18nContext() const { saveToken } = useAppConfig() const { toggleActivityIndicator } = useActivityIndicator() + const { submitAccountUpgrade } = useAccountUpgrade() const [code, setCode] = useState() const [errorMsg, setErrorMsg] = useState() @@ -51,14 +51,7 @@ const Validation: React.FC = ({ navigation, route }) => { saveToken(authToken) } if (accountType === "personal") { - toggleActivityIndicator(true) - const res = await insertUser({ - account_type: accountType, - name: personalInfo.fullName, - phone: phone, - email: personalInfo.email, - }) - toggleActivityIndicator(false) + const res = await submitAccountUpgrade() if (res) navigation.replace("AccountUpgradeSuccess") else alert("Something went wrong. Please, try again later.") } else { diff --git a/app/store/redux/slices/accountUpgradeSlice.ts b/app/store/redux/slices/accountUpgradeSlice.ts index 3b3e0fc92..ae582d257 100644 --- a/app/store/redux/slices/accountUpgradeSlice.ts +++ b/app/store/redux/slices/accountUpgradeSlice.ts @@ -4,56 +4,56 @@ import { Asset } from "react-native-image-picker" interface AccountUpgradeSlice { id?: string - accountType?: "personal" | "pro" | "merchant" + accountType?: "personal" | "business" | "merchant" personalInfo: { - fullName: string - countryCode: CountryCode - phoneNumber: string - email: string + fullName?: string + countryCode?: CountryCode + phoneNumber?: string + email?: string } businessInfo: { - businessName: string - businessAddress: string + businessName?: string + businessAddress?: string lat?: number lng?: number } bankInfo: { - bankName: string - bankBranch: string - accountType: string - currency: string - accountNumber: string + bankName?: string + bankBranch?: string + bankAccountType?: string + currency?: string + accountNumber?: string idDocument?: Asset } loading: boolean - error: string + error?: string } const initialState: AccountUpgradeSlice = { id: undefined, accountType: undefined, personalInfo: { - fullName: "", + fullName: undefined, countryCode: "JM", - phoneNumber: "", - email: "", + phoneNumber: undefined, + email: undefined, }, businessInfo: { - businessName: "", - businessAddress: "", + businessName: undefined, + businessAddress: undefined, lat: undefined, lng: undefined, }, bankInfo: { - bankName: "", - bankBranch: "", - accountType: "", - currency: "", - accountNumber: "", + bankName: undefined, + bankBranch: undefined, + bankAccountType: undefined, + currency: undefined, + accountNumber: undefined, idDocument: undefined, }, loading: false, - error: "", + error: undefined, } export const accountUpgradeSlice = createSlice({ From 64b7eac7fc058e9d8f8b8101616c1194817723b0 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Thu, 8 May 2025 10:37:28 +0500 Subject: [PATCH 42/81] fix app crash when PersonalInformation screen unmounts --- app/components/account-upgrade-flow/PhoneNumber.tsx | 4 ++-- app/screens/account-upgrade-flow/PersonalInformation.tsx | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/components/account-upgrade-flow/PhoneNumber.tsx b/app/components/account-upgrade-flow/PhoneNumber.tsx index dd411d30c..b9cd9d593 100644 --- a/app/components/account-upgrade-flow/PhoneNumber.tsx +++ b/app/components/account-upgrade-flow/PhoneNumber.tsx @@ -16,13 +16,13 @@ import CountryPicker, { // hooks import { useI18nContext } from "@app/i18n/i18n-react" -import { useRequestPhoneCodeLogin } from "@app/screens/phone-auth-screen/request-phone-code-login" type Props = { countryCode?: string phoneNumber?: string errorMsg?: string disabled?: boolean + supportedCountries: CountryCode[] setPhoneNumber: (number: string) => void setCountryCode: (countryCode: PhoneNumberCountryCode) => void } @@ -32,13 +32,13 @@ const PhoneNumber: React.FC = ({ phoneNumber, errorMsg, disabled, + supportedCountries, setPhoneNumber, setCountryCode, }) => { const styles = useStyles() const { mode, colors } = useTheme().theme const { LL } = useI18nContext() - const { supportedCountries } = useRequestPhoneCodeLogin() const [isFocused, setIsFocused] = useState(false) diff --git a/app/screens/account-upgrade-flow/PersonalInformation.tsx b/app/screens/account-upgrade-flow/PersonalInformation.tsx index 6afc0cb96..1da94268c 100644 --- a/app/screens/account-upgrade-flow/PersonalInformation.tsx +++ b/app/screens/account-upgrade-flow/PersonalInformation.tsx @@ -4,6 +4,7 @@ import { makeStyles } from "@rneui/themed" import { StackScreenProps } from "@react-navigation/stack" import { RootStackParamList } from "@app/navigation/stack-param-lists" import { AccountLevel, PhoneCodeChannelType } from "@app/graphql/generated" +import { CountryCode } from "react-native-country-picker-modal" import { parsePhoneNumber } from "libphonenumber-js/mobile" // components @@ -50,6 +51,7 @@ const PersonalInformation: React.FC = ({ navigation }) => { phoneCodeChannel, error, validatedPhoneNumber, + supportedCountries, setCountryCode, } = useRequestPhoneCodeLogin() @@ -117,6 +119,7 @@ const PersonalInformation: React.FC = ({ navigation }) => { phoneNumber={phoneNumber} errorMsg={phoneNumberErr} disabled={!!id} + supportedCountries={supportedCountries as CountryCode[]} setCountryCode={(val) => { setPhoneNumberErr(undefined) setCountryCode(val) From 95a131171327577187be76f64dda8822a64cbcaf Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Thu, 8 May 2025 10:43:19 +0500 Subject: [PATCH 43/81] implement supabase integration in BankInformation screen --- app/hooks/useAccountUpgrade.tsx | 23 +++++++++--- .../account-upgrade-flow/BankInformation.tsx | 35 +++++++++++++------ 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/app/hooks/useAccountUpgrade.tsx b/app/hooks/useAccountUpgrade.tsx index d788bf0a3..56d351dfe 100644 --- a/app/hooks/useAccountUpgrade.tsx +++ b/app/hooks/useAccountUpgrade.tsx @@ -1,3 +1,4 @@ +import { Platform } from "react-native" import DeviceInfo from "react-native-device-info" import { parsePhoneNumber } from "libphonenumber-js" @@ -8,12 +9,13 @@ import { useActivityIndicator } from "./useActivityIndicator" import { useAppDispatch, useAppSelector } from "@app/store/redux" import { setAccountUpgrade, + setBankInfo, setBusinessInfo, setPersonalInfo, } from "@app/store/redux/slices/accountUpgradeSlice" // supabase -import { fetchUser, insertUser, updateUser } from "@app/supabase" +import { fetchUser, insertUser, updateUser, uploadFile } from "@app/supabase" export const useAccountUpgrade = () => { const dispatch = useAppDispatch() @@ -21,7 +23,6 @@ export const useAccountUpgrade = () => { const { id, accountType, personalInfo, businessInfo, bankInfo } = useAppSelector( (state) => state.accountUpgrade, ) - const { toggleActivityIndicator } = useActivityIndicator() const fetchAccountUpgrade = async () => { @@ -46,6 +47,15 @@ export const useAccountUpgrade = () => { lng: res.longitude, }), ) + dispatch( + setBankInfo({ + bankName: res.bank_name, + bankBranch: res.bank_branch, + bankAccountType: res.bank_account_type, + currency: res.account_currency, + accountNumber: res.bank_account_number, + }), + ) toggleActivityIndicator(false) } } @@ -58,6 +68,11 @@ export const useAccountUpgrade = () => { personalInfo.countryCode, ) + let id_image_url = undefined + if (accountType === "merchant") { + id_image_url = await uploadFile(bankInfo.idDocument) + } + const data = { account_type: accountType, name: personalInfo.fullName, @@ -72,11 +87,11 @@ export const useAccountUpgrade = () => { bank_account_type: bankInfo.bankAccountType, account_currency: bankInfo.currency, bank_account_number: bankInfo.accountNumber, - id_image_url: "", + id_image_url: id_image_url, terms_accepted: true, terminal_requested: undefined, client_version: readableVersion, - device_info: undefined, + device_info: Platform.OS, signup_completed: undefined, } diff --git a/app/screens/account-upgrade-flow/BankInformation.tsx b/app/screens/account-upgrade-flow/BankInformation.tsx index e1e349a1b..fe3f75cf3 100644 --- a/app/screens/account-upgrade-flow/BankInformation.tsx +++ b/app/screens/account-upgrade-flow/BankInformation.tsx @@ -13,6 +13,9 @@ import { import { Screen } from "@app/components/screen" import { PrimaryBtn } from "@app/components/buttons" +// hooks +import { useAccountUpgrade } from "@app/hooks" + // store import { useAppDispatch, useAppSelector } from "@app/store/redux" import { setBankInfo } from "@app/store/redux/slices/accountUpgradeSlice" @@ -38,6 +41,7 @@ type Props = StackScreenProps const BankInformation: React.FC = ({ navigation }) => { const dispatch = useAppDispatch() const styles = useStyles() + const { submitAccountUpgrade } = useAccountUpgrade() const [nameErr, setNameErr] = useState() const [branchErr, setBranchErr] = useState() @@ -45,16 +49,25 @@ const BankInformation: React.FC = ({ navigation }) => { const [currencyErr, setCurrencyErr] = useState() const [accountNumErr, setAccountNumErr] = useState() const [idDocumentErr, setIdDocumentErr] = useState() - const { bankName, bankBranch, accountType, currency, accountNumber, idDocument } = - useAppSelector((state) => state.accountUpgrade.bankInfo) + const { + accountType, + bankInfo: { + bankName, + bankBranch, + bankAccountType, + currency, + accountNumber, + idDocument, + }, + } = useAppSelector((state) => state.accountUpgrade) - const onPressNext = () => { + const onPressNext = async () => { let hasError = false - if (bankName.length < 2) { + if (bankName && bankName.length < 2) { setNameErr("Bank name is required") hasError = true } - if (bankBranch.length < 2) { + if (bankBranch && bankBranch.length < 2) { setBranchErr("Branch is required") hasError = true } @@ -66,7 +79,7 @@ const BankInformation: React.FC = ({ navigation }) => { setCurrencyErr("Currency is required") hasError = true } - if (accountNumber.length < 4) { + if (accountNumber && accountNumber.length < 4) { setAccountNumErr("Account number is required") hasError = true } @@ -75,7 +88,9 @@ const BankInformation: React.FC = ({ navigation }) => { hasError = true } if (!hasError) { - navigation.navigate("AccountUpgradeSuccess") + const res = await submitAccountUpgrade() + if (res) navigation.navigate("AccountUpgradeSuccess") + else alert("Something went wrong. Please, try again later.") } } @@ -106,18 +121,18 @@ const BankInformation: React.FC = ({ navigation }) => { label="Account Type" placeholder={"Select account type"} data={accountTypes} - value={accountType} + value={bankAccountType || ""} errorMsg={accountTypeErr} onChange={(val) => { setAccountTypeErr(undefined) - dispatch(setBankInfo({ accountType: val })) + dispatch(setBankInfo({ bankAccountType: val })) }} /> { setCurrencyErr(undefined) From 0982ef1814d2e603dfef00d826a8c4ad73be5a24 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Thu, 8 May 2025 10:46:11 +0500 Subject: [PATCH 44/81] update supabase/index.ts --- app/supabase/index.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/app/supabase/index.ts b/app/supabase/index.ts index 0aebc7464..7a8b6f21c 100644 --- a/app/supabase/index.ts +++ b/app/supabase/index.ts @@ -51,17 +51,18 @@ export async function uploadFile(file: any) { const fileName = `${secureFileName}.${fileExt}` const filePath = `${fileName}` - console.log(">>>>>>>>>>", filePath) - const { data, error } = await supabase.storage - .from("id-documents") + .from("id_uploads") .upload(filePath, file) if (error) { // Handle error console.log("UPLOAD ERROR>>>>>>>", error) + return undefined } else { // Handle success console.log("UPLOAD SUCCESS>>>>>>>", data) + const res = supabase.storage.from("id_uploads").getPublicUrl(data.path) + return res.data.publicUrl } return } catch (err) { @@ -114,3 +115,18 @@ export async function fetchUser(phone: string) { return data } } + +export async function deleteUser(id: string) { + const { data, error } = await supabase + .from("signups") // your table name + .delete() + .eq("id", id) // filter to match the row + + if (error) { + console.error("Delete error:", error) + return false + } else { + console.log("Deleted row:", data) + return true + } +} From 658d941817dcc15a361c9c9a7c859ba7e675c247 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Thu, 8 May 2025 10:46:52 +0500 Subject: [PATCH 45/81] add a information text in success screen for merchant --- app/screens/account-upgrade-flow/Success.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/screens/account-upgrade-flow/Success.tsx b/app/screens/account-upgrade-flow/Success.tsx index 243533dc3..402a52369 100644 --- a/app/screens/account-upgrade-flow/Success.tsx +++ b/app/screens/account-upgrade-flow/Success.tsx @@ -36,9 +36,18 @@ const Success: React.FC = ({ navigation }) => { - {`You successfully upgraded your account to ${accountType?.toUpperCase()}`} + {`You successfully requested to upgrade your account to ${accountType?.toUpperCase()}`} + {accountType === "merchant" && ( + + * Please enter the test transaction amount to confirm your bank details. + + )} Date: Thu, 8 May 2025 12:03:11 +0500 Subject: [PATCH 46/81] TestTransaction screen is implemented and added in root navigation --- app/navigation/root-navigator.tsx | 6 ++ .../account-upgrade-flow/TestTransaction.tsx | 68 +++++++++++++++++++ app/screens/account-upgrade-flow/index.ts | 2 + 3 files changed, 76 insertions(+) create mode 100644 app/screens/account-upgrade-flow/TestTransaction.tsx diff --git a/app/navigation/root-navigator.tsx b/app/navigation/root-navigator.tsx index 066e8f6b5..038c344ac 100644 --- a/app/navigation/root-navigator.tsx +++ b/app/navigation/root-navigator.tsx @@ -125,6 +125,7 @@ import { AccountType, Validation, Success, + TestTransaction, } from "@app/screens/account-upgrade-flow" const useStyles = makeStyles(({ colors }) => ({ @@ -624,6 +625,11 @@ export const RootStack = () => { component={Success} options={{ headerShown: false }} /> + ) } diff --git a/app/screens/account-upgrade-flow/TestTransaction.tsx b/app/screens/account-upgrade-flow/TestTransaction.tsx new file mode 100644 index 000000000..a23e6cf1d --- /dev/null +++ b/app/screens/account-upgrade-flow/TestTransaction.tsx @@ -0,0 +1,68 @@ +import React, { useState } from "react" +import { RootStackParamList } from "@app/navigation/stack-param-lists" +import { StackScreenProps } from "@react-navigation/stack" +import { makeStyles, Text } from "@rneui/themed" +import { View } from "react-native" + +// components +import { Screen } from "@app/components/screen" +import { PrimaryBtn } from "@app/components/buttons" +import { InputField } from "@app/components/account-upgrade-flow" + +// hooks +import { useActivityIndicator } from "@app/hooks" +import { useI18nContext } from "@app/i18n/i18n-react" + +type Props = StackScreenProps + +const TestTransaction: React.FC = ({ navigation, route }) => { + const styles = useStyles() + const { LL } = useI18nContext() + const { toggleActivityIndicator } = useActivityIndicator() + + const [errorMsg, setErrorMsg] = useState() + const [amount, setAmount] = useState() + + const onConfirm = () => {} + + return ( + + + + To complete upgrading your account to MERCHANT, enter the test transaction + amount we sent your bank account to confirm your bank details. + + + + + + ) +} + +export default TestTransaction + +const useStyles = makeStyles(() => ({ + wrapper: { + flex: 1, + paddingVertical: 10, + paddingHorizontal: 20, + }, + header: { + marginBottom: 30, + }, + btn: { + marginHorizontal: 20, + marginVertical: 10, + }, +})) diff --git a/app/screens/account-upgrade-flow/index.ts b/app/screens/account-upgrade-flow/index.ts index 372d50471..b6159ce8e 100644 --- a/app/screens/account-upgrade-flow/index.ts +++ b/app/screens/account-upgrade-flow/index.ts @@ -4,6 +4,7 @@ import BusinessInformation from "./BusinessInformation" import BankInformation from "./BankInformation" import Validation from "./Validation" import Success from "./Success" +import TestTransaction from "./TestTransaction" export { AccountType, @@ -12,4 +13,5 @@ export { BankInformation, Validation, Success, + TestTransaction, } From 2435727d034495e6720e933847a9640acf9e394c Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Thu, 8 May 2025 12:24:44 +0500 Subject: [PATCH 47/81] add upgradeCompleted in accountUpgradeSlice --- app/hooks/useAccountUpgrade.tsx | 8 +++++++- app/store/redux/slices/accountUpgradeSlice.ts | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/hooks/useAccountUpgrade.tsx b/app/hooks/useAccountUpgrade.tsx index 56d351dfe..0033756f7 100644 --- a/app/hooks/useAccountUpgrade.tsx +++ b/app/hooks/useAccountUpgrade.tsx @@ -30,7 +30,13 @@ export const useAccountUpgrade = () => { toggleActivityIndicator(true) const res = await fetchUser(userData.phone) const parsedPhone = parsePhoneNumber(userData.phone) - dispatch(setAccountUpgrade({ id: res.id })) + dispatch( + setAccountUpgrade({ + id: res.id, + accountType: res.account_type, + upgradeCompleted: res.signup_completed, + }), + ) dispatch( setPersonalInfo({ fullName: res.name, diff --git a/app/store/redux/slices/accountUpgradeSlice.ts b/app/store/redux/slices/accountUpgradeSlice.ts index ae582d257..ff6b5c222 100644 --- a/app/store/redux/slices/accountUpgradeSlice.ts +++ b/app/store/redux/slices/accountUpgradeSlice.ts @@ -5,6 +5,7 @@ import { Asset } from "react-native-image-picker" interface AccountUpgradeSlice { id?: string accountType?: "personal" | "business" | "merchant" + upgradeCompleted?: boolean personalInfo: { fullName?: string countryCode?: CountryCode @@ -32,6 +33,7 @@ interface AccountUpgradeSlice { const initialState: AccountUpgradeSlice = { id: undefined, accountType: undefined, + upgradeCompleted: undefined, personalInfo: { fullName: undefined, countryCode: "JM", From 4668b99df47a675a0d27a1007202484364979bd7 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Thu, 8 May 2025 12:26:52 +0500 Subject: [PATCH 48/81] add a logic to show pending status in QuickStart section for upgrade account --- app/components/home-screen/QuickStart.tsx | 25 ++++++++++++++++++++--- app/navigation/stack-param-lists.ts | 1 + 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/components/home-screen/QuickStart.tsx b/app/components/home-screen/QuickStart.tsx index fcd72d122..0b6cebb8d 100644 --- a/app/components/home-screen/QuickStart.tsx +++ b/app/components/home-screen/QuickStart.tsx @@ -34,6 +34,7 @@ type RenderItemProps = { title: string description: string image: any + pending?: boolean onPress: () => void } index: number @@ -61,13 +62,19 @@ const QuickStart = () => { if (credentials) setHasRecoveryPhrase(true) } + const upgradePending = false + let carouselData = [ { type: "upgrade", title: LL.HomeScreen.upgradeTitle(), - description: LL.HomeScreen.upgradeDesc(), + description: upgradePending + ? "Enter test transaction amount to complete upgrading your account" + : LL.HomeScreen.upgradeDesc(), image: Account, - onPress: () => navigation.navigate("AccountType"), + pending: upgradePending, + onPress: () => + navigation.navigate(upgradePending ? "TestTransaction" : "AccountType"), }, { type: "currency", @@ -177,11 +184,23 @@ const QuickStart = () => { const renderItem = ({ item, index }: RenderItemProps) => { const Image = item.image return ( - + {item.title} + {item.pending && ( + + {` (Pending)`} + + )} {item.description} diff --git a/app/navigation/stack-param-lists.ts b/app/navigation/stack-param-lists.ts index bf6aae75e..9c865136f 100644 --- a/app/navigation/stack-param-lists.ts +++ b/app/navigation/stack-param-lists.ts @@ -163,6 +163,7 @@ export type RootStackParamList = { channel: PhoneCodeChannelType } AccountUpgradeSuccess: undefined + TestTransaction: undefined } export type ChatStackParamList = { From 12e78918c3e8bbd94b5e0990b7ee468ff256e94b Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Mon, 12 May 2025 15:57:31 +0500 Subject: [PATCH 49/81] ProgressStep component is implemented for account upgrade flow --- .../account-upgrade-flow/ProgressSteps.tsx | 95 +++++++++++++++++++ app/components/account-upgrade-flow/index.ts | 10 +- 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 app/components/account-upgrade-flow/ProgressSteps.tsx diff --git a/app/components/account-upgrade-flow/ProgressSteps.tsx b/app/components/account-upgrade-flow/ProgressSteps.tsx new file mode 100644 index 000000000..8db675da5 --- /dev/null +++ b/app/components/account-upgrade-flow/ProgressSteps.tsx @@ -0,0 +1,95 @@ +import React from "react" +import { Image, View } from "react-native" +import { makeStyles, Text, useTheme } from "@rneui/themed" + +// assets +import Check from "@app/assets/icons/circleCheck.png" + +type Props = { + numOfSteps: number + currentStep: number +} + +const ProgressSteps: React.FC = ({ numOfSteps, currentStep }) => { + const styles = useStyles() + const { colors } = useTheme().theme + + const renderStep = (i: number) => { + if (currentStep === i + 1) { + return ( + + + {i + 1} + + + ) + } else if (currentStep > i + 1) { + return + } else { + return ( + + + {i + 1} + + + ) + } + } + + return ( + + {Array(numOfSteps) + .fill(0) + .map((_, i) => ( + <> + {renderStep(i)} + {i + 1 < numOfSteps && ( + + )} + + ))} + + ) +} + +export default ProgressSteps + +const useStyles = makeStyles(({ colors }) => ({ + wrapper: { + flexDirection: "row", + alignItems: "center", + marginHorizontal: 20, + marginVertical: 10, + }, + icon: { + width: 35, + height: 35, + }, + separator: { + flex: 1, + height: 3, + backgroundColor: colors._lightBlue, + marginHorizontal: 5, + }, + step: { + width: 35, + height: 35, + alignItems: "center", + justifyContent: "center", + borderRadius: 1000, + borderWidth: 2, + borderColor: colors.grey2, + }, + currentStep: { + backgroundColor: colors._lightBlue, + borderColor: colors._borderBlue, + }, + currentStepText: { + color: "#fff", + }, +})) diff --git a/app/components/account-upgrade-flow/index.ts b/app/components/account-upgrade-flow/index.ts index bb1bc2fe5..0b03743ce 100644 --- a/app/components/account-upgrade-flow/index.ts +++ b/app/components/account-upgrade-flow/index.ts @@ -3,5 +3,13 @@ import InputField from "./InputField" import DropDownField from "./DropDownField" import PhotoUploadField from "./PhotoUploadField" import AddressField from "./AddressField" +import ProgressSteps from "./ProgressSteps" -export { PhoneNumber, InputField, DropDownField, PhotoUploadField, AddressField } +export { + PhoneNumber, + InputField, + DropDownField, + PhotoUploadField, + AddressField, + ProgressSteps, +} From 5092a381240910c82f04a6878b351e5441596298 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Mon, 12 May 2025 16:01:48 +0500 Subject: [PATCH 50/81] ProgressSteps added in AccountType screen and implemented logic to calculate total numOfSteps when press upgrade type --- app/screens/account-upgrade-flow/AccountType.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/screens/account-upgrade-flow/AccountType.tsx b/app/screens/account-upgrade-flow/AccountType.tsx index 670acb695..593a1106c 100644 --- a/app/screens/account-upgrade-flow/AccountType.tsx +++ b/app/screens/account-upgrade-flow/AccountType.tsx @@ -7,6 +7,7 @@ import { AccountLevel } from "@app/graphql/generated" // components import { Screen } from "@app/components/screen" +import { ProgressSteps } from "@app/components/account-upgrade-flow" // hooks import { useLevel } from "@app/graphql/level-context" @@ -30,12 +31,24 @@ const AccountType: React.FC = ({ navigation }) => { }, []) const onPress = (accountType: string) => { - dispatch(setAccountUpgrade({ accountType })) + const numOfSteps = + accountType === "personal" + ? 3 + : currentLevel === AccountLevel.Zero + ? accountType === "business" + ? 4 + : 5 + : accountType === "business" + ? 3 + : 4 + + dispatch(setAccountUpgrade({ accountType, numOfSteps })) navigation.navigate("PersonalInformation") } return ( + {currentLevel === AccountLevel.Zero && ( onPress("personal")}> From 9ffced354879cdb6b68a4f13d5acfda31c3f2ca0 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Mon, 12 May 2025 16:11:35 +0500 Subject: [PATCH 51/81] ProgressSteps used in PersonalInformation, Validation, BusinessInformation and BankInformation screens --- .../account-upgrade-flow/BankInformation.tsx | 3 +++ .../account-upgrade-flow/BusinessInformation.tsx | 13 ++++++++++--- .../account-upgrade-flow/PersonalInformation.tsx | 8 +++++++- app/screens/account-upgrade-flow/Validation.tsx | 5 +++-- app/store/redux/slices/accountUpgradeSlice.ts | 2 ++ 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/app/screens/account-upgrade-flow/BankInformation.tsx b/app/screens/account-upgrade-flow/BankInformation.tsx index fe3f75cf3..461f41da5 100644 --- a/app/screens/account-upgrade-flow/BankInformation.tsx +++ b/app/screens/account-upgrade-flow/BankInformation.tsx @@ -9,6 +9,7 @@ import { DropDownField, InputField, PhotoUploadField, + ProgressSteps, } from "@app/components/account-upgrade-flow" import { Screen } from "@app/components/screen" import { PrimaryBtn } from "@app/components/buttons" @@ -51,6 +52,7 @@ const BankInformation: React.FC = ({ navigation }) => { const [idDocumentErr, setIdDocumentErr] = useState() const { accountType, + numOfSteps, bankInfo: { bankName, bankBranch, @@ -96,6 +98,7 @@ const BankInformation: React.FC = ({ navigation }) => { return ( + = ({ navigation }) => { const [businessNameErr, setBusinessNameErr] = useState() const [businessAddressErr, setBusinessAddressErr] = useState() const { - id, accountType, - personalInfo, + numOfSteps, businessInfo: { businessName, businessAddress, lat, lng }, } = useAppSelector((state) => state.accountUpgrade) @@ -56,6 +59,10 @@ const BusinessInformation: React.FC = ({ navigation }) => { return ( + = ({ navigation }) => { const [phoneNumberErr, setPhoneNumberErr] = useState() const { id, + numOfSteps, personalInfo: { fullName, countryCode, phoneNumber, email }, } = useAppSelector((state) => state.accountUpgrade) @@ -103,6 +108,7 @@ const PersonalInformation: React.FC = ({ navigation }) => { return ( + const Validation: React.FC = ({ navigation, route }) => { const { phone, channel } = route.params const styles = useStyles() - const { accountType, personalInfo } = useAppSelector((state) => state.accountUpgrade) + const { accountType, numOfSteps } = useAppSelector((state) => state.accountUpgrade) const { LL } = useI18nContext() const { saveToken } = useAppConfig() const { toggleActivityIndicator } = useActivityIndicator() @@ -83,6 +83,7 @@ const Validation: React.FC = ({ navigation, route }) => { return ( + {LL.PhoneLoginValidationScreen.header({ diff --git a/app/store/redux/slices/accountUpgradeSlice.ts b/app/store/redux/slices/accountUpgradeSlice.ts index ff6b5c222..39fc44350 100644 --- a/app/store/redux/slices/accountUpgradeSlice.ts +++ b/app/store/redux/slices/accountUpgradeSlice.ts @@ -26,6 +26,7 @@ interface AccountUpgradeSlice { accountNumber?: string idDocument?: Asset } + numOfSteps: number loading: boolean error?: string } @@ -54,6 +55,7 @@ const initialState: AccountUpgradeSlice = { accountNumber: undefined, idDocument: undefined, }, + numOfSteps: 3, loading: false, error: undefined, } From fa17b99809debdefcdd221b9fbb01200dfe4a1f6 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 13 May 2025 11:51:28 +0500 Subject: [PATCH 52/81] update Check icons in ProgressSteps component --- .../account-upgrade-flow/ProgressSteps.tsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/app/components/account-upgrade-flow/ProgressSteps.tsx b/app/components/account-upgrade-flow/ProgressSteps.tsx index 8db675da5..e252cf0c9 100644 --- a/app/components/account-upgrade-flow/ProgressSteps.tsx +++ b/app/components/account-upgrade-flow/ProgressSteps.tsx @@ -1,9 +1,6 @@ import React from "react" -import { Image, View } from "react-native" -import { makeStyles, Text, useTheme } from "@rneui/themed" - -// assets -import Check from "@app/assets/icons/circleCheck.png" +import { View } from "react-native" +import { Icon, makeStyles, Text, useTheme } from "@rneui/themed" type Props = { numOfSteps: number @@ -24,7 +21,9 @@ const ProgressSteps: React.FC = ({ numOfSteps, currentStep }) => { ) } else if (currentStep > i + 1) { - return + return ( + + ) } else { return ( @@ -66,10 +65,6 @@ const useStyles = makeStyles(({ colors }) => ({ marginHorizontal: 20, marginVertical: 10, }, - icon: { - width: 35, - height: 35, - }, separator: { flex: 1, height: 3, From 1f4661a654ee2697199a478a01cd1c77c4453e05 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 13 May 2025 11:54:21 +0500 Subject: [PATCH 53/81] Terminal request check box is added in BusinessInformation screen --- app/hooks/useAccountUpgrade.tsx | 2 +- .../BusinessInformation.tsx | 27 ++++++++++++++++--- app/store/redux/slices/accountUpgradeSlice.ts | 2 ++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/app/hooks/useAccountUpgrade.tsx b/app/hooks/useAccountUpgrade.tsx index 0033756f7..57daf1412 100644 --- a/app/hooks/useAccountUpgrade.tsx +++ b/app/hooks/useAccountUpgrade.tsx @@ -95,7 +95,7 @@ export const useAccountUpgrade = () => { bank_account_number: bankInfo.accountNumber, id_image_url: id_image_url, terms_accepted: true, - terminal_requested: undefined, + terminal_requested: businessInfo.terminalRequested, client_version: readableVersion, device_info: Platform.OS, signup_completed: undefined, diff --git a/app/screens/account-upgrade-flow/BusinessInformation.tsx b/app/screens/account-upgrade-flow/BusinessInformation.tsx index b535e7e67..3f3f6aa33 100644 --- a/app/screens/account-upgrade-flow/BusinessInformation.tsx +++ b/app/screens/account-upgrade-flow/BusinessInformation.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react" -import { View } from "react-native" -import { makeStyles } from "@rneui/themed" +import { TouchableOpacity, View } from "react-native" +import { Icon, makeStyles, Text, useTheme } from "@rneui/themed" import { StackScreenProps } from "@react-navigation/stack" import { RootStackParamList } from "@app/navigation/stack-param-lists" @@ -25,6 +25,7 @@ type Props = StackScreenProps const BusinessInformation: React.FC = ({ navigation }) => { const dispatch = useAppDispatch() const styles = useStyles() + const { colors } = useTheme().theme const { submitAccountUpgrade } = useAccountUpgrade() const [businessNameErr, setBusinessNameErr] = useState() @@ -32,7 +33,7 @@ const BusinessInformation: React.FC = ({ navigation }) => { const { accountType, numOfSteps, - businessInfo: { businessName, businessAddress, lat, lng }, + businessInfo: { businessName, businessAddress, terminalRequested }, } = useAppSelector((state) => state.accountUpgrade) const onPressNext = async () => { @@ -83,6 +84,21 @@ const BusinessInformation: React.FC = ({ navigation }) => { dispatch(setBusinessInfo({ businessAddress: val, lat, lng })) } /> + + dispatch(setBusinessInfo({ terminalRequested: !terminalRequested })) + } + > + + Do you want a Flash terminal? + ({ marginBottom: 10, marginHorizontal: 20, }, + terminalRequest: { + flexDirection: "row", + alignItems: "center", + marginTop: 15, + }, })) diff --git a/app/store/redux/slices/accountUpgradeSlice.ts b/app/store/redux/slices/accountUpgradeSlice.ts index 39fc44350..6cfa27546 100644 --- a/app/store/redux/slices/accountUpgradeSlice.ts +++ b/app/store/redux/slices/accountUpgradeSlice.ts @@ -17,6 +17,7 @@ interface AccountUpgradeSlice { businessAddress?: string lat?: number lng?: number + terminalRequested?: boolean } bankInfo: { bankName?: string @@ -46,6 +47,7 @@ const initialState: AccountUpgradeSlice = { businessAddress: undefined, lat: undefined, lng: undefined, + terminalRequested: undefined, }, bankInfo: { bankName: undefined, From 9fce9e2ebcbbb26ab374ef8b94371e7fc051dd59 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 13 May 2025 12:30:09 +0500 Subject: [PATCH 54/81] react-native-walkthrough-tooltip package is added --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8db496676..87d735133 100644 --- a/package.json +++ b/package.json @@ -185,7 +185,7 @@ "react-native-video": "^5.2.1", "react-native-view-shot": "^3.7.0", "react-native-vision-camera": "3.6.4", - "react-native-walkthrough-tooltip": "^1.4.0", + "react-native-walkthrough-tooltip": "^1.6.0", "react-native-webln": "^0.1.11", "react-native-webview": "^13.8.1", "react-native-webview-crypto": "^0.0.25", From d3eb830addde8703d016d3f48bd61d034edaa047 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 13 May 2025 12:31:48 +0500 Subject: [PATCH 55/81] CheckBoxField component is implemented with help mark tooltip and used in BusinessInformation screen --- .../account-upgrade-flow/AddressField.tsx | 5 +- .../account-upgrade-flow/CheckBoxField.tsx | 62 +++++++++++++++++++ app/components/account-upgrade-flow/index.ts | 2 + .../BusinessInformation.tsx | 23 +++---- 4 files changed, 75 insertions(+), 17 deletions(-) create mode 100644 app/components/account-upgrade-flow/CheckBoxField.tsx diff --git a/app/components/account-upgrade-flow/AddressField.tsx b/app/components/account-upgrade-flow/AddressField.tsx index 98e86ec35..8bc789c04 100644 --- a/app/components/account-upgrade-flow/AddressField.tsx +++ b/app/components/account-upgrade-flow/AddressField.tsx @@ -41,7 +41,7 @@ const AddressField: React.FC = ({ }, [isVisible, ref.current]) return ( - + {label} @@ -102,6 +102,9 @@ const AddressField: React.FC = ({ export default AddressField const useStyles = makeStyles(({ colors }) => ({ + container: { + marginBottom: 15, + }, input: { paddingHorizontal: 15, paddingVertical: 20, diff --git a/app/components/account-upgrade-flow/CheckBoxField.tsx b/app/components/account-upgrade-flow/CheckBoxField.tsx new file mode 100644 index 000000000..b2b7956e8 --- /dev/null +++ b/app/components/account-upgrade-flow/CheckBoxField.tsx @@ -0,0 +1,62 @@ +import React, { useState } from "react" +import { View, TouchableOpacity } from "react-native" +import { Icon, makeStyles, Text, useTheme } from "@rneui/themed" +import Tooltip from "react-native-walkthrough-tooltip" + +type Props = { + isChecked?: boolean + onCheck: () => void +} + +const CheckBoxField: React.FC = ({ isChecked, onCheck }) => { + const styles = useStyles() + const { colors } = useTheme().theme + + const [tooltipVisible, setTooltipVisible] = useState(false) + + return ( + + + + Do you want a Flash terminal? + + + A Flash Terminal is a smart device that can accept payment via Flash for your + business and print receipts. A customer service representative will contact + you if you check this box. + + } + placement="top" + onClose={() => setTooltipVisible(false)} + > + setTooltipVisible(true)}> + + + + + ) +} + +export default CheckBoxField + +const useStyles = makeStyles(() => ({ + container: { + flexDirection: "row", + alignItems: "center", + }, +})) diff --git a/app/components/account-upgrade-flow/index.ts b/app/components/account-upgrade-flow/index.ts index 0b03743ce..68a7decf6 100644 --- a/app/components/account-upgrade-flow/index.ts +++ b/app/components/account-upgrade-flow/index.ts @@ -4,6 +4,7 @@ import DropDownField from "./DropDownField" import PhotoUploadField from "./PhotoUploadField" import AddressField from "./AddressField" import ProgressSteps from "./ProgressSteps" +import CheckBoxField from "./CheckBoxField" export { PhoneNumber, @@ -12,4 +13,5 @@ export { PhotoUploadField, AddressField, ProgressSteps, + CheckBoxField, } diff --git a/app/screens/account-upgrade-flow/BusinessInformation.tsx b/app/screens/account-upgrade-flow/BusinessInformation.tsx index 3f3f6aa33..7730dd429 100644 --- a/app/screens/account-upgrade-flow/BusinessInformation.tsx +++ b/app/screens/account-upgrade-flow/BusinessInformation.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react" -import { TouchableOpacity, View } from "react-native" -import { Icon, makeStyles, Text, useTheme } from "@rneui/themed" +import { View } from "react-native" +import { makeStyles } from "@rneui/themed" import { StackScreenProps } from "@react-navigation/stack" import { RootStackParamList } from "@app/navigation/stack-param-lists" @@ -9,6 +9,7 @@ import { Screen } from "@app/components/screen" import { PrimaryBtn } from "@app/components/buttons" import { AddressField, + CheckBoxField, InputField, ProgressSteps, } from "@app/components/account-upgrade-flow" @@ -25,7 +26,6 @@ type Props = StackScreenProps const BusinessInformation: React.FC = ({ navigation }) => { const dispatch = useAppDispatch() const styles = useStyles() - const { colors } = useTheme().theme const { submitAccountUpgrade } = useAccountUpgrade() const [businessNameErr, setBusinessNameErr] = useState() @@ -84,21 +84,12 @@ const BusinessInformation: React.FC = ({ navigation }) => { dispatch(setBusinessInfo({ businessAddress: val, lat, lng })) } /> - + dispatch(setBusinessInfo({ terminalRequested: !terminalRequested })) } - > - - Do you want a Flash terminal? - + /> Date: Tue, 13 May 2025 15:21:37 +0500 Subject: [PATCH 56/81] account upgrade flow texts are added in i18n internationalization --- app/i18n/en/index.ts | 43 +++++- app/i18n/i18n-types.ts | 290 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 328 insertions(+), 5 deletions(-) diff --git a/app/i18n/en/index.ts b/app/i18n/en/index.ts index dbea9d71a..362aa31b2 100644 --- a/app/i18n/en/index.ts +++ b/app/i18n/en/index.ts @@ -1417,7 +1417,7 @@ const en: BaseTranslation = { createProfilePubkeyMessage: "We couldn't find a profile event attached to this pubkey.", createProfilePrompt: "Do you want to continue to create?", createProfileButton: "Create Profile", - profileNotFound: "We’re looking, but we haven’t been able to find your profile.", + profileNotFound: "We're looking, but we haven't been able to find your profile.", promptToCreateProfile: "Would you like to create one now?", learnAboutNostr: "Learn about Nostr", learnAboutNostrSubtext: "Explore this guide to get the most out of nostr chat", @@ -1436,7 +1436,7 @@ const en: BaseTranslation = { importNsecDescription: "If you wish to use your own nsec, paste it below.", profileImportedSuccessfully: "Profile imported successfully", noProfileFound: "No Nostr Profile Found", - noProfileDescription: "You haven’t created a Nostr profile yet.\nTap below to create one.", + noProfileDescription: "You haven't created a Nostr profile yet.\nTap below to create one.", creatingProfile: "Creating Profile...", createNewProfile: "Create New Profile", findingYou: "Finding You..", @@ -1465,7 +1465,44 @@ const en: BaseTranslation = { copied: "Copied", goToSettings: "Go to settings" } - } + }, + AccountUpgrade: { + accountType: "Account Type", + personal: "Personal", + personalDesc: "For individual use, no additional info needed", + pro: "Pro", + proDesc: "Accept payments as a Pro Flashpoint. Business Name & Address required", + merchant: "Merchant", + merchantDesc: "Give rewards as a Merchant Flahpoint. ID and Bank account info required", + personalInfo: "Personal Information", + fullName: "Full name", + phoneNumber: "Phone Number", + email: "Email Address", + optional: " (Optional)", + validation: "Validation", + validationCode: "Validation code", + businessInfo: "Business Information", + businessName: "Business Name", + businessNamePlaceholder: "Enter your business name", + businessAddress: "Business Address", + businessAddressPlaceholder: "Enter your business address", + flashTerminal: "Do you want a Flash terminal?", + flashTerminalTooltip: "A Flash Terminal is a smart device that can accept payment via Flash for your business and print receipts. A customer service representative will contact you if you check this box.", + bankingInfo: "Banking Information", + bankName: "Bank Name", + bankNamePlaceholder: "Enter your bank name", + bankBranch: "Bank Branch", + bankBranchPlaceholder: "Enter your bank branch", + bankAccountType: "Account Type", + selectBankAccountType: "Select account type", + currency: "Currency", + selectCurrency: "Select Currency", + accountNum: "Account Number", + accountNumPlaceholder: "Enter your account number", + uploadId: "Upload ID Document", + successTitle: "You successfully requested to upgrade your account to {accountType: string}", + successDesc: "* Please enter the test transaction amount to confirm your bank details.", + } } export default en diff --git a/app/i18n/i18n-types.ts b/app/i18n/i18n-types.ts index a6b4bf1f3..3059641f6 100644 --- a/app/i18n/i18n-types.ts +++ b/app/i18n/i18n-types.ts @@ -4593,6 +4593,149 @@ type RootTranslation = { * T​r​a​n​s​a​c​t​i​o​n​ ​I​D */ txId: string + }, + AccountUpgrade: { + /** + * Account Type + */ + accountType: string + /** + * Personal + */ + personal: string + /** + * For individual use, no additional info needed + */ + personalDesc: string + /** + * Pro + */ + pro: string + /** + * Accept payments as a Pro Flashpoint. Business Name & Address required + */ + proDesc: string + /** + * Merchant + */ + merchant: string + /** + * Give rewards as a Merchant Flahpoint. ID and Bank account info required + */ + merchantDesc: string + /** + * Personal Information + */ + personalInfo: string + /** + * Full name + */ + fullName: string + /** + * Phone Number + */ + phoneNumber: string + /** + * Email Address + */ + email: string + /** + * Optional + */ + optional: string + /** + * Validation + */ + validation: string + /** + * Validation code + */ + validationCode: string + /** + * Business Information + */ + businessInfo: string + /** + * Business Name + */ + businessName: string + /** + * Enter your business name + */ + businessNamePlaceholder: string + /** + * Business Address + */ + businessAddress: string + /** + * Enter your business address + */ + businessAddressPlaceholder: string + /** + * Do you want a Flash terminal? + */ + flashTerminal: string + /** + * A Flash Terminal is a smart device that can accept payment via Flash for your business and print receipts. A customer service representative will contact you if you check this box. + */ + flashTerminalTooltip: string + /** + * Banking Information + */ + bankingInfo: string + /** + * Bank Name + */ + bankName: string + /** + * Enter your bank name + */ + bankNamePlaceholder: string + /** + * Bank Branch + */ + bankBranch: string + /** + * Enter your bank branch + */ + bankBranchPlaceholder: string + /** + * Account Type + */ + bankAccountType: string + /** + * Select account type + */ + selectBankAccountType: string, + /** + * Currency + */ + currency: string + /** + * Select currency + */ + selectCurrency: string, + /** + * Account Number + */ + accountNum: string + /** + * Enter your account number + */ + accountNumPlaceholder: string + /** + * Upload ID Document + */ + uploadId: string + /** + * You successfully requested to upgrade your account to {accountType} + * @param {string} accountType + */ + successTitle: RequiredParams<'accountType'> + /** + * * Please enter the test transaction amount to confirm your bank details. + */ + successDesc: string } Nostr: { /** @@ -9309,7 +9452,7 @@ export type TranslationFunctions = { */ createProfileButton: () => LocalizedString /** - * We’re looking, but we haven’t been able to find your profile. + * We're looking, but we haven't been able to find your profile. */ profileNotFound: () => LocalizedString /** @@ -9381,7 +9524,7 @@ export type TranslationFunctions = { */ noProfileFound: () => LocalizedString /** - * You haven’t created a Nostr profile yet. + * You haven't created a Nostr profile yet. Tap below to create one. */ noProfileDescription: () => LocalizedString @@ -9480,6 +9623,149 @@ export type TranslationFunctions = { goToSettings: () => LocalizedString } } + AccountUpgrade: { + /** + * Account Type + */ + accountType: () => LocalizedString + /** + * Personal + */ + personal: () => LocalizedString + /** + * For individual use, no additional info needed + */ + personalDesc: () => LocalizedString + /** + * Pro + */ + pro: () => LocalizedString + /** + * Accept payments as a Pro Flashpoint. Business Name & Address required + */ + proDesc: () => LocalizedString + /** + * Merchant + */ + merchant: () => LocalizedString + /** + * Give rewards as a Merchant Flahpoint. ID and Bank account info required + */ + merchantDesc: () => LocalizedString + /** + * Personal Information + */ + personalInfo: () => LocalizedString + /** + * Full name + */ + fullName: () => LocalizedString + /** + * Phone Number + */ + phoneNumber: () => LocalizedString + /** + * Email Address + */ + email: () => LocalizedString + /** + * Optional + */ + optional: () => LocalizedString + /** + * Validation + */ + validation: () => LocalizedString + /** + * Validation code + */ + validationCode: () => LocalizedString + /** + * Business Information + */ + businessInfo: () => LocalizedString + /** + * Business Name + */ + businessName: () => LocalizedString + /** + * Enter your business name + */ + businessNamePlaceholder: () => LocalizedString + /** + * Business Address + */ + businessAddress: () => LocalizedString + /** + * Enter your business address + */ + businessAddressPlaceholder: () => LocalizedString + /** + * Do you want a Flash terminal? + */ + flashTerminal: () => LocalizedString + /** + * A Flash Terminal is a smart device that can accept payment via Flash for your business and print receipts. A customer service representative will contact you if you check this box. + */ + flashTerminalTooltip: () => LocalizedString + /** + * Banking Information + */ + bankingInfo: () => LocalizedString + /** + * Bank Name + */ + bankName: () => LocalizedString + /** + * Enter your bank name + */ + bankNamePlaceholder: () => LocalizedString + /** + * Bank Branch + */ + bankBranch: () => LocalizedString + /** + * Enter your bank branch + */ + bankBranchPlaceholder: () => LocalizedString + /** + * Account Type + */ + bankAccountType: () => LocalizedString + /** + * Select account type + */ + selectBankAccountType: () => LocalizedString + /** + * Currency + */ + currency: () => LocalizedString + /** + * Select currency + */ + selectCurrency: () => LocalizedString + /** + * Account Number + */ + accountNum: () => LocalizedString + /** + * Enter your account number + */ + accountNumPlaceholder: () => LocalizedString + /** + * Upload ID Document + */ + uploadId: () => LocalizedString + /** + * You successfully requested to upgrade your account to {accountType} + */ + successTitle: (arg: { accountType: string }) => LocalizedString + /** + * * Please enter the test transaction amount to confirm your bank details. + */ + successDesc: () => LocalizedString + } +} } export type Formatters = { From 95f6642ce59f2eaf1d138317f8b978ab999f38fb Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Tue, 13 May 2025 15:29:29 +0500 Subject: [PATCH 57/81] use i18n internationalization texts in AccountType, PersonalInformation, Validation, BusinessInformation, BankInformation and Success screens --- .../account-upgrade-flow/CheckBoxField.tsx | 14 ++++------ .../account-upgrade-flow/InputField.tsx | 5 ++-- .../account-upgrade-flow/PhoneNumber.tsx | 2 +- app/navigation/root-navigator.tsx | 10 +++---- .../account-upgrade-flow/AccountType.tsx | 14 +++++----- .../account-upgrade-flow/BankInformation.tsx | 26 ++++++++++--------- .../BusinessInformation.tsx | 12 +++++---- .../PersonalInformation.tsx | 6 ++--- app/screens/account-upgrade-flow/Success.tsx | 10 +++++-- .../account-upgrade-flow/Validation.tsx | 2 +- 10 files changed, 55 insertions(+), 46 deletions(-) diff --git a/app/components/account-upgrade-flow/CheckBoxField.tsx b/app/components/account-upgrade-flow/CheckBoxField.tsx index b2b7956e8..f17c6c940 100644 --- a/app/components/account-upgrade-flow/CheckBoxField.tsx +++ b/app/components/account-upgrade-flow/CheckBoxField.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react" import { View, TouchableOpacity } from "react-native" import { Icon, makeStyles, Text, useTheme } from "@rneui/themed" import Tooltip from "react-native-walkthrough-tooltip" +import { useI18nContext } from "@app/i18n/i18n-react" type Props = { isChecked?: boolean @@ -11,6 +12,7 @@ type Props = { const CheckBoxField: React.FC = ({ isChecked, onCheck }) => { const styles = useStyles() const { colors } = useTheme().theme + const { LL } = useI18nContext() const [tooltipVisible, setTooltipVisible] = useState(false) @@ -24,17 +26,11 @@ const CheckBoxField: React.FC = ({ isChecked, onCheck }) => { type="ionicon" style={{ marginRight: 10 }} /> - Do you want a Flash terminal? + {LL.AccountUpgrade.flashTerminal()} - A Flash Terminal is a smart device that can accept payment via Flash for your - business and print receipts. A customer service representative will contact - you if you check this box. - - } + content={{LL.AccountUpgrade.flashTerminalTooltip()}} placement="top" onClose={() => setTooltipVisible(false)} > @@ -44,7 +40,7 @@ const CheckBoxField: React.FC = ({ isChecked, onCheck }) => { size={25} color={colors._lightBlue} type="ionicon" - style={{ marginHorizontal: 10 }} + style={{ marginHorizontal: 5 }} /> diff --git a/app/components/account-upgrade-flow/InputField.tsx b/app/components/account-upgrade-flow/InputField.tsx index 8d74e4f89..8d5d29547 100644 --- a/app/components/account-upgrade-flow/InputField.tsx +++ b/app/components/account-upgrade-flow/InputField.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react" import { makeStyles, Text, useTheme } from "@rneui/themed" import { TextInput, TextInputProps, View } from "react-native" +import { useI18nContext } from "@app/i18n/i18n-react" type Props = { label: string @@ -18,6 +19,7 @@ const InputField: React.FC = ({ }) => { const styles = useStyles() const { colors } = useTheme().theme + const { LL } = useI18nContext() const [isFocused, setIsFocused] = useState(false) @@ -27,8 +29,7 @@ const InputField: React.FC = ({ {label} {isOptional && ( - {" "} - (Optional) + {LL.AccountUpgrade.optional()} )} diff --git a/app/components/account-upgrade-flow/PhoneNumber.tsx b/app/components/account-upgrade-flow/PhoneNumber.tsx index b9cd9d593..9c8d545ec 100644 --- a/app/components/account-upgrade-flow/PhoneNumber.tsx +++ b/app/components/account-upgrade-flow/PhoneNumber.tsx @@ -62,7 +62,7 @@ const PhoneNumber: React.FC = ({ return ( - Phone Number + {LL.AccountUpgrade.phoneNumber()} { = ({ navigation }) => { const dispatch = useAppDispatch() const styles = useStyles() const { colors } = useTheme().theme + const { LL } = useI18nContext() const { currentLevel } = useLevel() const { fetchAccountUpgrade } = useAccountUpgrade() @@ -54,10 +56,10 @@ const AccountType: React.FC = ({ navigation }) => { - Personal + {LL.AccountUpgrade.personal()} - For individual use, no additional info needed + {LL.AccountUpgrade.personalDesc()} @@ -68,10 +70,10 @@ const AccountType: React.FC = ({ navigation }) => { - Pro + {LL.AccountUpgrade.pro()} - Accept payments as a Pro Flashpoint. Business Name & Address required + {LL.AccountUpgrade.proDesc()} @@ -81,10 +83,10 @@ const AccountType: React.FC = ({ navigation }) => { - Merchant + {LL.AccountUpgrade.merchant()} - Give rewards as a Merchant Flashpoint. ID and Bank account info required + {LL.AccountUpgrade.merchantDesc()} diff --git a/app/screens/account-upgrade-flow/BankInformation.tsx b/app/screens/account-upgrade-flow/BankInformation.tsx index 461f41da5..33eafb9b9 100644 --- a/app/screens/account-upgrade-flow/BankInformation.tsx +++ b/app/screens/account-upgrade-flow/BankInformation.tsx @@ -16,6 +16,7 @@ import { PrimaryBtn } from "@app/components/buttons" // hooks import { useAccountUpgrade } from "@app/hooks" +import { useI18nContext } from "@app/i18n/i18n-react" // store import { useAppDispatch, useAppSelector } from "@app/store/redux" @@ -42,6 +43,7 @@ type Props = StackScreenProps const BankInformation: React.FC = ({ navigation }) => { const dispatch = useAppDispatch() const styles = useStyles() + const { LL } = useI18nContext() const { submitAccountUpgrade } = useAccountUpgrade() const [nameErr, setNameErr] = useState() @@ -101,8 +103,8 @@ const BankInformation: React.FC = ({ navigation }) => { { @@ -111,8 +113,8 @@ const BankInformation: React.FC = ({ navigation }) => { }} /> { @@ -121,8 +123,8 @@ const BankInformation: React.FC = ({ navigation }) => { }} /> = ({ navigation }) => { }} /> = ({ navigation }) => { }} /> { @@ -153,14 +155,14 @@ const BankInformation: React.FC = ({ navigation }) => { }} /> dispatch(setBankInfo({ idDocument: val }))} setErrorMsg={setIdDocumentErr} /> - + ) } diff --git a/app/screens/account-upgrade-flow/BusinessInformation.tsx b/app/screens/account-upgrade-flow/BusinessInformation.tsx index 7730dd429..d3fcd4a7b 100644 --- a/app/screens/account-upgrade-flow/BusinessInformation.tsx +++ b/app/screens/account-upgrade-flow/BusinessInformation.tsx @@ -16,6 +16,7 @@ import { // hooks import { useAccountUpgrade } from "@app/hooks" +import { useI18nContext } from "@app/i18n/i18n-react" // store import { useAppDispatch, useAppSelector } from "@app/store/redux" @@ -26,6 +27,7 @@ type Props = StackScreenProps const BusinessInformation: React.FC = ({ navigation }) => { const dispatch = useAppDispatch() const styles = useStyles() + const { LL } = useI18nContext() const { submitAccountUpgrade } = useAccountUpgrade() const [businessNameErr, setBusinessNameErr] = useState() @@ -66,8 +68,8 @@ const BusinessInformation: React.FC = ({ navigation }) => { /> { @@ -76,8 +78,8 @@ const BusinessInformation: React.FC = ({ navigation }) => { }} /> @@ -92,7 +94,7 @@ const BusinessInformation: React.FC = ({ navigation }) => { /> = ({ navigation }) => { = ({ navigation }) => { }} /> = ({ navigation }) => { ) : ( diff --git a/app/screens/account-upgrade-flow/Success.tsx b/app/screens/account-upgrade-flow/Success.tsx index 402a52369..e032641db 100644 --- a/app/screens/account-upgrade-flow/Success.tsx +++ b/app/screens/account-upgrade-flow/Success.tsx @@ -11,6 +11,9 @@ import { PrimaryBtn } from "@app/components/buttons" // assets import Account from "@app/assets/illustrations/account.svg" +// hooks +import { useI18nContext } from "@app/i18n/i18n-react" + // store import { useAppDispatch, useAppSelector } from "@app/store/redux" import { resetAccountUpgrade } from "@app/store/redux/slices/accountUpgradeSlice" @@ -20,6 +23,7 @@ type Props = StackScreenProps const Success: React.FC = ({ navigation }) => { const styles = useStyles() const { colors } = useTheme().theme + const { LL } = useI18nContext() const dispatch = useAppDispatch() const { accountType } = useAppSelector((state) => state.accountUpgrade) @@ -36,7 +40,9 @@ const Success: React.FC = ({ navigation }) => { - {`You successfully requested to upgrade your account to ${accountType?.toUpperCase()}`} + {LL.AccountUpgrade.successTitle({ + accountType: accountType?.toUpperCase() || "", + })} {accountType === "merchant" && ( @@ -45,7 +51,7 @@ const Success: React.FC = ({ navigation }) => { color={colors.grey5} style={{ marginHorizontal: 30, textAlign: "center" }} > - * Please enter the test transaction amount to confirm your bank details. + {LL.AccountUpgrade.successDesc()} )} diff --git a/app/screens/account-upgrade-flow/Validation.tsx b/app/screens/account-upgrade-flow/Validation.tsx index 3426cb7ee..1174d770a 100644 --- a/app/screens/account-upgrade-flow/Validation.tsx +++ b/app/screens/account-upgrade-flow/Validation.tsx @@ -92,7 +92,7 @@ const Validation: React.FC = ({ navigation, route }) => { })} Date: Tue, 13 May 2025 17:38:28 +0500 Subject: [PATCH 58/81] use i18n internationalization texts in TestTransaction screen --- app/components/home-screen/QuickStart.tsx | 2 +- app/i18n/en/index.ts | 5 +++ app/i18n/i18n-types.ts | 35 ++++++++++++++++++- app/navigation/root-navigator.tsx | 2 +- .../account-upgrade-flow/TestTransaction.tsx | 21 ++++++++--- 5 files changed, 57 insertions(+), 8 deletions(-) diff --git a/app/components/home-screen/QuickStart.tsx b/app/components/home-screen/QuickStart.tsx index 0b6cebb8d..0e359b8a6 100644 --- a/app/components/home-screen/QuickStart.tsx +++ b/app/components/home-screen/QuickStart.tsx @@ -69,7 +69,7 @@ const QuickStart = () => { type: "upgrade", title: LL.HomeScreen.upgradeTitle(), description: upgradePending - ? "Enter test transaction amount to complete upgrading your account" + ? LL.HomeScreen.upgradePendingDesc() : LL.HomeScreen.upgradeDesc(), image: Account, pending: upgradePending, diff --git a/app/i18n/en/index.ts b/app/i18n/en/index.ts index 362aa31b2..96db40b44 100644 --- a/app/i18n/en/index.ts +++ b/app/i18n/en/index.ts @@ -552,6 +552,7 @@ const en: BaseTranslation = { addFlashcard: "Add Flashcard", upgradeTitle: "Upgrade your account", upgradeDesc: "Backup your cash wallet and increase transaction limits.", + upgradePendingDesc: "Enter test transaction amount to complete upgrading your account.", currencyTitle:"Change to your local currency", currencyDesc: "Review our available currency list and select your currency.", flashcardTitle: "Get a Flashcard", @@ -1502,6 +1503,10 @@ const en: BaseTranslation = { uploadId: "Upload ID Document", successTitle: "You successfully requested to upgrade your account to {accountType: string}", successDesc: "* Please enter the test transaction amount to confirm your bank details.", + transactionVerification: "Transaction Verification", + transactionTitle: "To complete upgrading your account to MERCHANT, enter the test transaction amount we sent to your bank account {accountNum: string} to confirm your bank details.", + transactionAmount: "Transaction amount", + } } diff --git a/app/i18n/i18n-types.ts b/app/i18n/i18n-types.ts index 3059641f6..7e919f78c 100644 --- a/app/i18n/i18n-types.ts +++ b/app/i18n/i18n-types.ts @@ -1729,7 +1729,11 @@ type RootTranslation = { */ upgradeDesc: string /** - * C​h​a​n​g​e​ ​t​o​ ​y​o​u​r​ ​l​o​c​a​l​ ​c​u​r​r​e​n​c​y + * Enter test transaction amount to complete upgrading your account. + */ + upgradePendingDesc: string + /** + * Change to your local currency */ currencyTitle: string /** @@ -4736,6 +4740,19 @@ type RootTranslation = { * * Please enter the test transaction amount to confirm your bank details. */ successDesc: string + /** + * * Transaction Verification + */ + transactionVerification: string + /** + * To complete upgrading your account to MERCHANT, enter the test transaction amount we sent to your bank account {accountNum} to confirm your bank details. + * @param {string} accountNum + */ + transactionTitle: RequiredParams<'accountNum'> + /** + * Transaction amount + */ + transactionAmount: string } Nostr: { /** @@ -6629,6 +6646,10 @@ export type TranslationFunctions = { * Backup your cash wallet and increase transaction limits. */ upgradeDesc: () => LocalizedString + /** + * Enter test transaction amount to complete upgrading your account. + */ + upgradePendingDesc: () => LocalizedString /** * Change to your local currency */ @@ -9764,6 +9785,18 @@ export type TranslationFunctions = { * * Please enter the test transaction amount to confirm your bank details. */ successDesc: () => LocalizedString + /** + * * Transaction Verification + */ + transactionVerification: () => LocalizedString + /** + * To complete upgrading your account to MERCHANT, enter the test transaction amount we sent to your bank account {accountNum} to confirm your bank details. + */ + transactionTitle: (arg: { accountNum: string }) => LocalizedString + /** + * Transaction amount + */ + transactionAmount: () => LocalizedString } } } diff --git a/app/navigation/root-navigator.tsx b/app/navigation/root-navigator.tsx index f4d525823..405122f16 100644 --- a/app/navigation/root-navigator.tsx +++ b/app/navigation/root-navigator.tsx @@ -628,7 +628,7 @@ export const RootStack = () => { ) diff --git a/app/screens/account-upgrade-flow/TestTransaction.tsx b/app/screens/account-upgrade-flow/TestTransaction.tsx index a23e6cf1d..bd9466d65 100644 --- a/app/screens/account-upgrade-flow/TestTransaction.tsx +++ b/app/screens/account-upgrade-flow/TestTransaction.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react" +import React, { useEffect, useState } from "react" import { RootStackParamList } from "@app/navigation/stack-param-lists" import { StackScreenProps } from "@react-navigation/stack" import { makeStyles, Text } from "@rneui/themed" @@ -10,30 +10,41 @@ import { PrimaryBtn } from "@app/components/buttons" import { InputField } from "@app/components/account-upgrade-flow" // hooks -import { useActivityIndicator } from "@app/hooks" +import { useAccountUpgrade, useActivityIndicator } from "@app/hooks" import { useI18nContext } from "@app/i18n/i18n-react" +// store +import { useAppSelector } from "@app/store/redux" + type Props = StackScreenProps const TestTransaction: React.FC = ({ navigation, route }) => { const styles = useStyles() const { LL } = useI18nContext() + const { fetchAccountUpgrade } = useAccountUpgrade() const { toggleActivityIndicator } = useActivityIndicator() const [errorMsg, setErrorMsg] = useState() const [amount, setAmount] = useState() + const { bankInfo } = useAppSelector((state) => state.accountUpgrade) + + useEffect(() => { + fetchAccountUpgrade() + }, []) + const onConfirm = () => {} return ( - To complete upgrading your account to MERCHANT, enter the test transaction - amount we sent your bank account to confirm your bank details. + {LL.AccountUpgrade.transactionTitle({ + accountNum: "****" + bankInfo.accountNumber?.slice(-4) || "", + })} Date: Wed, 14 May 2025 13:07:00 -0400 Subject: [PATCH 59/81] label changes --- app/i18n/en/index.ts | 8 ++++---- app/i18n/i18n-types.ts | 16 ++++++++-------- app/i18n/raw-i18n/source/en.json | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/i18n/en/index.ts b/app/i18n/en/index.ts index 96db40b44..38104a4a4 100644 --- a/app/i18n/en/index.ts +++ b/app/i18n/en/index.ts @@ -551,7 +551,7 @@ const en: BaseTranslation = { flashcard: "Flashcard", addFlashcard: "Add Flashcard", upgradeTitle: "Upgrade your account", - upgradeDesc: "Backup your cash wallet and increase transaction limits.", + upgradeDesc: "Unlock your full potential! Upgrade to boost visibility, limits, and payouts.", upgradePendingDesc: "Enter test transaction amount to complete upgrading your account.", currencyTitle:"Change to your local currency", currencyDesc: "Review our available currency list and select your currency.", @@ -1470,11 +1470,11 @@ const en: BaseTranslation = { AccountUpgrade: { accountType: "Account Type", personal: "Personal", - personalDesc: "For individual use, no additional info needed", + personalDesc: "Secure your wallet with phone and email. Stay safe and recover easily if needed", pro: "Pro", - proDesc: "Accept payments as a Pro Flashpoint. Business Name & Address required", + proDesc: "Accept payments and get discovered on the map. Requires a business name and location.", merchant: "Merchant", - merchantDesc: "Give rewards as a Merchant Flahpoint. ID and Bank account info required", + merchantDesc: "Give rewards, appear on the map, and settle to your bank. ID and bank info required.", personalInfo: "Personal Information", fullName: "Full name", phoneNumber: "Phone Number", diff --git a/app/i18n/i18n-types.ts b/app/i18n/i18n-types.ts index 7e919f78c..1e93d8df8 100644 --- a/app/i18n/i18n-types.ts +++ b/app/i18n/i18n-types.ts @@ -1725,7 +1725,7 @@ type RootTranslation = { */ upgradeTitle: string /** - * B​a​c​k​u​p​ ​y​o​u​r​ ​c​a​s​h​ ​w​a​l​l​e​t​ ​a​n​d​ ​i​n​c​r​e​a​s​e​ ​t​r​a​n​s​a​c​t​i​o​n​ ​l​i​m​i​t​s​. + * Unlock your full potential! Upgrade to boost visibility, limits, and payouts. */ upgradeDesc: string /** @@ -4608,7 +4608,7 @@ type RootTranslation = { */ personal: string /** - * For individual use, no additional info needed + * Secure your wallet with phone and email. Stay safe and recover easily if needed */ personalDesc: string /** @@ -4616,7 +4616,7 @@ type RootTranslation = { */ pro: string /** - * Accept payments as a Pro Flashpoint. Business Name & Address required + * Accept payments and get discovered on the map. Requires a business name and location. */ proDesc: string /** @@ -4624,7 +4624,7 @@ type RootTranslation = { */ merchant: string /** - * Give rewards as a Merchant Flahpoint. ID and Bank account info required + * Give rewards, appear on the map, and settle to your bank. ID and bank info required. */ merchantDesc: string /** @@ -6643,7 +6643,7 @@ export type TranslationFunctions = { */ upgradeTitle: () => LocalizedString /** - * Backup your cash wallet and increase transaction limits. + * Unlock your full potential! Upgrade to boost visibility, limits, and payouts. */ upgradeDesc: () => LocalizedString /** @@ -9654,7 +9654,7 @@ export type TranslationFunctions = { */ personal: () => LocalizedString /** - * For individual use, no additional info needed + * Secure your wallet with phone and email. Stay safe and recover easily if needed */ personalDesc: () => LocalizedString /** @@ -9662,7 +9662,7 @@ export type TranslationFunctions = { */ pro: () => LocalizedString /** - * Accept payments as a Pro Flashpoint. Business Name & Address required + * Accept payments and get discovered on the map. Requires a business name and location. */ proDesc: () => LocalizedString /** @@ -9670,7 +9670,7 @@ export type TranslationFunctions = { */ merchant: () => LocalizedString /** - * Give rewards as a Merchant Flahpoint. ID and Bank account info required + * Give rewards, appear on the map, and settle to your bank. ID and bank info required. */ merchantDesc: () => LocalizedString /** diff --git a/app/i18n/raw-i18n/source/en.json b/app/i18n/raw-i18n/source/en.json index d2dcd416b..c12502262 100644 --- a/app/i18n/raw-i18n/source/en.json +++ b/app/i18n/raw-i18n/source/en.json @@ -520,7 +520,7 @@ "flashcard": "Flashcard", "addFlashcard": "Add Flashcard", "upgradeTitle": "Upgrade your account", - "upgradeDesc": "Backup your cash wallet and increase transaction limits.", + "upgradeDesc": "Unlock your full potential! Upgrade to boost visibility, limits, and payouts.", "currencyTitle": "Change to your local currency", "currencyDesc": "Review our available currency list and select your currency.", "flashcardTitle": "Get a Flashcard", From b4ed2b9642e3525645be8043f119ca52a4ca2bdb Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Sat, 17 May 2025 17:14:50 +0500 Subject: [PATCH 60/81] hide x button for pending quick start --- app/components/home-screen/QuickStart.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/components/home-screen/QuickStart.tsx b/app/components/home-screen/QuickStart.tsx index 0e359b8a6..2155600f7 100644 --- a/app/components/home-screen/QuickStart.tsx +++ b/app/components/home-screen/QuickStart.tsx @@ -204,9 +204,11 @@ const QuickStart = () => { {item.description} - onHide(item.type)}> - - + {!item.pending && ( + onHide(item.type)}> + + + )} ) } From 6c924d3f46d853fd22ec1aa060a4894ff741a337 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Sun, 18 May 2025 11:24:37 +0500 Subject: [PATCH 61/81] add redux-persist package --- package.json | 1 + yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/package.json b/package.json index 87d735133..880304008 100644 --- a/package.json +++ b/package.json @@ -190,6 +190,7 @@ "react-native-webview": "^13.8.1", "react-native-webview-crypto": "^0.0.25", "react-redux": "^9.1.0", + "redux-persist": "^6.0.0", "rn-qr-generator": "^1.2.1", "styled-components": "^6.1.8", "subscriptions-transport-ws": "^0.11.0", diff --git a/yarn.lock b/yarn.lock index bfe49fb64..41ff1559a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22029,6 +22029,11 @@ redis-parser@^3.0.0: dependencies: redis-errors "^1.0.0" +redux-persist@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8" + integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ== + redux-thunk@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3" From 1d012fca195842090ae6ec3e7737a55c2f2eb142 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Sun, 18 May 2025 11:28:48 +0500 Subject: [PATCH 62/81] Implemented logic to save account upgrade flow data to AsyncStorage and prepopulate previously entered data when the app restarts --- app/app.tsx | 29 ++++++++++++++++------------- app/store/redux/index.ts | 29 ++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/app/app.tsx b/app/app.tsx index cf35c1d7e..485f6120b 100644 --- a/app/app.tsx +++ b/app/app.tsx @@ -36,7 +36,7 @@ import { FeatureFlagContextProvider } from "./config/feature-flags-context" import "./utils/logs" import { GestureHandlerRootView } from "react-native-gesture-handler" import { Provider } from "react-redux" -import { store } from "./store/redux" +import { persistor, store } from "./store/redux" import PolyfillCrypto from "react-native-webview-crypto" import { ActivityIndicatorProvider } from "./contexts/ActivityIndicatorContext" import { BreezProvider } from "./contexts/BreezContext" @@ -45,6 +45,7 @@ import { NotificationsProvider } from "./components/notification" import { SafeAreaProvider } from "react-native-safe-area-context" import { FlashcardProvider } from "./contexts/Flashcard" import { NostrGroupChatProvider } from "./screens/chat/GroupChat/GroupChatProvider" +import { PersistGate } from "redux-persist/integration/react" // FIXME should we only load the currently used local? // this would help to make the app load faster @@ -67,14 +68,15 @@ export const App = () => ( - - - - + + + + + @@ -101,10 +103,11 @@ export const App = () => ( - - - - + + + + + diff --git a/app/store/redux/index.ts b/app/store/redux/index.ts index 5fdf7d953..f80d1239f 100644 --- a/app/store/redux/index.ts +++ b/app/store/redux/index.ts @@ -2,11 +2,38 @@ import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit" import { useDispatch, useSelector } from "react-redux" import type { TypedUseSelectorHook } from "react-redux" import rootReducer from "./reducers" +import { + persistStore, + persistReducer, + FLUSH, + REHYDRATE, + PAUSE, + PERSIST, + PURGE, + REGISTER, +} from "redux-persist" +import AsyncStorage from "@react-native-async-storage/async-storage" + +const persistConfig = { + key: "root", + storage: AsyncStorage, + whitelist: ["accountUpgrade"], +} + +const persistedReducer = persistReducer(persistConfig, rootReducer) export const store = configureStore({ - reducer: rootReducer, + reducer: persistedReducer, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ + serializableCheck: { + ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], + }, + }), }) +export const persistor = persistStore(store) + export type AppDispatch = typeof store.dispatch export type RootState = ReturnType From 773bcc68e4605e5b4d8b8748a5c7fe517d93783f Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Mon, 19 May 2025 11:09:56 +0500 Subject: [PATCH 63/81] update fetchAccountUpgrade method in useAccountUpgrade hook and use it in Validation screen to fetch user supabase data if there is one for provided phone number --- app/hooks/useAccountUpgrade.tsx | 76 ++++++++++--------- .../account-upgrade-flow/Validation.tsx | 6 +- 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/app/hooks/useAccountUpgrade.tsx b/app/hooks/useAccountUpgrade.tsx index 57daf1412..b655d4e27 100644 --- a/app/hooks/useAccountUpgrade.tsx +++ b/app/hooks/useAccountUpgrade.tsx @@ -25,43 +25,46 @@ export const useAccountUpgrade = () => { ) const { toggleActivityIndicator } = useActivityIndicator() - const fetchAccountUpgrade = async () => { - if (userData.phone) { + const fetchAccountUpgrade = async (phone?: string) => { + if (userData.phone || phone) { toggleActivityIndicator(true) - const res = await fetchUser(userData.phone) - const parsedPhone = parsePhoneNumber(userData.phone) - dispatch( - setAccountUpgrade({ - id: res.id, - accountType: res.account_type, - upgradeCompleted: res.signup_completed, - }), - ) - dispatch( - setPersonalInfo({ - fullName: res.name, - countryCode: parsedPhone.country, - phoneNumber: parsedPhone.nationalNumber, - email: res.email, - }), - ) - dispatch( - setBusinessInfo({ - businessName: res.business_name, - businessAddress: res.business_address, - lat: res.latitude, - lng: res.longitude, - }), - ) - dispatch( - setBankInfo({ - bankName: res.bank_name, - bankBranch: res.bank_branch, - bankAccountType: res.bank_account_type, - currency: res.account_currency, - accountNumber: res.bank_account_number, - }), - ) + const res = await fetchUser(userData.phone || phone) + if (res) { + const parsedPhone = parsePhoneNumber(userData.phone || phone) + dispatch( + setAccountUpgrade({ + id: res.id, + accountType: res.account_type, + upgradeCompleted: res.signup_completed, + }), + ) + dispatch( + setPersonalInfo({ + fullName: res.name, + countryCode: parsedPhone.country, + phoneNumber: parsedPhone.nationalNumber, + email: res.email, + }), + ) + dispatch( + setBusinessInfo({ + businessName: res.business_name, + businessAddress: res.business_address, + lat: res.latitude, + lng: res.longitude, + terminalRequested: res.terminal_requested, + }), + ) + dispatch( + setBankInfo({ + bankName: res.bank_name, + bankBranch: res.bank_branch, + bankAccountType: res.bank_account_type, + currency: res.account_currency, + accountNumber: res.bank_account_number, + }), + ) + } toggleActivityIndicator(false) } } @@ -96,6 +99,7 @@ export const useAccountUpgrade = () => { id_image_url: id_image_url, terms_accepted: true, terminal_requested: businessInfo.terminalRequested, + wants_terminal: businessInfo.terminalRequested, client_version: readableVersion, device_info: Platform.OS, signup_completed: undefined, diff --git a/app/screens/account-upgrade-flow/Validation.tsx b/app/screens/account-upgrade-flow/Validation.tsx index 1174d770a..476e533b4 100644 --- a/app/screens/account-upgrade-flow/Validation.tsx +++ b/app/screens/account-upgrade-flow/Validation.tsx @@ -10,7 +10,7 @@ import { InputField, ProgressSteps } from "@app/components/account-upgrade-flow" // hooks import { useAccountUpgrade, useActivityIndicator, useAppConfig } from "@app/hooks" -import { useUserLoginUpgradeMutation } from "@app/graphql/generated" +import { HomeAuthedDocument, useUserLoginUpgradeMutation } from "@app/graphql/generated" import { useI18nContext } from "@app/i18n/i18n-react" import { useAppSelector } from "@app/store/redux" @@ -26,13 +26,14 @@ const Validation: React.FC = ({ navigation, route }) => { const { LL } = useI18nContext() const { saveToken } = useAppConfig() const { toggleActivityIndicator } = useActivityIndicator() - const { submitAccountUpgrade } = useAccountUpgrade() + const { submitAccountUpgrade, fetchAccountUpgrade } = useAccountUpgrade() const [code, setCode] = useState() const [errorMsg, setErrorMsg] = useState() const [userLoginUpgradeMutation] = useUserLoginUpgradeMutation({ fetchPolicy: "no-cache", + refetchQueries: [HomeAuthedDocument], }) const send = useCallback( @@ -50,6 +51,7 @@ const Validation: React.FC = ({ navigation, route }) => { if (authToken) { saveToken(authToken) } + await fetchAccountUpgrade(phone) if (accountType === "personal") { const res = await submitAccountUpgrade() if (res) navigation.replace("AccountUpgradeSuccess") From 6fdb9373288ab9f7bc811acae23934eb2199d39a Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Mon, 19 May 2025 11:18:05 +0500 Subject: [PATCH 64/81] set phone number and country code in useRequestPhoneCodeLogin hook with prepopulated data in PersonalInformation screen. Disable getting country code from ip if countryCode already exists in useRequestPhoneCodeLogin hook --- app/screens/account-upgrade-flow/PersonalInformation.tsx | 9 ++++++++- app/screens/home-screen/home-screen.tsx | 2 +- .../phone-auth-screen/request-phone-code-login.ts | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/screens/account-upgrade-flow/PersonalInformation.tsx b/app/screens/account-upgrade-flow/PersonalInformation.tsx index 0a34303cc..cf4da0486 100644 --- a/app/screens/account-upgrade-flow/PersonalInformation.tsx +++ b/app/screens/account-upgrade-flow/PersonalInformation.tsx @@ -60,6 +60,13 @@ const PersonalInformation: React.FC = ({ navigation }) => { setCountryCode, } = useRequestPhoneCodeLogin() + useEffect(() => { + if (phoneNumber && countryCode) { + setPhoneNumber(phoneNumber) + setCountryCode(countryCode) + } + }, []) + useEffect(() => { if ( status === RequestPhoneCodeStatus.CompletingCaptcha || @@ -123,7 +130,7 @@ const PersonalInformation: React.FC = ({ navigation }) => { { diff --git a/app/screens/home-screen/home-screen.tsx b/app/screens/home-screen/home-screen.tsx index ce414e30f..fd0643da3 100644 --- a/app/screens/home-screen/home-screen.tsx +++ b/app/screens/home-screen/home-screen.tsx @@ -68,7 +68,7 @@ export const HomeScreen: React.FC = () => { dispatch(setUserData(dataAuthed.me)) saveDefaultWallet() } - }, [dataAuthed]) + }, [dataAuthed?.me]) const saveDefaultWallet = () => { const defaultWallet = getDefaultWallet( diff --git a/app/screens/phone-auth-screen/request-phone-code-login.ts b/app/screens/phone-auth-screen/request-phone-code-login.ts index 8d229779b..40cdb0df9 100644 --- a/app/screens/phone-auth-screen/request-phone-code-login.ts +++ b/app/screens/phone-auth-screen/request-phone-code-login.ts @@ -161,7 +161,7 @@ export const useRequestPhoneCodeLogin = (): UseRequestPhoneCodeReturn => { setStatus(RequestPhoneCodeStatus.InputtingPhoneNumber) } - getCountryCodeFromIP() + if (!countryCode) getCountryCodeFromIP() }, []) const setPhoneNumber = (number: string) => { From 0ec5c5e1eb87a1bf56286d1e275916edf0b1d0e1 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Mon, 19 May 2025 19:22:55 +0500 Subject: [PATCH 65/81] add a logic to delete supabase user data in delete.tsx --- app/screens/settings-screen/account/settings/delete.tsx | 2 ++ app/supabase/index.ts | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/screens/settings-screen/account/settings/delete.tsx b/app/screens/settings-screen/account/settings/delete.tsx index 005600f8d..d88fa2811 100644 --- a/app/screens/settings-screen/account/settings/delete.tsx +++ b/app/screens/settings-screen/account/settings/delete.tsx @@ -20,6 +20,7 @@ import useNostrProfile from "@app/hooks/use-nostr-profile" import { useAccountDeleteContext } from "../account-delete-context" import { useDisplayCurrency } from "@app/hooks/use-display-currency" import { useAccountDeleteMutation, useSettingsScreenQuery } from "@app/graphql/generated" +import { deleteUser } from "@app/supabase" // utils import { CONTACT_EMAIL_ADDRESS } from "@app/config" @@ -104,6 +105,7 @@ export const Delete = () => { if (res.data?.accountDelete?.success) { await deleteNostrData() + if (data?.me?.phone) await deleteUser(data?.me?.phone) await cleanUp(true) setAccountIsBeingDeleted(false) navigation.reset({ diff --git a/app/supabase/index.ts b/app/supabase/index.ts index 7a8b6f21c..fd458fc79 100644 --- a/app/supabase/index.ts +++ b/app/supabase/index.ts @@ -110,17 +110,18 @@ export async function fetchUser(phone: string) { .single() // if expecting only one match if (error) { console.error("Fetch error:", error) + return undefined } else { console.log("User data:", data) return data } } -export async function deleteUser(id: string) { +export async function deleteUser(phone: string) { const { data, error } = await supabase .from("signups") // your table name .delete() - .eq("id", id) // filter to match the row + .eq("phone", phone) // filter to match the row if (error) { console.error("Delete error:", error) From e5290e0aa351cd9f95e6cce02a5595fd57081e7c Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Wed, 18 Jun 2025 11:13:06 +0500 Subject: [PATCH 66/81] adjust numOfSteps for business acount type --- app/screens/account-upgrade-flow/AccountType.tsx | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/app/screens/account-upgrade-flow/AccountType.tsx b/app/screens/account-upgrade-flow/AccountType.tsx index c6e558ac5..7bdb6d10e 100644 --- a/app/screens/account-upgrade-flow/AccountType.tsx +++ b/app/screens/account-upgrade-flow/AccountType.tsx @@ -34,23 +34,17 @@ const AccountType: React.FC = ({ navigation }) => { const onPress = (accountType: string) => { const numOfSteps = - accountType === "personal" - ? 3 - : currentLevel === AccountLevel.Zero - ? accountType === "business" - ? 4 - : 5 - : accountType === "business" - ? 3 - : 4 + accountType === "personal" ? 3 : currentLevel === AccountLevel.Zero ? 5 : 4 dispatch(setAccountUpgrade({ accountType, numOfSteps })) navigation.navigate("PersonalInformation") } + const numOfSteps = currentLevel === AccountLevel.Zero ? 3 : 4 + return ( - + {currentLevel === AccountLevel.Zero && ( onPress("personal")}> From ea37660ce1dcfd9b9687cdae07c888f6c9951751 Mon Sep 17 00:00:00 2001 From: nodirbek75 Date: Wed, 18 Jun 2025 11:15:20 +0500 Subject: [PATCH 67/81] navigate to BankInformation screen even account type is business --- .../BusinessInformation.tsx | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/app/screens/account-upgrade-flow/BusinessInformation.tsx b/app/screens/account-upgrade-flow/BusinessInformation.tsx index d3fcd4a7b..8635193dc 100644 --- a/app/screens/account-upgrade-flow/BusinessInformation.tsx +++ b/app/screens/account-upgrade-flow/BusinessInformation.tsx @@ -15,7 +15,6 @@ import { } from "@app/components/account-upgrade-flow" // hooks -import { useAccountUpgrade } from "@app/hooks" import { useI18nContext } from "@app/i18n/i18n-react" // store @@ -28,19 +27,16 @@ const BusinessInformation: React.FC = ({ navigation }) => { const dispatch = useAppDispatch() const styles = useStyles() const { LL } = useI18nContext() - const { submitAccountUpgrade } = useAccountUpgrade() const [businessNameErr, setBusinessNameErr] = useState() const [businessAddressErr, setBusinessAddressErr] = useState() const { - accountType, numOfSteps, businessInfo: { businessName, businessAddress, terminalRequested }, } = useAppSelector((state) => state.accountUpgrade) const onPressNext = async () => { let hasError = false - if (businessName && businessName.length < 2) { setBusinessNameErr("Business name must be at least 2 characters") hasError = true @@ -50,22 +46,13 @@ const BusinessInformation: React.FC = ({ navigation }) => { hasError = true } if (!hasError) { - if (accountType === "business") { - const res = await submitAccountUpgrade() - if (res) navigation.navigate("AccountUpgradeSuccess") - else alert("Something went wrong. Please, try again later.") - } else { - navigation.navigate("BankInformation") - } + navigation.navigate("BankInformation") } } return ( - + Date: Wed, 18 Jun 2025 11:16:38 +0500 Subject: [PATCH 68/81] make bank informations optional for business account type --- .../account-upgrade-flow/DropDownField.tsx | 9 +++ .../account-upgrade-flow/PhotoUploadField.tsx | 7 +++ .../account-upgrade-flow/BankInformation.tsx | 55 +++++++++++-------- 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/app/components/account-upgrade-flow/DropDownField.tsx b/app/components/account-upgrade-flow/DropDownField.tsx index 8c734389d..d4a58fb04 100644 --- a/app/components/account-upgrade-flow/DropDownField.tsx +++ b/app/components/account-upgrade-flow/DropDownField.tsx @@ -2,6 +2,7 @@ import React from "react" import { View } from "react-native" import { makeStyles, Text, useTheme } from "@rneui/themed" import { Dropdown } from "react-native-element-dropdown" +import { useI18nContext } from "@app/i18n/i18n-react" type Props = { label: string @@ -9,6 +10,7 @@ type Props = { data: any[] value: string errorMsg?: string + isOptional?: boolean onChange: (val: string) => void } @@ -18,15 +20,22 @@ const DropDownField: React.FC = ({ data, value, errorMsg, + isOptional, onChange, }) => { const styles = useStyles() const { colors } = useTheme().theme + const { LL } = useI18nContext() return ( {label} + {isOptional && ( + + {LL.AccountUpgrade.optional()} + + )} void setErrorMsg: (val: string) => void } @@ -38,6 +39,7 @@ const PhotoUploadField: React.FC = ({ label, photo, errorMsg, + isOptional, onPhotoUpload, setErrorMsg, }) => { @@ -131,6 +133,11 @@ const PhotoUploadField: React.FC = ({ {label} + {isOptional && ( + + {LL.AccountUpgrade.optional()} + + )} setIsCameraVisible(true)}> {!!photo && } diff --git a/app/screens/account-upgrade-flow/BankInformation.tsx b/app/screens/account-upgrade-flow/BankInformation.tsx index 33eafb9b9..00bc6a592 100644 --- a/app/screens/account-upgrade-flow/BankInformation.tsx +++ b/app/screens/account-upgrade-flow/BankInformation.tsx @@ -67,30 +67,33 @@ const BankInformation: React.FC = ({ navigation }) => { const onPressNext = async () => { let hasError = false - if (bankName && bankName.length < 2) { - setNameErr("Bank name is required") - hasError = true - } - if (bankBranch && bankBranch.length < 2) { - setBranchErr("Branch is required") - hasError = true - } - if (!accountType) { - setAccountTypeErr("Account type is required") - hasError = true - } - if (!currency) { - setCurrencyErr("Currency is required") - hasError = true - } - if (accountNumber && accountNumber.length < 4) { - setAccountNumErr("Account number is required") - hasError = true - } - if (!idDocument) { - setIdDocumentErr("You must upload an ID document before proceeding") - hasError = true + if (accountType === "merchant") { + if (!bankName || bankName.length < 2) { + setNameErr("Bank name is required") + hasError = true + } + if (!bankBranch || bankBranch.length < 2) { + setBranchErr("Branch is required") + hasError = true + } + if (!bankAccountType) { + setAccountTypeErr("Account type is required") + hasError = true + } + if (!currency) { + setCurrencyErr("Currency is required") + hasError = true + } + if (!accountNumber || accountNumber.length < 4) { + setAccountNumErr("Account number is required") + hasError = true + } + if (!idDocument) { + setIdDocumentErr("You must upload an ID document before proceeding") + hasError = true + } } + if (!hasError) { const res = await submitAccountUpgrade() if (res) navigation.navigate("AccountUpgradeSuccess") @@ -107,6 +110,7 @@ const BankInformation: React.FC = ({ navigation }) => { placeholder={LL.AccountUpgrade.bankNamePlaceholder()} value={bankName} errorMsg={nameErr} + isOptional={accountType === "business"} onChangeText={(val) => { setNameErr(undefined) dispatch(setBankInfo({ bankName: val })) @@ -117,6 +121,7 @@ const BankInformation: React.FC = ({ navigation }) => { placeholder={LL.AccountUpgrade.bankBranchPlaceholder()} value={bankBranch} errorMsg={branchErr} + isOptional={accountType === "business"} onChangeText={(val) => { setBranchErr(undefined) dispatch(setBankInfo({ bankBranch: val })) @@ -128,6 +133,7 @@ const BankInformation: React.FC = ({ navigation }) => { data={accountTypes} value={bankAccountType || ""} errorMsg={accountTypeErr} + isOptional={accountType === "business"} onChange={(val) => { setAccountTypeErr(undefined) dispatch(setBankInfo({ bankAccountType: val })) @@ -139,6 +145,7 @@ const BankInformation: React.FC = ({ navigation }) => { data={currencies} value={currency || ""} errorMsg={currencyErr} + isOptional={accountType === "business"} onChange={(val) => { setCurrencyErr(undefined) dispatch(setBankInfo({ currency: val })) @@ -149,6 +156,7 @@ const BankInformation: React.FC = ({ navigation }) => { placeholder={LL.AccountUpgrade.accountNumPlaceholder()} value={accountNumber} errorMsg={accountNumErr} + isOptional={accountType === "business"} onChangeText={(val) => { setAccountNumErr(undefined) dispatch(setBankInfo({ accountNumber: val })) @@ -158,6 +166,7 @@ const BankInformation: React.FC = ({ navigation }) => { label={LL.AccountUpgrade.uploadId()} photo={idDocument} errorMsg={idDocumentErr} + isOptional={accountType === "business"} onPhotoUpload={(val) => dispatch(setBankInfo({ idDocument: val }))} setErrorMsg={setIdDocumentErr} /> From 53157a05a94096472c1c4991a370195517f388fc Mon Sep 17 00:00:00 2001 From: Dread <34528298+islandbitcoin@users.noreply.github.com> Date: Thu, 16 Oct 2025 19:29:01 -0400 Subject: [PATCH 69/81] conversion from supabase to erpnext --- .../account-upgrade-flow/AddressField.tsx | 157 +++++----- app/components/home-screen/QuickStart.tsx | 5 +- app/graphql/front-end-mutations.ts | 12 + app/graphql/front-end-queries.ts | 20 ++ app/graphql/generated.gql | 35 +++ app/graphql/generated.ts | 293 +++++++++--------- app/hooks/useAccountUpgrade.tsx | 123 ++++---- app/i18n/i18n-types.ts | 167 +++++++++- app/i18n/raw-i18n/source/en.json | 41 +++ .../PersonalInformation.tsx | 14 + app/supabase/index.ts | 133 -------- codegen.yml | 3 +- 12 files changed, 572 insertions(+), 431 deletions(-) delete mode 100644 app/supabase/index.ts diff --git a/app/components/account-upgrade-flow/AddressField.tsx b/app/components/account-upgrade-flow/AddressField.tsx index 8bc789c04..60a3d3436 100644 --- a/app/components/account-upgrade-flow/AddressField.tsx +++ b/app/components/account-upgrade-flow/AddressField.tsx @@ -1,16 +1,11 @@ -import React, { useEffect, useRef, useState } from "react" -import { Modal, Platform, TouchableOpacity, View } from "react-native" +import React, { useState } from "react" +import { ActivityIndicator, TextInput, View } from "react-native" import { makeStyles, Text, useTheme } from "@rneui/themed" -import { - GooglePlacesAutocomplete, - GooglePlacesAutocompleteRef, -} from "react-native-google-places-autocomplete" // components import { PrimaryBtn } from "../buttons" -// env -import { GOOGLE_PLACE_API_KEY } from "@env" +import { useBusinessAddressEnrichLazyQuery } from "@app/graphql/generated" type Props = { label: string @@ -30,71 +25,79 @@ const AddressField: React.FC = ({ const styles = useStyles() const { colors } = useTheme().theme - const ref = useRef(null) - const [isFocused, setIsFocused] = useState(false) - const [isVisible, setIsVisible] = useState(false) + const [inputValue, setInputValue] = useState(value || "") + const [loading, setLoading] = useState(false) + const [enrichError, setEnrichError] = useState() - useEffect(() => { - if (isVisible && ref.current) { - ref.current.focus() + const [enrichAddress] = useBusinessAddressEnrichLazyQuery() + + const handleEnrichAddress = async () => { + if (!inputValue || inputValue.length < 3) { + setEnrichError("Please enter a valid address") + return + } + + setLoading(true) + setEnrichError(undefined) + + try { + const { data } = await enrichAddress({ + variables: { address: inputValue }, + }) + + if (data?.businessAddressEnrich?.errors?.length) { + setEnrichError(data.businessAddressEnrich.errors[0].message) + } else if (data?.businessAddressEnrich?.formattedAddress) { + onAddressSelect( + data.businessAddressEnrich.formattedAddress, + data.businessAddressEnrich.latitude ?? undefined, + data.businessAddressEnrich.longitude ?? undefined, + ) + } + } catch (err) { + console.log("Address enrichment error:", err) + setEnrichError("Failed to validate address. Please try again.") + } finally { + setLoading(false) } - }, [isVisible, ref.current]) + } return ( {label} - setIsVisible(true)}> - - {!!value ? value : placeholder} - - - {!!errorMsg && ( + + { + setInputValue(text) + setEnrichError(undefined) + }} + /> + + {(errorMsg || enrichError) && ( - {errorMsg} + {errorMsg || enrichError} )} - setIsVisible(false)} - > - - console.log("Google places auto complete", err)} - onNotFound={() => console.log("Google places auto complete not found")} - fetchDetails={true} - onPress={(data, details) => { - setIsVisible(false) - onAddressSelect( - data.description, - details?.geometry.location.lat, - details?.geometry.location.lng, - ) - }} - query={{ - key: GOOGLE_PLACE_API_KEY, - language: "en", - }} - styles={{ - textInput: [ - styles.googlePlace, - isFocused ? { borderColor: colors.primary } : {}, - ], - }} - textInputProps={{ - onFocus: () => setIsFocused(true), - onBlur: () => setIsFocused(false), - }} - /> - setIsVisible(false)} /> + {value && ( + + + Selected: {value} + - + )} + + {loading && } ) } @@ -105,32 +108,32 @@ const useStyles = makeStyles(({ colors }) => ({ container: { marginBottom: 15, }, + inputContainer: { + marginTop: 5, + marginBottom: 2, + }, input: { paddingHorizontal: 15, paddingVertical: 20, - marginTop: 5, - marginBottom: 2, borderRadius: 10, borderWidth: 1, borderColor: colors.grey4, backgroundColor: colors.grey5, + fontSize: 16, + fontFamily: "Sora-Regular", + color: colors.black, + width: "100%", }, - modal: { - flex: 1, - backgroundColor: colors.white, - padding: 20, + btn: { + marginTop: 10, }, - googlePlace: { - height: Platform.OS === "ios" ? 51 : 60, - paddingHorizontal: 15, - padding: 20, + loader: { + marginTop: 10, + }, + selectedAddress: { marginTop: 5, - marginBottom: 15, - borderWidth: 1, - borderRadius: 10, - borderColor: colors.grey4, + padding: 8, backgroundColor: colors.grey5, - fontSize: 16, - fontFamily: "Sora-Regular", + borderRadius: 8, }, })) diff --git a/app/components/home-screen/QuickStart.tsx b/app/components/home-screen/QuickStart.tsx index 2155600f7..c8e4828f3 100644 --- a/app/components/home-screen/QuickStart.tsx +++ b/app/components/home-screen/QuickStart.tsx @@ -21,7 +21,7 @@ import { QuickStartAdvancedMode } from "../advanced-mode-modal" import { useI18nContext } from "@app/i18n/i18n-react" import { useNavigation } from "@react-navigation/native" import { usePersistentStateContext } from "@app/store/persistent-state" -import { AccountLevel, useHomeAuthedQuery } from "@app/graphql/generated" +import { AccountLevel, useHomeAuthedQuery, useAccountUpgradeRequestStatusQuery } from "@app/graphql/generated" // utils import { KEYCHAIN_MNEMONIC_KEY } from "@app/utils/breez-sdk-liquid" @@ -62,7 +62,8 @@ const QuickStart = () => { if (credentials) setHasRecoveryPhrase(true) } - const upgradePending = false + const { data: upgradeStatusData } = useAccountUpgradeRequestStatusQuery() + const upgradePending = upgradeStatusData?.accountUpgradeRequestStatus?.hasPendingRequest ?? false let carouselData = [ { diff --git a/app/graphql/front-end-mutations.ts b/app/graphql/front-end-mutations.ts index 9f3cf05ad..a59c6651e 100644 --- a/app/graphql/front-end-mutations.ts +++ b/app/graphql/front-end-mutations.ts @@ -195,3 +195,15 @@ gql` } } ` + +gql` + mutation businessAccountUpgradeRequest($input: BusinessAccountUpgradeRequestInput!) { + businessAccountUpgradeRequest(input: $input) { + errors { + message + code + } + success + } + } +` diff --git a/app/graphql/front-end-queries.ts b/app/graphql/front-end-queries.ts index 822e492fe..e9af3fecb 100644 --- a/app/graphql/front-end-queries.ts +++ b/app/graphql/front-end-queries.ts @@ -288,4 +288,24 @@ gql` username } } + + query businessAddressEnrich($address: String!) { + businessAddressEnrich(address: $address) { + formattedAddress + latitude + longitude + errors { + message + code + } + } + } + + query accountUpgradeRequestStatus { + accountUpgradeRequestStatus { + hasPendingRequest + requestedLevel + errors + } + } ` diff --git a/app/graphql/generated.gql b/app/graphql/generated.gql index 242a55b4a..af4f9752d 100644 --- a/app/graphql/generated.gql +++ b/app/graphql/generated.gql @@ -235,6 +235,18 @@ mutation accountUpdateDisplayCurrency($input: AccountUpdateDisplayCurrencyInput! } } +mutation businessAccountUpgradeRequest($input: BusinessAccountUpgradeRequestInput!) { + businessAccountUpgradeRequest(input: $input) { + errors { + message + code + __typename + } + success + __typename + } +} + mutation captchaCreateChallenge { captchaCreateChallenge { errors { @@ -878,6 +890,15 @@ query accountScreen { } } +query accountUpgradeRequestStatus { + accountUpgradeRequestStatus { + hasPendingRequest + requestedLevel + errors + __typename + } +} + query addressScreen { me { id @@ -947,6 +968,20 @@ query btcPriceList($range: PriceGraphRange!) { } } +query businessAddressEnrich($address: String!) { + businessAddressEnrich(address: $address) { + formattedAddress + latitude + longitude + errors { + message + code + __typename + } + __typename + } +} + query businessMapMarkers { businessMapMarkers { username diff --git a/app/graphql/generated.ts b/app/graphql/generated.ts index 53407af7b..a93b04f02 100644 --- a/app/graphql/generated.ts +++ b/app/graphql/generated.ts @@ -198,6 +198,13 @@ export type AccountUpdateNotificationSettingsPayload = { readonly errors: ReadonlyArray; }; +export type AccountUpgradeRequestStatus = { + readonly __typename: 'AccountUpgradeRequestStatus'; + readonly errors: ReadonlyArray; + readonly hasPendingRequest: Scalars['Boolean']['output']; + readonly requestedLevel?: Maybe; +}; + export type AuthTokenPayload = { readonly __typename: 'AuthTokenPayload'; readonly authToken?: Maybe; @@ -246,6 +253,26 @@ export type BuildInformation = { readonly helmRevision?: Maybe; }; +export type BusinessAccountUpgradeRequestInput = { + readonly additionalInfo?: InputMaybe; + readonly businessAddress: Scalars['String']['input']; + readonly businessName: Scalars['String']['input']; + readonly businessPhone: Scalars['String']['input']; + readonly businessType: Scalars['String']['input']; + readonly level: AccountLevel; +}; + +export type BusinessAddressEnrichPayload = { + readonly __typename: 'BusinessAddressEnrichPayload'; + readonly errors: ReadonlyArray; + /** The standardized/formatted address returned by Google Places API */ + readonly formattedAddress?: Maybe; + /** Geographic latitude coordinate */ + readonly latitude?: Maybe; + /** Geographic longitude coordinate */ + readonly longitude?: Maybe; +}; + export type CallbackEndpoint = { readonly __typename: 'CallbackEndpoint'; readonly id: Scalars['EndpointId']['output']; @@ -736,6 +763,7 @@ export type Mutation = { readonly accountEnableNotificationChannel: AccountUpdateNotificationSettingsPayload; readonly accountUpdateDefaultWalletId: AccountUpdateDefaultWalletIdPayload; readonly accountUpdateDisplayCurrency: AccountUpdateDisplayCurrencyPayload; + readonly businessAccountUpgradeRequest: SuccessPayload; readonly callbackEndpointAdd: CallbackEndpointAddPayload; readonly callbackEndpointDelete: SuccessPayload; readonly captchaCreateChallenge: CaptchaCreateChallengePayload; @@ -887,6 +915,11 @@ export type MutationAccountUpdateDisplayCurrencyArgs = { }; +export type MutationBusinessAccountUpgradeRequestArgs = { + input: BusinessAccountUpgradeRequestInput; +}; + + export type MutationCallbackEndpointAddArgs = { input: CallbackEndpointAddInput; }; @@ -1341,10 +1374,12 @@ export type PublicWallet = { export type Query = { readonly __typename: 'Query'; readonly accountDefaultWallet: PublicWallet; + readonly accountUpgradeRequestStatus: AccountUpgradeRequestStatus; readonly beta: Scalars['Boolean']['output']; /** @deprecated Deprecated in favor of realtimePrice */ readonly btcPrice?: Maybe; readonly btcPriceList?: Maybe>>; + readonly businessAddressEnrich: BusinessAddressEnrichPayload; readonly businessMapMarkers: ReadonlyArray; readonly colorScheme: Scalars['String']['output']; readonly currencyList: ReadonlyArray; @@ -1366,7 +1401,6 @@ export type Query = { readonly quizQuestions?: Maybe>>; /** Returns 1 Sat and 1 Usd Cent price for the given currency */ readonly realtimePrice: RealtimePrice; - readonly transactionDetails: TransactionDetailsPayload; /** @deprecated will be migrated to AccountDefaultWalletId */ readonly userDefaultWalletId: Scalars['WalletId']['output']; readonly usernameAvailable?: Maybe; @@ -1389,6 +1423,11 @@ export type QueryBtcPriceListArgs = { }; +export type QueryBusinessAddressEnrichArgs = { + address: Scalars['String']['input']; +}; + + export type QueryIsFlashNpubArgs = { input: IsFlashNpubInput; }; @@ -1433,11 +1472,6 @@ export type QueryRealtimePriceArgs = { }; -export type QueryTransactionDetailsArgs = { - input: TransactionDetailsInput; -}; - - export type QueryUserDefaultWalletIdArgs = { username: Scalars['Username']['input']; }; @@ -1607,60 +1641,6 @@ export type TransactionConnection = { readonly pageInfo: PageInfo; }; -export type TransactionDetails = { - readonly __typename: 'TransactionDetails'; - /** Account ID associated with the transaction */ - readonly accountId?: Maybe; - /** Bitcoin address for onchain transactions */ - readonly address?: Maybe; - /** Transaction amount */ - readonly amount?: Maybe; - /** Number of confirmations for onchain transactions */ - readonly confirmations?: Maybe; - /** Transaction creation timestamp */ - readonly createdAt?: Maybe; - /** Transaction currency */ - readonly currency?: Maybe; - /** Transaction fee */ - readonly fee?: Maybe; - /** Transaction ID */ - readonly id: Scalars['String']['output']; - /** Lightning invoice (bolt11) */ - readonly invoice?: Maybe; - /** Transaction memo/description */ - readonly memo?: Maybe; - /** Lightning payment hash */ - readonly paymentHash?: Maybe; - /** Lightning payment preimage */ - readonly paymentPreimage?: Maybe; - /** Transaction status */ - readonly status?: Maybe; - /** Bitcoin transaction ID for onchain transactions */ - readonly txid?: Maybe; - /** Transaction type (lightning/onchain) */ - readonly type?: Maybe; - /** Transaction last update timestamp */ - readonly updatedAt?: Maybe; - /** Output index for onchain transactions */ - readonly vout?: Maybe; -}; - -export type TransactionDetailsError = { - readonly __typename: 'TransactionDetailsError'; - readonly message: Scalars['String']['output']; -}; - -export type TransactionDetailsInput = { - /** Transaction ID to fetch details for */ - readonly transactionId: Scalars['String']['input']; -}; - -export type TransactionDetailsPayload = { - readonly __typename: 'TransactionDetailsPayload'; - readonly errors: ReadonlyArray; - readonly transactionDetails?: Maybe; -}; - /** An edge in a connection. */ export type TransactionEdge = { readonly __typename: 'TransactionEdge'; @@ -2178,6 +2158,13 @@ export type UserUpdateNpubMutationVariables = Exact<{ export type UserUpdateNpubMutation = { readonly __typename: 'Mutation', readonly userUpdateNpub: { readonly __typename: 'UserUpdateNpubPayload', readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly code?: string | null }>, readonly user?: { readonly __typename: 'User', readonly id: string, readonly npub?: string | null } | null } }; +export type BusinessAccountUpgradeRequestMutationVariables = Exact<{ + input: BusinessAccountUpgradeRequestInput; +}>; + + +export type BusinessAccountUpgradeRequestMutation = { readonly __typename: 'Mutation', readonly businessAccountUpgradeRequest: { readonly __typename: 'SuccessPayload', readonly success?: boolean | null, readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly message: string, readonly code?: string | null }> } }; + export type AuthQueryVariables = Exact<{ [key: string]: never; }>; @@ -2279,12 +2266,17 @@ export type RealtimePriceUnauthedQueryVariables = Exact<{ export type RealtimePriceUnauthedQuery = { readonly __typename: 'Query', readonly realtimePrice: { readonly __typename: 'RealtimePrice', readonly timestamp: number, readonly denominatorCurrency: string, readonly btcSatPrice: { readonly __typename: 'PriceOfOneSatInMinorUnit', readonly base: number, readonly offset: number }, readonly usdCentPrice: { readonly __typename: 'PriceOfOneUsdCentInMinorUnit', readonly base: number, readonly offset: number } } }; -export type NpubByUsernameQueryVariables = Exact<{ - username: Scalars['Username']['input']; +export type BusinessAddressEnrichQueryVariables = Exact<{ + address: Scalars['String']['input']; }>; -export type NpubByUsernameQuery = { readonly __typename: 'Query', readonly npubByUsername?: { readonly __typename: 'npubByUsername', readonly npub?: string | null, readonly username?: string | null } | null }; +export type BusinessAddressEnrichQuery = { readonly __typename: 'Query', readonly businessAddressEnrich: { readonly __typename: 'BusinessAddressEnrichPayload', readonly formattedAddress?: string | null, readonly latitude?: number | null, readonly longitude?: number | null, readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly message: string, readonly code?: string | null }> } }; + +export type AccountUpgradeRequestStatusQueryVariables = Exact<{ [key: string]: never; }>; + + +export type AccountUpgradeRequestStatusQuery = { readonly __typename: 'Query', readonly accountUpgradeRequestStatus: { readonly __typename: 'AccountUpgradeRequestStatus', readonly hasPendingRequest: boolean, readonly requestedLevel?: AccountLevel | null, readonly errors: ReadonlyArray } }; export type RealtimePriceWsSubscriptionVariables = Exact<{ currency: Scalars['DisplayCurrency']['input']; @@ -2656,13 +2648,6 @@ export type UserTotpRegistrationValidateMutationVariables = Exact<{ export type UserTotpRegistrationValidateMutation = { readonly __typename: 'Mutation', readonly userTotpRegistrationValidate: { readonly __typename: 'UserTotpRegistrationValidatePayload', readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly message: string }>, readonly me?: { readonly __typename: 'User', readonly totpEnabled: boolean, readonly phone?: string | null, readonly email?: { readonly __typename: 'Email', readonly address?: string | null, readonly verified?: boolean | null } | null } | null } }; -export type TransactionDetailsQueryVariables = Exact<{ - input: TransactionDetailsInput; -}>; - - -export type TransactionDetailsQuery = { readonly __typename: 'Query', readonly transactionDetails: { readonly __typename: 'TransactionDetailsPayload', readonly errors: ReadonlyArray<{ readonly __typename: 'TransactionDetailsError', readonly message: string }>, readonly transactionDetails?: { readonly __typename: 'TransactionDetails', readonly id: string, readonly accountId?: string | null, readonly amount?: number | null, readonly currency?: string | null, readonly status?: string | null, readonly type?: string | null, readonly createdAt?: string | null, readonly updatedAt?: string | null, readonly invoice?: string | null, readonly paymentHash?: string | null, readonly paymentPreimage?: string | null, readonly memo?: string | null, readonly address?: string | null, readonly txid?: string | null, readonly vout?: number | null, readonly confirmations?: number | null, readonly fee?: number | null } | null } }; - export type DeviceNotificationTokenCreateMutationVariables = Exact<{ input: DeviceNotificationTokenCreateInput; }>; @@ -3697,6 +3682,43 @@ export function useUserUpdateNpubMutation(baseOptions?: Apollo.MutationHookOptio export type UserUpdateNpubMutationHookResult = ReturnType; export type UserUpdateNpubMutationResult = Apollo.MutationResult; export type UserUpdateNpubMutationOptions = Apollo.BaseMutationOptions; +export const BusinessAccountUpgradeRequestDocument = gql` + mutation businessAccountUpgradeRequest($input: BusinessAccountUpgradeRequestInput!) { + businessAccountUpgradeRequest(input: $input) { + errors { + message + code + } + success + } +} + `; +export type BusinessAccountUpgradeRequestMutationFn = Apollo.MutationFunction; + +/** + * __useBusinessAccountUpgradeRequestMutation__ + * + * To run a mutation, you first call `useBusinessAccountUpgradeRequestMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useBusinessAccountUpgradeRequestMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [businessAccountUpgradeRequestMutation, { data, loading, error }] = useBusinessAccountUpgradeRequestMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useBusinessAccountUpgradeRequestMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(BusinessAccountUpgradeRequestDocument, options); + } +export type BusinessAccountUpgradeRequestMutationHookResult = ReturnType; +export type BusinessAccountUpgradeRequestMutationResult = Apollo.MutationResult; +export type BusinessAccountUpgradeRequestMutationOptions = Apollo.BaseMutationOptions; export const AuthDocument = gql` query auth { me { @@ -4477,42 +4499,83 @@ export function useRealtimePriceUnauthedLazyQuery(baseOptions?: Apollo.LazyQuery export type RealtimePriceUnauthedQueryHookResult = ReturnType; export type RealtimePriceUnauthedLazyQueryHookResult = ReturnType; export type RealtimePriceUnauthedQueryResult = Apollo.QueryResult; -export const NpubByUsernameDocument = gql` - query npubByUsername($username: Username!) { - npubByUsername(username: $username) { - npub - username +export const BusinessAddressEnrichDocument = gql` + query businessAddressEnrich($address: String!) { + businessAddressEnrich(address: $address) { + formattedAddress + latitude + longitude + errors { + message + code + } } } `; /** - * __useNpubByUsernameQuery__ + * __useBusinessAddressEnrichQuery__ * - * To run a query within a React component, call `useNpubByUsernameQuery` and pass it any options that fit your needs. - * When your component renders, `useNpubByUsernameQuery` returns an object from Apollo Client that contains loading, error, and data properties + * To run a query within a React component, call `useBusinessAddressEnrichQuery` and pass it any options that fit your needs. + * When your component renders, `useBusinessAddressEnrichQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example - * const { data, loading, error } = useNpubByUsernameQuery({ + * const { data, loading, error } = useBusinessAddressEnrichQuery({ * variables: { - * username: // value for 'username' + * address: // value for 'address' * }, * }); */ -export function useNpubByUsernameQuery(baseOptions: Apollo.QueryHookOptions) { +export function useBusinessAddressEnrichQuery(baseOptions: Apollo.QueryHookOptions) { const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(NpubByUsernameDocument, options); + return Apollo.useQuery(BusinessAddressEnrichDocument, options); } -export function useNpubByUsernameLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { +export function useBusinessAddressEnrichLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(NpubByUsernameDocument, options); + return Apollo.useLazyQuery(BusinessAddressEnrichDocument, options); } -export type NpubByUsernameQueryHookResult = ReturnType; -export type NpubByUsernameLazyQueryHookResult = ReturnType; -export type NpubByUsernameQueryResult = Apollo.QueryResult; +export type BusinessAddressEnrichQueryHookResult = ReturnType; +export type BusinessAddressEnrichLazyQueryHookResult = ReturnType; +export type BusinessAddressEnrichQueryResult = Apollo.QueryResult; +export const AccountUpgradeRequestStatusDocument = gql` + query accountUpgradeRequestStatus { + accountUpgradeRequestStatus { + hasPendingRequest + requestedLevel + errors + } +} + `; + +/** + * __useAccountUpgradeRequestStatusQuery__ + * + * To run a query within a React component, call `useAccountUpgradeRequestStatusQuery` and pass it any options that fit your needs. + * When your component renders, `useAccountUpgradeRequestStatusQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useAccountUpgradeRequestStatusQuery({ + * variables: { + * }, + * }); + */ +export function useAccountUpgradeRequestStatusQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(AccountUpgradeRequestStatusDocument, options); + } +export function useAccountUpgradeRequestStatusLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(AccountUpgradeRequestStatusDocument, options); + } +export type AccountUpgradeRequestStatusQueryHookResult = ReturnType; +export type AccountUpgradeRequestStatusLazyQueryHookResult = ReturnType; +export type AccountUpgradeRequestStatusQueryResult = Apollo.QueryResult; export const RealtimePriceWsDocument = gql` subscription realtimePriceWs($currency: DisplayCurrency!) { realtimePrice(input: {currency: $currency}) { @@ -6861,62 +6924,6 @@ export function useUserTotpRegistrationValidateMutation(baseOptions?: Apollo.Mut export type UserTotpRegistrationValidateMutationHookResult = ReturnType; export type UserTotpRegistrationValidateMutationResult = Apollo.MutationResult; export type UserTotpRegistrationValidateMutationOptions = Apollo.BaseMutationOptions; -export const TransactionDetailsDocument = gql` - query transactionDetails($input: TransactionDetailsInput!) { - transactionDetails(input: $input) { - errors { - message - } - transactionDetails { - id - accountId - amount - currency - status - type - createdAt - updatedAt - invoice - paymentHash - paymentPreimage - memo - address - txid - vout - confirmations - fee - } - } -} - `; - -/** - * __useTransactionDetailsQuery__ - * - * To run a query within a React component, call `useTransactionDetailsQuery` and pass it any options that fit your needs. - * When your component renders, `useTransactionDetailsQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useTransactionDetailsQuery({ - * variables: { - * input: // value for 'input' - * }, - * }); - */ -export function useTransactionDetailsQuery(baseOptions: Apollo.QueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(TransactionDetailsDocument, options); - } -export function useTransactionDetailsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(TransactionDetailsDocument, options); - } -export type TransactionDetailsQueryHookResult = ReturnType; -export type TransactionDetailsLazyQueryHookResult = ReturnType; -export type TransactionDetailsQueryResult = Apollo.QueryResult; export const DeviceNotificationTokenCreateDocument = gql` mutation deviceNotificationTokenCreate($input: DeviceNotificationTokenCreateInput!) { deviceNotificationTokenCreate(input: $input) { diff --git a/app/hooks/useAccountUpgrade.tsx b/app/hooks/useAccountUpgrade.tsx index b655d4e27..85ec6a341 100644 --- a/app/hooks/useAccountUpgrade.tsx +++ b/app/hooks/useAccountUpgrade.tsx @@ -1,6 +1,7 @@ import { Platform } from "react-native" import DeviceInfo from "react-native-device-info" import { parsePhoneNumber } from "libphonenumber-js" +import RNFS from "react-native-fs" // hooks import { useActivityIndicator } from "./useActivityIndicator" @@ -14,8 +15,7 @@ import { setPersonalInfo, } from "@app/store/redux/slices/accountUpgradeSlice" -// supabase -import { fetchUser, insertUser, updateUser, uploadFile } from "@app/supabase" +import { useBusinessAccountUpgradeRequestMutation, HomeAuthedDocument } from "@app/graphql/generated" export const useAccountUpgrade = () => { const dispatch = useAppDispatch() @@ -25,48 +25,15 @@ export const useAccountUpgrade = () => { ) const { toggleActivityIndicator } = useActivityIndicator() + const [businessAccountUpgradeRequestMutation] = useBusinessAccountUpgradeRequestMutation({ + refetchQueries: [HomeAuthedDocument], + }) + const fetchAccountUpgrade = async (phone?: string) => { - if (userData.phone || phone) { - toggleActivityIndicator(true) - const res = await fetchUser(userData.phone || phone) - if (res) { - const parsedPhone = parsePhoneNumber(userData.phone || phone) - dispatch( - setAccountUpgrade({ - id: res.id, - accountType: res.account_type, - upgradeCompleted: res.signup_completed, - }), - ) - dispatch( - setPersonalInfo({ - fullName: res.name, - countryCode: parsedPhone.country, - phoneNumber: parsedPhone.nationalNumber, - email: res.email, - }), - ) - dispatch( - setBusinessInfo({ - businessName: res.business_name, - businessAddress: res.business_address, - lat: res.latitude, - lng: res.longitude, - terminalRequested: res.terminal_requested, - }), - ) - dispatch( - setBankInfo({ - bankName: res.bank_name, - bankBranch: res.bank_branch, - bankAccountType: res.bank_account_type, - currency: res.account_currency, - accountNumber: res.bank_account_number, - }), - ) - } - toggleActivityIndicator(false) - } + // Note: This function previously fetched from Supabase + // Now we rely on the GraphQL backend data from queries + // This function may no longer be needed, but keeping for compatibility + console.log("fetchAccountUpgrade called - functionality moved to GraphQL queries") } const submitAccountUpgrade = async () => { @@ -77,42 +44,58 @@ export const useAccountUpgrade = () => { personalInfo.countryCode, ) - let id_image_url = undefined - if (accountType === "merchant") { - id_image_url = await uploadFile(bankInfo.idDocument) + let idDocumentBase64 = undefined + if (accountType === "merchant" && bankInfo.idDocument) { + try { + // Convert image file to base64 + const base64String = await RNFS.readFile( + bankInfo.idDocument.uri, + "base64", + ) + idDocumentBase64 = `data:${bankInfo.idDocument.type};base64,${base64String}` + } catch (err) { + console.log("Error converting ID document to base64:", err) + } } - const data = { - account_type: accountType, - name: personalInfo.fullName, + const input = { + accountType: accountType, + fullName: personalInfo.fullName, phone: parsedPhoneNumber.number, email: personalInfo.email, - business_name: businessInfo.businessName, - business_address: businessInfo.businessAddress, + businessName: businessInfo.businessName, + businessAddress: businessInfo.businessAddress, latitude: businessInfo.lat, longitude: businessInfo.lng, - bank_name: bankInfo.bankName, - bank_branch: bankInfo.bankBranch, - bank_account_type: bankInfo.bankAccountType, - account_currency: bankInfo.currency, - bank_account_number: bankInfo.accountNumber, - id_image_url: id_image_url, - terms_accepted: true, - terminal_requested: businessInfo.terminalRequested, - wants_terminal: businessInfo.terminalRequested, - client_version: readableVersion, - device_info: Platform.OS, - signup_completed: undefined, + terminalRequested: businessInfo.terminalRequested, + bankName: bankInfo.bankName, + bankBranch: bankInfo.bankBranch, + bankAccountType: bankInfo.bankAccountType, + accountCurrency: bankInfo.currency, + bankAccountNumber: bankInfo.accountNumber, + idDocumentBase64: idDocumentBase64, + clientVersion: readableVersion, + deviceInfo: Platform.OS, } - let res = null - if (!id) { - res = await insertUser(data) - } else { - res = await updateUser(id, data) + try { + const { data } = await businessAccountUpgradeRequestMutation({ + variables: { input }, + }) + + toggleActivityIndicator(false) + + if (data?.businessAccountUpgradeRequest?.errors?.length) { + console.error("Upgrade request errors:", data.businessAccountUpgradeRequest.errors) + return false + } + + return data?.businessAccountUpgradeRequest?.success ?? false + } catch (err) { + console.log("BUSINESS ACCOUNT UPGRADE REQUEST ERR: ", err) + toggleActivityIndicator(false) + return false } - toggleActivityIndicator(false) - return res } return { fetchAccountUpgrade, submitAccountUpgrade } diff --git a/app/i18n/i18n-types.ts b/app/i18n/i18n-types.ts index 1e93d8df8..a765ff107 100644 --- a/app/i18n/i18n-types.ts +++ b/app/i18n/i18n-types.ts @@ -1721,11 +1721,11 @@ type RootTranslation = { */ addFlashcard: string /** - * Upgrade your account + * U​p​g​r​a​d​e​ ​y​o​u​r​ ​a​c​c​o​u​n​t */ upgradeTitle: string /** - * Unlock your full potential! Upgrade to boost visibility, limits, and payouts. + * U​n​l​o​c​k​ ​y​o​u​r​ ​f​u​l​l​ ​p​o​t​e​n​t​i​a​l​!​ ​U​p​g​r​a​d​e​ ​t​o​ ​b​o​o​s​t​ ​v​i​s​i​b​i​l​i​t​y​,​ ​l​i​m​i​t​s​,​ ​a​n​d​ ​p​a​y​o​u​t​s​. */ upgradeDesc: string /** @@ -4597,6 +4597,7 @@ type RootTranslation = { * T​r​a​n​s​a​c​t​i​o​n​ ​I​D */ txId: string +<<<<<<< HEAD }, AccountUpgrade: { /** @@ -4959,6 +4960,162 @@ type RootTranslation = { goToSettings: string } } + AccountUpgrade: { + /** + * A​c​c​o​u​n​t​ ​T​y​p​e + */ + accountType: string + /** + * P​e​r​s​o​n​a​l + */ + personal: string + /** + * S​e​c​u​r​e​ ​y​o​u​r​ ​w​a​l​l​e​t​ ​w​i​t​h​ ​p​h​o​n​e​ ​a​n​d​ ​e​m​a​i​l​.​ ​S​t​a​y​ ​s​a​f​e​ ​a​n​d​ ​r​e​c​o​v​e​r​ ​e​a​s​i​l​y​ ​i​f​ ​n​e​e​d​e​d + */ + personalDesc: string + /** + * P​r​o + */ + pro: string + /** + * A​c​c​e​p​t​ ​p​a​y​m​e​n​t​s​ ​a​n​d​ ​g​e​t​ ​d​i​s​c​o​v​e​r​e​d​ ​o​n​ ​t​h​e​ ​m​a​p​.​ ​R​e​q​u​i​r​e​s​ ​a​ ​b​u​s​i​n​e​s​s​ ​n​a​m​e​ ​a​n​d​ ​l​o​c​a​t​i​o​n​. + */ + proDesc: string + /** + * M​e​r​c​h​a​n​t + */ + merchant: string + /** + * G​i​v​e​ ​r​e​w​a​r​d​s​,​ ​a​p​p​e​a​r​ ​o​n​ ​t​h​e​ ​m​a​p​,​ ​a​n​d​ ​s​e​t​t​l​e​ ​t​o​ ​y​o​u​r​ ​b​a​n​k​.​ ​I​D​ ​a​n​d​ ​b​a​n​k​ ​i​n​f​o​ ​r​e​q​u​i​r​e​d​. + */ + merchantDesc: string + /** + * P​e​r​s​o​n​a​l​ ​I​n​f​o​r​m​a​t​i​o​n + */ + personalInfo: string + /** + * F​u​l​l​ ​n​a​m​e + */ + fullName: string + /** + * P​h​o​n​e​ ​N​u​m​b​e​r + */ + phoneNumber: string + /** + * E​m​a​i​l​ ​A​d​d​r​e​s​s + */ + email: string + /** + * ​(​O​p​t​i​o​n​a​l​) + */ + optional: string + /** + * V​a​l​i​d​a​t​i​o​n + */ + validation: string + /** + * V​a​l​i​d​a​t​i​o​n​ ​c​o​d​e + */ + validationCode: string + /** + * B​u​s​i​n​e​s​s​ ​I​n​f​o​r​m​a​t​i​o​n + */ + businessInfo: string + /** + * B​u​s​i​n​e​s​s​ ​N​a​m​e + */ + businessName: string + /** + * E​n​t​e​r​ ​y​o​u​r​ ​b​u​s​i​n​e​s​s​ ​n​a​m​e + */ + businessNamePlaceholder: string + /** + * B​u​s​i​n​e​s​s​ ​A​d​d​r​e​s​s + */ + businessAddress: string + /** + * E​n​t​e​r​ ​y​o​u​r​ ​b​u​s​i​n​e​s​s​ ​a​d​d​r​e​s​s + */ + businessAddressPlaceholder: string + /** + * D​o​ ​y​o​u​ ​w​a​n​t​ ​a​ ​F​l​a​s​h​ ​t​e​r​m​i​n​a​l​? + */ + flashTerminal: string + /** + * A​ ​F​l​a​s​h​ ​T​e​r​m​i​n​a​l​ ​i​s​ ​a​ ​s​m​a​r​t​ ​d​e​v​i​c​e​ ​t​h​a​t​ ​c​a​n​ ​a​c​c​e​p​t​ ​p​a​y​m​e​n​t​ ​v​i​a​ ​F​l​a​s​h​ ​f​o​r​ ​y​o​u​r​ ​b​u​s​i​n​e​s​s​ ​a​n​d​ ​p​r​i​n​t​ ​r​e​c​e​i​p​t​s​.​ ​A​ ​c​u​s​t​o​m​e​r​ ​s​e​r​v​i​c​e​ ​r​e​p​r​e​s​e​n​t​a​t​i​v​e​ ​w​i​l​l​ ​c​o​n​t​a​c​t​ ​y​o​u​ ​i​f​ ​y​o​u​ ​c​h​e​c​k​ ​t​h​i​s​ ​b​o​x​. + */ + flashTerminalTooltip: string + /** + * B​a​n​k​i​n​g​ ​I​n​f​o​r​m​a​t​i​o​n + */ + bankingInfo: string + /** + * B​a​n​k​ ​N​a​m​e + */ + bankName: string + /** + * E​n​t​e​r​ ​y​o​u​r​ ​b​a​n​k​ ​n​a​m​e + */ + bankNamePlaceholder: string + /** + * B​a​n​k​ ​B​r​a​n​c​h + */ + bankBranch: string + /** + * E​n​t​e​r​ ​y​o​u​r​ ​b​a​n​k​ ​b​r​a​n​c​h + */ + bankBranchPlaceholder: string + /** + * A​c​c​o​u​n​t​ ​T​y​p​e + */ + bankAccountType: string + /** + * S​e​l​e​c​t​ ​a​c​c​o​u​n​t​ ​t​y​p​e + */ + selectBankAccountType: string + /** + * C​u​r​r​e​n​c​y + */ + currency: string + /** + * S​e​l​e​c​t​ ​C​u​r​r​e​n​c​y + */ + selectCurrency: string + /** + * A​c​c​o​u​n​t​ ​N​u​m​b​e​r + */ + accountNum: string + /** + * E​n​t​e​r​ ​y​o​u​r​ ​a​c​c​o​u​n​t​ ​n​u​m​b​e​r + */ + accountNumPlaceholder: string + /** + * U​p​l​o​a​d​ ​I​D​ ​D​o​c​u​m​e​n​t + */ + uploadId: string + /** + * Y​o​u​ ​s​u​c​c​e​s​s​f​u​l​l​y​ ​r​e​q​u​e​s​t​e​d​ ​t​o​ ​u​p​g​r​a​d​e​ ​y​o​u​r​ ​a​c​c​o​u​n​t​ ​t​o​ ​{​a​c​c​o​u​n​t​T​y​p​e​} + * @param {string} accountType + */ + successTitle: RequiredParams<'accountType'> + /** + * *​ ​P​l​e​a​s​e​ ​e​n​t​e​r​ ​t​h​e​ ​t​e​s​t​ ​t​r​a​n​s​a​c​t​i​o​n​ ​a​m​o​u​n​t​ ​t​o​ ​c​o​n​f​i​r​m​ ​y​o​u​r​ ​b​a​n​k​ ​d​e​t​a​i​l​s​. + */ + successDesc: string + /** + * T​r​a​n​s​a​c​t​i​o​n​ ​V​e​r​i​f​i​c​a​t​i​o​n + */ + transactionVerification: string + /** + * T​o​ ​c​o​m​p​l​e​t​e​ ​u​p​g​r​a​d​i​n​g​ ​y​o​u​r​ ​a​c​c​o​u​n​t​ ​t​o​ ​M​E​R​C​H​A​N​T​,​ ​e​n​t​e​r​ ​t​h​e​ ​t​e​s​t​ ​t​r​a​n​s​a​c​t​i​o​n​ ​a​m​o​u​n​t​ ​w​e​ ​s​e​n​t​ ​t​o​ ​y​o​u​r​ ​b​a​n​k​ ​a​c​c​o​u​n​t​ ​{​a​c​c​o​u​n​t​N​u​m​}​ ​t​o​ ​c​o​n​f​i​r​m​ ​y​o​u​r​ ​b​a​n​k​ ​d​e​t​a​i​l​s​. + * @param {string} accountNum + */ + transactionTitle: RequiredParams<'accountNum'> + /** + * T​r​a​n​s​a​c​t​i​o​n​ ​a​m​o​u​n​t + */ + transactionAmount: string + } } export type TranslationFunctions = { @@ -9690,7 +9847,7 @@ export type TranslationFunctions = { */ email: () => LocalizedString /** - * Optional + * (Optional) */ optional: () => LocalizedString /** @@ -9762,7 +9919,7 @@ export type TranslationFunctions = { */ currency: () => LocalizedString /** - * Select currency + * Select Currency */ selectCurrency: () => LocalizedString /** @@ -9786,7 +9943,7 @@ export type TranslationFunctions = { */ successDesc: () => LocalizedString /** - * * Transaction Verification + * Transaction Verification */ transactionVerification: () => LocalizedString /** diff --git a/app/i18n/raw-i18n/source/en.json b/app/i18n/raw-i18n/source/en.json index c12502262..60cde2df0 100644 --- a/app/i18n/raw-i18n/source/en.json +++ b/app/i18n/raw-i18n/source/en.json @@ -521,6 +521,7 @@ "addFlashcard": "Add Flashcard", "upgradeTitle": "Upgrade your account", "upgradeDesc": "Unlock your full potential! Upgrade to boost visibility, limits, and payouts.", + "upgradePendingDesc": "Enter test transaction amount to complete upgrading your account.", "currencyTitle": "Change to your local currency", "currencyDesc": "Review our available currency list and select your currency.", "flashcardTitle": "Get a Flashcard", @@ -1369,5 +1370,45 @@ "copied": "Copied", "goToSettings": "Go to settings" } + }, + "AccountUpgrade": { + "accountType": "Account Type", + "personal": "Personal", + "personalDesc": "Secure your wallet with phone and email. Stay safe and recover easily if needed", + "pro": "Pro", + "proDesc": "Accept payments and get discovered on the map. Requires a business name and location.", + "merchant": "Merchant", + "merchantDesc": "Give rewards, appear on the map, and settle to your bank. ID and bank info required.", + "personalInfo": "Personal Information", + "fullName": "Full name", + "phoneNumber": "Phone Number", + "email": "Email Address", + "optional": " (Optional)", + "validation": "Validation", + "validationCode": "Validation code", + "businessInfo": "Business Information", + "businessName": "Business Name", + "businessNamePlaceholder": "Enter your business name", + "businessAddress": "Business Address", + "businessAddressPlaceholder": "Enter your business address", + "flashTerminal": "Do you want a Flash terminal?", + "flashTerminalTooltip": "A Flash Terminal is a smart device that can accept payment via Flash for your business and print receipts. A customer service representative will contact you if you check this box.", + "bankingInfo": "Banking Information", + "bankName": "Bank Name", + "bankNamePlaceholder": "Enter your bank name", + "bankBranch": "Bank Branch", + "bankBranchPlaceholder": "Enter your bank branch", + "bankAccountType": "Account Type", + "selectBankAccountType": "Select account type", + "currency": "Currency", + "selectCurrency": "Select Currency", + "accountNum": "Account Number", + "accountNumPlaceholder": "Enter your account number", + "uploadId": "Upload ID Document", + "successTitle": "You successfully requested to upgrade your account to {accountType: string}", + "successDesc": "* Please enter the test transaction amount to confirm your bank details.", + "transactionVerification": "Transaction Verification", + "transactionTitle": "To complete upgrading your account to MERCHANT, enter the test transaction amount we sent to your bank account {accountNum: string} to confirm your bank details.", + "transactionAmount": "Transaction amount" } } diff --git a/app/screens/account-upgrade-flow/PersonalInformation.tsx b/app/screens/account-upgrade-flow/PersonalInformation.tsx index cf4da0486..c13fc6cd2 100644 --- a/app/screens/account-upgrade-flow/PersonalInformation.tsx +++ b/app/screens/account-upgrade-flow/PersonalInformation.tsx @@ -28,6 +28,7 @@ import { import { useLevel } from "@app/graphql/level-context" import { useI18nContext } from "@app/i18n/i18n-react" import { useActivityIndicator } from "@app/hooks" +import { useUserEmailRegistrationInitiateMutation } from "@app/graphql/generated" type Props = StackScreenProps @@ -60,6 +61,8 @@ const PersonalInformation: React.FC = ({ navigation }) => { setCountryCode, } = useRequestPhoneCodeLogin() + const [userEmailRegistrationInitiateMutation] = useUserEmailRegistrationInitiateMutation() + useEffect(() => { if (phoneNumber && countryCode) { setPhoneNumber(phoneNumber) @@ -103,6 +106,17 @@ const PersonalInformation: React.FC = ({ navigation }) => { if (currentLevel === AccountLevel.Zero && channel) { submitPhoneNumber(channel) } else { + // For Level 1+ users upgrading to business, save email if provided + if (email && email.length > 0) { + try { + await userEmailRegistrationInitiateMutation({ + variables: { input: { email } }, + }) + } catch (err) { + console.log("Email registration error:", err) + // Continue to next screen even if email fails + } + } navigation.navigate("BusinessInformation") } } diff --git a/app/supabase/index.ts b/app/supabase/index.ts deleted file mode 100644 index fd458fc79..000000000 --- a/app/supabase/index.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { createClient } from "@supabase/supabase-js" - -// env -import { SUPABASE_URL, SUPABASE_KEY } from "@env" - -// Create Supabase client -const supabase = createClient(SUPABASE_URL, SUPABASE_KEY) - -type User = { - account_type?: string - name?: string - phone?: string - email?: string - business_name?: string - business_address?: string - latitude?: number - longitude?: number - bank_name?: string - bank_branch?: string - bank_account_type?: string - account_currency?: string - bank_account_number?: string - id_image_url?: string - terms_accepted?: boolean - terminal_requested?: boolean - client_version?: string - device_info?: string - signup_completed?: boolean -} - -// Upload file using standard upload -export async function uploadFile(file: any) { - try { - // Create a cryptographically secure unique file name using UUID v4 pattern - const fileExt = file.fileName.split(".").pop() - // Generate a UUID-like string with timestamp for better security - const timestamp = Date.now().toString(36) - const random1 = crypto - .getRandomValues(new Uint8Array(8)) - .reduce((acc, val) => acc + val.toString(16).padStart(2, "0"), "") - const random2 = crypto - .getRandomValues(new Uint8Array(4)) - .reduce((acc, val) => acc + val.toString(16).padStart(2, "0"), "") - const random3 = crypto - .getRandomValues(new Uint8Array(4)) - .reduce((acc, val) => acc + val.toString(16).padStart(2, "0"), "") - const random4 = crypto - .getRandomValues(new Uint8Array(12)) - .reduce((acc, val) => acc + val.toString(16).padStart(2, "0"), "") - const secureFileName = `${random1}-${random2}-${random3}-${random4}-${timestamp}` - const fileName = `${secureFileName}.${fileExt}` - const filePath = `${fileName}` - - const { data, error } = await supabase.storage - .from("id_uploads") - .upload(filePath, file) - if (error) { - // Handle error - console.log("UPLOAD ERROR>>>>>>>", error) - return undefined - } else { - // Handle success - console.log("UPLOAD SUCCESS>>>>>>>", data) - const res = supabase.storage.from("id_uploads").getPublicUrl(data.path) - return res.data.publicUrl - } - return - } catch (err) { - console.log("Upload File Err", err) - } -} - -export async function insertUser(userData: User) { - try { - const { data, error } = await supabase - .from("signups") - .insert([{ ...userData, terms_accepted: true }]) - if (error) { - console.error("Insert error:", error) - return false - } else { - console.log("Insert success:", data) - return true - } - } catch (err) { - console.log("SUPABASE INSERT USER CATCH ERR: ", err) - } -} - -export async function updateUser(id: string, userData: User) { - try { - const { data, error } = await supabase.from("signups").update(userData).eq("id", id) - if (error) { - console.error("Update error:", error) - return false - } else { - console.log("Updated user:", data) - return true - } - } catch (err) { - console.log("SUPABASE UPDATE USER CATCH ERR: ", err) - } -} - -export async function fetchUser(phone: string) { - const { data, error } = await supabase - .from("signups") // your table name - .select("*") // or specify fields: 'id, name, email' - .eq("phone", phone) // phone number to match - .single() // if expecting only one match - if (error) { - console.error("Fetch error:", error) - return undefined - } else { - console.log("User data:", data) - return data - } -} - -export async function deleteUser(phone: string) { - const { data, error } = await supabase - .from("signups") // your table name - .delete() - .eq("phone", phone) // filter to match the row - - if (error) { - console.error("Delete error:", error) - return false - } else { - console.log("Deleted row:", data) - return true - } -} diff --git a/codegen.yml b/codegen.yml index cb6f47e40..4117322e6 100644 --- a/codegen.yml +++ b/codegen.yml @@ -1,5 +1,6 @@ overwrite: true -schema: "https://api.flashapp.me/graphql" +# schema: "https://api.flashapp.me/graphql" +schema: "http://localhost:4002/graphql" # schema: "https://raw.githubusercontent.com/lnflash/flash/feature/merchant-map-suggest/src/graphql/public/schema.graphql" documents: - "app/**/*.ts" From 912b0048d44a7d3dd8b9451fa795550bfa02ba0b Mon Sep 17 00:00:00 2001 From: Nodirbek Jamoldinov Date: Wed, 7 Jan 2026 11:39:55 +0500 Subject: [PATCH 70/81] remove unused packages --- ios/LNFlash.xcodeproj/project.pbxproj | 8 - ios/Podfile.lock | 16 - package.json | 5 +- yarn.lock | 683 +------------------------- 4 files changed, 15 insertions(+), 697 deletions(-) diff --git a/ios/LNFlash.xcodeproj/project.pbxproj b/ios/LNFlash.xcodeproj/project.pbxproj index 3f28dc150..81599fedc 100644 --- a/ios/LNFlash.xcodeproj/project.pbxproj +++ b/ios/LNFlash.xcodeproj/project.pbxproj @@ -381,7 +381,6 @@ "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo/RNDeviceInfoPrivacyInfo.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf", @@ -402,7 +401,6 @@ "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/react-native-image-picker/RNImagePickerPrivacyInfo.bundle", ); @@ -423,7 +421,6 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Promises_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNDeviceInfoPrivacyInfo.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf", @@ -444,7 +441,6 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nanopb_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNImagePickerPrivacyInfo.bundle", ); @@ -564,7 +560,6 @@ "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo/RNDeviceInfoPrivacyInfo.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf", @@ -585,7 +580,6 @@ "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/react-native-image-picker/RNImagePickerPrivacyInfo.bundle", ); @@ -606,7 +600,6 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Promises_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNDeviceInfoPrivacyInfo.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf", @@ -627,7 +620,6 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nanopb_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNImagePickerPrivacyInfo.bundle", ); diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 9f3208bb3..3696345e3 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -681,15 +681,6 @@ PODS: - React-Core - RNGestureHandler (2.12.1): - React-Core - - RNImageCropPicker (0.39.0): - - React-Core - - React-RCTImage - - RNImageCropPicker/QBImagePickerController (= 0.39.0) - - TOCropViewController - - RNImageCropPicker/QBImagePickerController (0.39.0): - - React-Core - - React-RCTImage - - TOCropViewController - RNInAppBrowser (3.7.0): - React-Core - RNKeychain (8.2.0): @@ -754,7 +745,6 @@ PODS: - RCT-Folly (= 2021.07.22.00) - React-Core - SocketRocket (0.6.1) - - TOCropViewController (3.1.1) - VisionCamera (3.6.4): - React - React-callinvoker @@ -845,7 +835,6 @@ DEPENDENCIES: - RNFileViewer (from `../node_modules/react-native-file-viewer`) - RNFS (from `../node_modules/react-native-fs`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - - RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`) - RNInAppBrowser (from `../node_modules/react-native-inappbrowser-reborn`) - RNKeychain (from `../node_modules/react-native-keychain`) - RNLocalize (from `../node_modules/react-native-localize`) @@ -895,7 +884,6 @@ SPEC REPOS: - PromisesObjC - PromisesSwift - SocketRocket - - TOCropViewController - ZXingObjC EXTERNAL SOURCES: @@ -1053,8 +1041,6 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-fs" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" - RNImageCropPicker: - :path: "../node_modules/react-native-image-crop-picker" RNInAppBrowser: :path: "../node_modules/react-native-inappbrowser-reborn" RNKeychain: @@ -1195,7 +1181,6 @@ SPEC CHECKSUMS: RNFileViewer: ce7ca3ac370e18554d35d6355cffd7c30437c592 RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 RNGestureHandler: c0d04458598fcb26052494ae23dda8f8f5162b13 - RNImageCropPicker: 14fe1c29298fb4018f3186f455c475ab107da332 RNInAppBrowser: e36d6935517101ccba0e875bac8ad7b0cb655364 RNKeychain: bfe3d12bf4620fe488771c414530bf16e88f3678 RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81 @@ -1211,7 +1196,6 @@ SPEC CHECKSUMS: RNSVG: 963a95f1f5d512a13d11ffd50d351c87fb5c6890 RNVectorIcons: 5fc79a64e2ec2468ef4d1f9d40c132e52ef37241 SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 - TOCropViewController: 9002a9b12d8104d7478cdc306d80f0efea7fe2c5 VisionCamera: 1910a51e4c6f6b049650086d343090f267b4c260 Yoga: eddf2bbe4a896454c248a8f23b4355891eb720a6 ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 diff --git a/package.json b/package.json index 880304008..c21c15361 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,6 @@ "@sinonjs/text-encoding": "^0.7.2", "@snort/system": "^1.1.8", "@stablelib/xchacha20": "^1.0.1", - "@supabase/supabase-js": "^2.49.4", "apollo3-cache-persist": "^0.14.1", "axios": "^1.6.2", "bech32": "^2.0.0", @@ -149,7 +148,6 @@ "react-native-gradle-plugin": "^0.71.19", "react-native-haptic-feedback": "^2.0.3", "react-native-html-to-pdf": "^0.12.0", - "react-native-image-crop-picker": "^0.39.0", "react-native-image-picker": "^7.0.0", "react-native-in-app-review": "^4.3.3", "react-native-inappbrowser-reborn": "^3.7.0", @@ -289,7 +287,7 @@ "jetifier": "^2.0.0", "jimp": "^0.22.8", "jsqr": "^1.4.0", - "metro-config": "0.76.1", + "metro-config": "0.76.8", "metro-react-native-babel-preset": "0.76.8", "mocha": "^10.2.0", "npm-run-all": "4.1.5", @@ -325,6 +323,7 @@ } }, "resolutions": { + "metro": "0.76.8", "types-ramda": "0.29.4", "pbkdf2": "^3.1.3", "elliptic": "^6.6.1", diff --git a/yarn.lock b/yarn.lock index 41ff1559a..8d6306cd0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -102,7 +102,7 @@ "@smithy/types" "^4.9.0" tslib "^2.6.2" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.24.7", "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== @@ -189,7 +189,7 @@ eslint-visitor-keys "^2.1.0" semver "^6.3.1" -"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.20.0", "@babel/generator@^7.21.5", "@babel/generator@^7.25.0", "@babel/generator@^7.26.10", "@babel/generator@^7.28.5", "@babel/generator@^7.7.2": +"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.20.0", "@babel/generator@^7.21.5", "@babel/generator@^7.26.10", "@babel/generator@^7.28.5", "@babel/generator@^7.7.2": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ== @@ -457,7 +457,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.0": +"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== @@ -501,7 +501,7 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.12.7", "@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.20.0": +"@babel/plugin-proposal-optional-chaining@^7.12.7", "@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.20.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== @@ -568,7 +568,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-syntax-dynamic-import@^7.0.0", "@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": +"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== @@ -718,7 +718,7 @@ "@babel/helper-remap-async-to-generator" "^7.27.1" "@babel/traverse" "^7.28.0" -"@babel/plugin-transform-async-to-generator@^7.0.0", "@babel/plugin-transform-async-to-generator@^7.20.0", "@babel/plugin-transform-async-to-generator@^7.27.1": +"@babel/plugin-transform-async-to-generator@^7.20.0", "@babel/plugin-transform-async-to-generator@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz#9a93893b9379b39466c74474f55af03de78c66e7" integrity sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA== @@ -1317,7 +1317,7 @@ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== -"@babel/template@^7.0.0", "@babel/template@^7.12.7", "@babel/template@^7.14.5", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.25.0", "@babel/template@^7.27.1", "@babel/template@^7.27.2", "@babel/template@^7.3.3": +"@babel/template@^7.0.0", "@babel/template@^7.12.7", "@babel/template@^7.14.5", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.27.1", "@babel/template@^7.27.2", "@babel/template@^7.3.3": version "7.27.2" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== @@ -6397,63 +6397,6 @@ regenerator-runtime "^0.13.7" resolve-from "^5.0.0" -"@supabase/auth-js@2.69.1": - version "2.69.1" - resolved "https://registry.yarnpkg.com/@supabase/auth-js/-/auth-js-2.69.1.tgz#fcf310d24dfab823ffbf22191e6ceaef933360d8" - integrity sha512-FILtt5WjCNzmReeRLq5wRs3iShwmnWgBvxHfqapC/VoljJl+W8hDAyFmf1NVw3zH+ZjZ05AKxiKxVeb0HNWRMQ== - dependencies: - "@supabase/node-fetch" "^2.6.14" - -"@supabase/functions-js@2.4.4": - version "2.4.4" - resolved "https://registry.yarnpkg.com/@supabase/functions-js/-/functions-js-2.4.4.tgz#45fcd94d546bdfa66d01f93a796ca0304ec154b8" - integrity sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA== - dependencies: - "@supabase/node-fetch" "^2.6.14" - -"@supabase/node-fetch@2.6.15", "@supabase/node-fetch@^2.6.14": - version "2.6.15" - resolved "https://registry.yarnpkg.com/@supabase/node-fetch/-/node-fetch-2.6.15.tgz#731271430e276983191930816303c44159e7226c" - integrity sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ== - dependencies: - whatwg-url "^5.0.0" - -"@supabase/postgrest-js@1.19.4": - version "1.19.4" - resolved "https://registry.yarnpkg.com/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz#41de1e4310ce8ddba87becd7e0878f4ad7a659a2" - integrity sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw== - dependencies: - "@supabase/node-fetch" "^2.6.14" - -"@supabase/realtime-js@2.11.2": - version "2.11.2" - resolved "https://registry.yarnpkg.com/@supabase/realtime-js/-/realtime-js-2.11.2.tgz#7f7399c326be717eadc9d5e259f9e2690fbf83dd" - integrity sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w== - dependencies: - "@supabase/node-fetch" "^2.6.14" - "@types/phoenix" "^1.5.4" - "@types/ws" "^8.5.10" - ws "^8.18.0" - -"@supabase/storage-js@2.7.1": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@supabase/storage-js/-/storage-js-2.7.1.tgz#761482f237deec98a59e5af1ace18c7a5e0a69af" - integrity sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA== - dependencies: - "@supabase/node-fetch" "^2.6.14" - -"@supabase/supabase-js@^2.49.4": - version "2.49.4" - resolved "https://registry.yarnpkg.com/@supabase/supabase-js/-/supabase-js-2.49.4.tgz#5be79f0bf5e6ba9aa5be0775fb0073871834e4d3" - integrity sha512-jUF0uRUmS8BKt37t01qaZ88H9yV1mbGYnqLeuFWLcdV+x1P4fl0yP9DGtaEhFPZcwSom7u16GkLEH9QJZOqOkw== - dependencies: - "@supabase/auth-js" "2.69.1" - "@supabase/functions-js" "2.4.4" - "@supabase/node-fetch" "2.6.15" - "@supabase/postgrest-js" "1.19.4" - "@supabase/realtime-js" "2.11.2" - "@supabase/storage-js" "2.7.1" - "@svgr/babel-plugin-add-jsx-attribute@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22" @@ -7092,11 +7035,6 @@ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== -"@types/phoenix@^1.5.4": - version "1.6.6" - resolved "https://registry.yarnpkg.com/@types/phoenix/-/phoenix-1.6.6.tgz#3c1ab53fd5a23634b8e37ea72ccacbf07fbc7816" - integrity sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A== - "@types/prettier@^2.1.5": version "2.7.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" @@ -7344,13 +7282,6 @@ dependencies: "@types/node" "*" -"@types/ws@^8.5.10": - version "8.18.1" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" - integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== - dependencies: - "@types/node" "*" - "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" @@ -14320,11 +14251,6 @@ hermes-estree@0.32.0: resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.32.0.tgz#bb7da6613ab8e67e334a1854ea1e209f487d307b" integrity sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ== -hermes-estree@0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.8.0.tgz#530be27243ca49f008381c1f3e8b18fb26bf9ec0" - integrity sha512-W6JDAOLZ5pMPMjEiQGLCXSSV7pIBEgRR5zGkxgmzGSXHOxqV5dC/M1Zevqpbm9TZDE5tu358qZf8Vkzmsc+u7Q== - hermes-parser@0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.12.0.tgz#114dc26697cfb41a6302c215b859b74224383773" @@ -14339,13 +14265,6 @@ hermes-parser@0.32.0: dependencies: hermes-estree "0.32.0" -hermes-parser@0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.8.0.tgz#116dceaba32e45b16d6aefb5c4c830eaeba2d257" - integrity sha512-yZKalg1fTYG5eOiToLUaw69rQfZq/fi+/NtEXRU7N87K/XobNRhRWorh80oSge2lWUiZfTgUvRJH+XgZWrhoqA== - dependencies: - hermes-estree "0.8.0" - hermes-profile-transformer@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz#bd0f5ecceda80dd0ddaae443469ab26fb38fc27b" @@ -14637,7 +14556,7 @@ image-q@^4.0.0: dependencies: "@types/node" "16.9.1" -image-size@^0.6.0, image-size@^1.0.2, image-size@^1.2.1: +image-size@^1.0.2, image-size@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.2.1.tgz#ee118aedfe666db1a6ee12bed5821cde3740276d" integrity sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw== @@ -15851,11 +15770,6 @@ jest-environment-node@^29.2.1, jest-environment-node@^29.7.0: jest-mock "^29.7.0" jest-util "^29.7.0" -jest-get-type@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" - integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== - jest-get-type@^28.0.2: version "28.0.2" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" @@ -16355,18 +16269,6 @@ jest-util@^29.7.0: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^26.5.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" - integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== - dependencies: - "@jest/types" "^26.6.2" - camelcase "^6.0.0" - chalk "^4.0.0" - jest-get-type "^26.3.0" - leven "^3.1.0" - pretty-format "^26.6.2" - jest-validate@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" @@ -17756,16 +17658,6 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -metro-babel-transformer@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.76.1.tgz#feb14d58b71b1767e9c9af2c294768a30ededd4e" - integrity sha512-dhLo5tS+PxhkOLkHH5Cxw1FF+zQuUTXl2a5K/DD73ggj+bNXMm61bpmjKldIk6j1QkzwvuHvs7NugC6bzoi7tA== - dependencies: - "@babel/core" "^7.20.0" - hermes-parser "0.8.0" - metro-source-map "0.76.1" - nullthrows "^1.1.1" - metro-babel-transformer@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.76.8.tgz#5efd1027353b36b73706164ef09c290dceac096a" @@ -17775,55 +17667,11 @@ metro-babel-transformer@0.76.8: hermes-parser "0.12.0" nullthrows "^1.1.1" -metro-babel-transformer@0.76.9: - version "0.76.9" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.76.9.tgz#659ba481d471b5f748c31a8f9397094b629f50ec" - integrity sha512-dAnAmBqRdTwTPVn4W4JrowPolxD1MDbuU97u3MqtWZgVRvDpmr+Cqnn5oSxLQk3Uc+Zy3wkqVrB/zXNRlLDSAQ== - dependencies: - "@babel/core" "^7.20.0" - hermes-parser "0.12.0" - nullthrows "^1.1.1" - -metro-babel-transformer@0.83.3: - version "0.83.3" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.83.3.tgz#d8c134615530c9ee61364526d44ca4bb0c5343ea" - integrity sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g== - dependencies: - "@babel/core" "^7.25.2" - flow-enums-runtime "^0.0.6" - hermes-parser "0.32.0" - nullthrows "^1.1.1" - -metro-cache-key@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.76.1.tgz#c98c55c422ddea5519b01fe872519dc696b1c1ed" - integrity sha512-edykkNFb3yt+uESZynTm+vVBlJFKf3tGOpZdvScovvlfRAInWuoErpZ72q2oFeswIEsE0D6J8kJgILtikhIHeA== - metro-cache-key@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.76.8.tgz#8a0a5e991c06f56fcc584acadacb313c312bdc16" integrity sha512-buKQ5xentPig9G6T37Ww/R/bC+/V1MA5xU/D8zjnhlelsrPG6w6LtHUS61ID3zZcMZqYaELWk5UIadIdDsaaLw== -metro-cache-key@0.76.9: - version "0.76.9" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.76.9.tgz#6f17f821d6f306fa9028b7e79445eb18387d03d9" - integrity sha512-ugJuYBLngHVh1t2Jj+uP9pSCQl7enzVXkuh6+N3l0FETfqjgOaSHlcnIhMPn6yueGsjmkiIfxQU4fyFVXRtSTw== - -metro-cache-key@0.83.3: - version "0.83.3" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.83.3.tgz#ae6c5d4eb1ad8d06a92bf7294ca730a8d880b573" - integrity sha512-59ZO049jKzSmvBmG/B5bZ6/dztP0ilp0o988nc6dpaDsU05Cl1c/lRf+yx8m9WW/JVgbmfO5MziBU559XjI5Zw== - dependencies: - flow-enums-runtime "^0.0.6" - -metro-cache@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.76.1.tgz#d71827d99c2f6c6b5b8a69232c0ba13325a79eb4" - integrity sha512-2vpLWxG0fxZMYlSADPdoyrljra/8eAWaIOuSVZDQDcUowOjKmm8DGYZ7TJ8g4XJe0+RqU2s9MBazh/tMbeQIkQ== - dependencies: - metro-core "0.76.1" - rimraf "^3.0.2" - metro-cache@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.76.8.tgz#296c1c189db2053b89735a8f33dbe82575f53661" @@ -17850,18 +17698,6 @@ metro-cache@0.83.3: https-proxy-agent "^7.0.5" metro-core "0.83.3" -metro-config@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.76.1.tgz#335fa403a0d68337bbcefad4de69af48e808fb52" - integrity sha512-HCJOymif3LQsK2YsB/5Br15r9gOOBBP/6aikxy48aLoYj5uzQGVBqHAjaJMnYNN2AKRj49cHGESryKrlhRg7LQ== - dependencies: - cosmiconfig "^5.0.5" - jest-validate "^26.5.2" - metro "0.76.1" - metro-cache "0.76.1" - metro-core "0.76.1" - metro-runtime "0.76.1" - metro-config@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.76.8.tgz#20bd5397fcc6096f98d2a813a7cecb38b8af062d" @@ -17875,7 +17711,7 @@ metro-config@0.76.8: metro-core "0.76.8" metro-runtime "0.76.8" -metro-config@0.76.9, metro-config@^0.76.9: +metro-config@^0.76.9: version "0.76.9" resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.76.9.tgz#5e60aff9d8894c1ee6bbc5de23b7c8515a0b84a3" integrity sha512-oYyJ16PY3rprsfoi80L+gDJhFJqsKI3Pob5LKQbJpvL+gGr8qfZe1eQzYp5Xxxk9DOHKBV1xD94NB8GdT/DA8Q== @@ -17888,7 +17724,7 @@ metro-config@0.76.9, metro-config@^0.76.9: metro-core "0.76.9" metro-runtime "0.76.9" -metro-config@0.83.3, metro-config@^0.83.1: +metro-config@^0.83.1: version "0.83.3" resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.83.3.tgz#a30e7a69b5cf8c4ac4c4b68b1b4c33649ae129a2" integrity sha512-mTel7ipT0yNjKILIan04bkJkuCzUUkm2SeEaTads8VfEecCh+ltXchdq6DovXJqzQAXuR2P9cxZB47Lg4klriA== @@ -17902,14 +17738,6 @@ metro-config@0.83.3, metro-config@^0.83.1: metro-runtime "0.83.3" yaml "^2.6.1" -metro-core@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.76.1.tgz#5dea1c4365ad63f7dc4b987ef0fefdf5e82f85a2" - integrity sha512-RAqanj94vOFrwRjOdF3ByhiHJO87kXLW01LLvH+VmJzlyKJ5P7BN4/VnLKYyHS+uBiGnf78tN7ip/uz8jiQMhA== - dependencies: - lodash.throttle "^4.1.1" - metro-resolver "0.76.1" - metro-core@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.76.8.tgz#917c8157c63406cb223522835abb8e7c6291dcad" @@ -17935,26 +17763,6 @@ metro-core@0.83.3, metro-core@^0.83.1: lodash.throttle "^4.1.1" metro-resolver "0.83.3" -metro-file-map@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.76.1.tgz#5a93c4e6ff3c68aaee199ef7d6807c7527621768" - integrity sha512-I/9RUxWQvty7JXG18t4HJTG8QN7Mg+ZBC1/6s70e1w7fincNfuiwwMltpthZ26e9QlN672Eu6Q0ZX4W2iQwkCg== - dependencies: - anymatch "^3.0.3" - debug "^2.2.0" - fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - invariant "^2.2.4" - jest-regex-util "^27.0.6" - jest-util "^27.2.0" - jest-worker "^27.2.0" - micromatch "^4.0.4" - node-abort-controller "^3.1.1" - nullthrows "^1.1.1" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.3.2" - metro-file-map@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.76.8.tgz#a1db1185b6c316904ba6b53d628e5d1323991d79" @@ -17975,52 +17783,6 @@ metro-file-map@0.76.8: optionalDependencies: fsevents "^2.3.2" -metro-file-map@0.76.9: - version "0.76.9" - resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.76.9.tgz#dd3d76ec23fc0ba8cb7b3a3b8075bb09e0b5d378" - integrity sha512-7vJd8kksMDTO/0fbf3081bTrlw8SLiploeDf+vkkf0OwlrtDUWPOikfebp+MpZB2S61kamKjCNRfRkgrbPfSwg== - dependencies: - anymatch "^3.0.3" - debug "^2.2.0" - fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - invariant "^2.2.4" - jest-regex-util "^27.0.6" - jest-util "^27.2.0" - jest-worker "^27.2.0" - micromatch "^4.0.4" - node-abort-controller "^3.1.1" - nullthrows "^1.1.1" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.3.2" - -metro-file-map@0.83.3: - version "0.83.3" - resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.83.3.tgz#3d79fbb1d379ab178dd895ce54cb5ecb183d74a2" - integrity sha512-jg5AcyE0Q9Xbbu/4NAwwZkmQn7doJCKGW0SLeSJmzNB9Z24jBe0AL2PHNMy4eu0JiKtNWHz9IiONGZWq7hjVTA== - dependencies: - debug "^4.4.0" - fb-watchman "^2.0.0" - flow-enums-runtime "^0.0.6" - graceful-fs "^4.2.4" - invariant "^2.2.4" - jest-worker "^29.7.0" - micromatch "^4.0.4" - nullthrows "^1.1.1" - walker "^1.0.7" - -metro-inspector-proxy@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.76.1.tgz#43a4de729884fab8a8125a968f9bff13d6e4e040" - integrity sha512-SCyXw4SOO7Y8f3eD5ecwXlRLxo+hczeOvUi1cNnh5q02EjAnHwqhmGMulxB0KtEpAgTvkUJMlz5NCwUo5t6ugw== - dependencies: - connect "^3.6.5" - debug "^2.2.0" - node-fetch "^2.2.0" - ws "^7.5.1" - yargs "^17.6.2" - metro-inspector-proxy@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.76.8.tgz#6b8678a7461b0b42f913a7881cc9319b4d3cddff" @@ -18032,24 +17794,6 @@ metro-inspector-proxy@0.76.8: ws "^7.5.1" yargs "^17.6.2" -metro-inspector-proxy@0.76.9: - version "0.76.9" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.76.9.tgz#0d333e64a7bc9d156d712265faa7b7ae88c775e8" - integrity sha512-idIiPkb8CYshc0WZmbzwmr4B1QwsQUbpDwBzHwxE1ni27FWKWhV9CD5p+qlXZHgfwJuMRfPN+tIaLSR8+vttYg== - dependencies: - connect "^3.6.5" - debug "^2.2.0" - node-fetch "^2.2.0" - ws "^7.5.1" - yargs "^17.6.2" - -metro-minify-terser@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.76.1.tgz#be860d4d00b18309a171a96692feab2011792d90" - integrity sha512-M0Dvu4IdePW63Hlbj0D2/HmV90195aWYGb/B9ZBelLrI2vhlD1t606EidhQb1HFq6EaD56LimgGHtvzy83MqKQ== - dependencies: - terser "^5.15.0" - metro-minify-terser@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.76.8.tgz#915ab4d1419257fc6a0b9fa15827b83fe69814bf" @@ -18057,28 +17801,6 @@ metro-minify-terser@0.76.8: dependencies: terser "^5.15.0" -metro-minify-terser@0.76.9: - version "0.76.9" - resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.76.9.tgz#3f6271da74dd57179852118443b62cc8dc578aab" - integrity sha512-ju2nUXTKvh96vHPoGZH/INhSvRRKM14CbGAJXQ98+g8K5z1v3luYJ/7+dFQB202eVzJdTB2QMtBjI1jUUpooCg== - dependencies: - terser "^5.15.0" - -metro-minify-terser@0.83.3: - version "0.83.3" - resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.83.3.tgz#c1c70929c86b14c8bf03e6321b4f9310bc8dbe87" - integrity sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ== - dependencies: - flow-enums-runtime "^0.0.6" - terser "^5.15.0" - -metro-minify-uglify@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.76.1.tgz#47875dfc17b7fc68ecab6b1388b2ed1a659fe248" - integrity sha512-36wOrfb+bn2d8n3Is0RYUMl7S85jBk/QB7vqsW7inYqgVadosRz/uTItGghutB2GN0ElOEVPpOezr64I9+bqng== - dependencies: - uglify-es "^3.1.9" - metro-minify-uglify@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.76.8.tgz#74745045ea2dd29f8783db483b2fce58385ba695" @@ -18086,58 +17808,6 @@ metro-minify-uglify@0.76.8: dependencies: uglify-es "^3.1.9" -metro-minify-uglify@0.76.9: - version "0.76.9" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.76.9.tgz#e88c30c27911c053e1ee20e12077f0f4cbb154f8" - integrity sha512-MXRrM3lFo62FPISlPfTqC6n9HTEI3RJjDU5SvpE7sJFfJKLx02xXQEltsL/wzvEqK+DhRQ5DEYACTwf5W4Z3yA== - dependencies: - uglify-es "^3.1.9" - -metro-react-native-babel-preset@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.1.tgz#c6cc85556091d2064fe9c9e339c227cdce773b1b" - integrity sha512-1336W8B84eew4J3bfTbL2I40S3M03v5Rus6nTjAsXMkJZWpeVFvJLRG3NO04kp+7CyFJCR1JBOJPzpLLv75fNQ== - dependencies: - "@babel/core" "^7.20.0" - "@babel/plugin-proposal-async-generator-functions" "^7.0.0" - "@babel/plugin-proposal-class-properties" "^7.0.0" - "@babel/plugin-proposal-export-default-from" "^7.0.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-proposal-numeric-separator" "^7.0.0" - "@babel/plugin-proposal-object-rest-spread" "^7.0.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" - "@babel/plugin-proposal-optional-chaining" "^7.0.0" - "@babel/plugin-syntax-dynamic-import" "^7.0.0" - "@babel/plugin-syntax-export-default-from" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.18.0" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-syntax-optional-chaining" "^7.0.0" - "@babel/plugin-transform-arrow-functions" "^7.0.0" - "@babel/plugin-transform-async-to-generator" "^7.0.0" - "@babel/plugin-transform-block-scoping" "^7.0.0" - "@babel/plugin-transform-classes" "^7.0.0" - "@babel/plugin-transform-computed-properties" "^7.0.0" - "@babel/plugin-transform-destructuring" "^7.0.0" - "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-function-name" "^7.0.0" - "@babel/plugin-transform-literals" "^7.0.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.0.0" - "@babel/plugin-transform-parameters" "^7.0.0" - "@babel/plugin-transform-react-display-name" "^7.0.0" - "@babel/plugin-transform-react-jsx" "^7.0.0" - "@babel/plugin-transform-react-jsx-self" "^7.0.0" - "@babel/plugin-transform-react-jsx-source" "^7.0.0" - "@babel/plugin-transform-runtime" "^7.0.0" - "@babel/plugin-transform-shorthand-properties" "^7.0.0" - "@babel/plugin-transform-spread" "^7.0.0" - "@babel/plugin-transform-sticky-regex" "^7.0.0" - "@babel/plugin-transform-typescript" "^7.5.0" - "@babel/plugin-transform-unicode-regex" "^7.0.0" - "@babel/template" "^7.0.0" - babel-plugin-transform-flow-enums "^0.0.2" - react-refresh "^0.4.0" - metro-react-native-babel-preset@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.8.tgz#7476efae14363cbdfeeec403b4f01d7348e6c048" @@ -18250,11 +17920,6 @@ metro-react-native-babel-transformer@^0.76.9: metro-react-native-babel-preset "0.76.9" nullthrows "^1.1.1" -metro-resolver@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.76.1.tgz#b24ab6489834952290346830eca1ec1ca7822e48" - integrity sha512-a0tRFz1dwQX/trNIpi3PR6rE4fjvHeZgZFFhNbCYsLV9xrdkxbdUTo66eegnPji0DZRdxsUgo1YkVPHY6FNQiA== - metro-resolver@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.76.8.tgz#0862755b9b84e26853978322464fb37c6fdad76d" @@ -18272,14 +17937,6 @@ metro-resolver@0.83.3: dependencies: flow-enums-runtime "^0.0.6" -metro-runtime@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.76.1.tgz#f3b50b5a777d94222196459f48215dca7a98853c" - integrity sha512-J9yMTpbsao0YOo8PrzgYKbC+ZpRrPu+T3HNme7MrWNr9DgIG+8ufkyvQl9u8ieTN2uWi0habIg5VXW35TJZtSw== - dependencies: - "@babel/runtime" "^7.0.0" - react-refresh "^0.4.0" - metro-runtime@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.76.8.tgz#74b2d301a2be5f3bbde91b8f1312106f8ffe50c3" @@ -18304,20 +17961,6 @@ metro-runtime@0.83.3, metro-runtime@^0.83.1: "@babel/runtime" "^7.25.0" flow-enums-runtime "^0.0.6" -metro-source-map@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.76.1.tgz#1e0487bf807b9b52c5a71580d588ee02f6b059d4" - integrity sha512-lfMhXX7JokW4pkDlqYYtzLaqC3OKF4/AJPjgMtR44NSFWvJJO/pHrBebVnth8zDG+hO7R+SXgtnXcGMYbrwyyQ== - dependencies: - "@babel/traverse" "^7.20.0" - "@babel/types" "^7.20.0" - invariant "^2.2.4" - metro-symbolicate "0.76.1" - nullthrows "^1.1.1" - ob1 "0.76.1" - source-map "^0.5.6" - vlq "^1.0.0" - metro-source-map@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.76.8.tgz#f085800152a6ba0b41ca26833874d31ec36c5a53" @@ -18332,20 +17975,6 @@ metro-source-map@0.76.8: source-map "^0.5.6" vlq "^1.0.0" -metro-source-map@0.76.9: - version "0.76.9" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.76.9.tgz#0f976ada836717f307427d3830aea52a2ca7ed5f" - integrity sha512-q5qsMlu8EFvsT46wUUh+ao+efDsicT30zmaPATNhq+PcTawDbDgnMuUD+FT0bvxxnisU2PWl91RdzKfNc2qPQA== - dependencies: - "@babel/traverse" "^7.20.0" - "@babel/types" "^7.20.0" - invariant "^2.2.4" - metro-symbolicate "0.76.9" - nullthrows "^1.1.1" - ob1 "0.76.9" - source-map "^0.5.6" - vlq "^1.0.0" - metro-source-map@0.83.3, metro-source-map@^0.83.1: version "0.83.3" resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.83.3.tgz#04bb464f7928ea48bcdfd18912c8607cf317c898" @@ -18362,18 +17991,6 @@ metro-source-map@0.83.3, metro-source-map@^0.83.1: source-map "^0.5.6" vlq "^1.0.0" -metro-symbolicate@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.76.1.tgz#6beb39f1e31050eac228642073af5c0a8caeb6ac" - integrity sha512-t7D7WXOE9d5eAGUiv7N61ZhDyQ0KbRegqB/AMzBJB3rPmuQPDO0nvadY1/m7BeJKu+va6KIF1gI6JolnxmCzjQ== - dependencies: - invariant "^2.2.4" - metro-source-map "0.76.1" - nullthrows "^1.1.1" - source-map "^0.5.6" - through2 "^2.0.1" - vlq "^1.0.0" - metro-symbolicate@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.76.8.tgz#f102ac1a306d51597ecc8fdf961c0a88bddbca03" @@ -18386,18 +18003,6 @@ metro-symbolicate@0.76.8: through2 "^2.0.1" vlq "^1.0.0" -metro-symbolicate@0.76.9: - version "0.76.9" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.76.9.tgz#f1627ef6f73bb0c4d48c55684d3c87866a0b0920" - integrity sha512-Yyq6Ukj/IeWnGST09kRt0sBK8TwzGZWoU7YAcQlh14+AREH454Olx4wbFTpkkhUkV05CzNCvUuXQ0efFxhA1bw== - dependencies: - invariant "^2.2.4" - metro-source-map "0.76.9" - nullthrows "^1.1.1" - source-map "^0.5.6" - through2 "^2.0.1" - vlq "^1.0.0" - metro-symbolicate@0.83.3: version "0.83.3" resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.83.3.tgz#67af03950f0dfe19a7c059e3983e39a31e95d03a" @@ -18410,17 +18015,6 @@ metro-symbolicate@0.83.3: source-map "^0.5.6" vlq "^1.0.0" -metro-transform-plugins@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.76.1.tgz#22ef3373c9272303bf25a2b24fb3cdce2e07bdca" - integrity sha512-3nhguyDVS0zi9rELCiB6jWW+vBLbnRxYVqNJqDEtX7Gf0DZnhl8oP8gU4k3R80wlADjahuhXV2xzzA0qVqfPog== - dependencies: - "@babel/core" "^7.20.0" - "@babel/generator" "^7.20.0" - "@babel/template" "^7.0.0" - "@babel/traverse" "^7.20.0" - nullthrows "^1.1.1" - metro-transform-plugins@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.76.8.tgz#d77c28a6547a8e3b72250f740fcfbd7f5408f8ba" @@ -18432,47 +18026,6 @@ metro-transform-plugins@0.76.8: "@babel/traverse" "^7.20.0" nullthrows "^1.1.1" -metro-transform-plugins@0.76.9: - version "0.76.9" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.76.9.tgz#73e34f2014d3df3c336a882b13e541bceb826d37" - integrity sha512-YEQeNlOCt92I7S9A3xbrfaDfwfgcxz9PpD/1eeop3c4cO3z3Q3otYuxw0WJ/rUIW8pZfOm5XCehd+1NRbWlAaw== - dependencies: - "@babel/core" "^7.20.0" - "@babel/generator" "^7.20.0" - "@babel/template" "^7.0.0" - "@babel/traverse" "^7.20.0" - nullthrows "^1.1.1" - -metro-transform-plugins@0.83.3: - version "0.83.3" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.83.3.tgz#2c59ba841e269363cf3acb13138cb992f0c75013" - integrity sha512-eRGoKJU6jmqOakBMH5kUB7VitEWiNrDzBHpYbkBXW7C5fUGeOd2CyqrosEzbMK5VMiZYyOcNFEphvxk3OXey2A== - dependencies: - "@babel/core" "^7.25.2" - "@babel/generator" "^7.25.0" - "@babel/template" "^7.25.0" - "@babel/traverse" "^7.25.3" - flow-enums-runtime "^0.0.6" - nullthrows "^1.1.1" - -metro-transform-worker@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.76.1.tgz#b736bb4df90752d339b33c1caa7a10733cdfbc10" - integrity sha512-TiFxZIxLrARd9OEr2l0yRK/eCGEZ7Euv1x8x45mZeo0YZ6SwJLiCuL/Z4TQDyvn7tT+PzgCQonmjtok1i2MhJQ== - dependencies: - "@babel/core" "^7.20.0" - "@babel/generator" "^7.20.0" - "@babel/parser" "^7.20.0" - "@babel/types" "^7.20.0" - babel-preset-fbjs "^3.4.0" - metro "0.76.1" - metro-babel-transformer "0.76.1" - metro-cache "0.76.1" - metro-cache-key "0.76.1" - metro-source-map "0.76.1" - metro-transform-plugins "0.76.1" - nullthrows "^1.1.1" - metro-transform-worker@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.76.8.tgz#b9012a196cee205170d0c899b8b175b9305acdea" @@ -18491,98 +18044,7 @@ metro-transform-worker@0.76.8: metro-transform-plugins "0.76.8" nullthrows "^1.1.1" -metro-transform-worker@0.76.9: - version "0.76.9" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.76.9.tgz#281fad223f0447e1ff9cc44d6f7e33dfab9ab120" - integrity sha512-F69A0q0qFdJmP2Clqr6TpTSn4WTV9p5A28h5t9o+mB22ryXBZfUQ6BFBBW/6Wp2k/UtPH+oOsBfV9guiqm3d2Q== - dependencies: - "@babel/core" "^7.20.0" - "@babel/generator" "^7.20.0" - "@babel/parser" "^7.20.0" - "@babel/types" "^7.20.0" - babel-preset-fbjs "^3.4.0" - metro "0.76.9" - metro-babel-transformer "0.76.9" - metro-cache "0.76.9" - metro-cache-key "0.76.9" - metro-minify-terser "0.76.9" - metro-source-map "0.76.9" - metro-transform-plugins "0.76.9" - nullthrows "^1.1.1" - -metro-transform-worker@0.83.3: - version "0.83.3" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.83.3.tgz#ca6ae4a02b0f61b33299e6e56bacaba32dcd607f" - integrity sha512-Ztekew9t/gOIMZX1tvJOgX7KlSLL5kWykl0Iwu2cL2vKMKVALRl1hysyhUw0vjpAvLFx+Kfq9VLjnHIkW32fPA== - dependencies: - "@babel/core" "^7.25.2" - "@babel/generator" "^7.25.0" - "@babel/parser" "^7.25.3" - "@babel/types" "^7.25.2" - flow-enums-runtime "^0.0.6" - metro "0.83.3" - metro-babel-transformer "0.83.3" - metro-cache "0.83.3" - metro-cache-key "0.83.3" - metro-minify-terser "0.83.3" - metro-source-map "0.83.3" - metro-transform-plugins "0.83.3" - nullthrows "^1.1.1" - -metro@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.76.1.tgz#935556c1bfaebd9bda36f0eaca041d3c343a8fdd" - integrity sha512-zuO8wKvF/kgVMjcKkxM/o68J4Kqc1/ZtQ3mRQdNe1+/hHjIfoxfO3Btr98jFMUL4VHq7Dg5p/mJZdzOabU7Sbg== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/core" "^7.20.0" - "@babel/generator" "^7.20.0" - "@babel/parser" "^7.20.0" - "@babel/template" "^7.0.0" - "@babel/traverse" "^7.20.0" - "@babel/types" "^7.20.0" - accepts "^1.3.7" - async "^3.2.2" - chalk "^4.0.0" - ci-info "^2.0.0" - connect "^3.6.5" - debug "^2.2.0" - denodeify "^1.2.1" - error-stack-parser "^2.0.6" - graceful-fs "^4.2.4" - hermes-parser "0.8.0" - image-size "^0.6.0" - invariant "^2.2.4" - jest-worker "^27.2.0" - lodash.throttle "^4.1.1" - metro-babel-transformer "0.76.1" - metro-cache "0.76.1" - metro-cache-key "0.76.1" - metro-config "0.76.1" - metro-core "0.76.1" - metro-file-map "0.76.1" - metro-inspector-proxy "0.76.1" - metro-minify-terser "0.76.1" - metro-minify-uglify "0.76.1" - metro-react-native-babel-preset "0.76.1" - metro-resolver "0.76.1" - metro-runtime "0.76.1" - metro-source-map "0.76.1" - metro-symbolicate "0.76.1" - metro-transform-plugins "0.76.1" - metro-transform-worker "0.76.1" - mime-types "^2.1.27" - node-fetch "^2.2.0" - nullthrows "^1.1.1" - rimraf "^3.0.2" - serialize-error "^2.1.0" - source-map "^0.5.6" - strip-ansi "^6.0.0" - throat "^5.0.0" - ws "^7.5.1" - yargs "^17.6.2" - -metro@0.76.8: +metro@0.76.8, metro@0.76.9, metro@0.83.3, metro@^0.83.1: version "0.76.8" resolved "https://registry.yarnpkg.com/metro/-/metro-0.76.8.tgz#ba526808b99977ca3f9ac5a7432fd02a340d13a6" integrity sha512-oQA3gLzrrYv3qKtuWArMgHPbHu8odZOD9AoavrqSFllkPgOtmkBvNNDLCELqv5SjBfqjISNffypg+5UGG3y0pg== @@ -18636,105 +18098,6 @@ metro@0.76.8: ws "^7.5.1" yargs "^17.6.2" -metro@0.76.9: - version "0.76.9" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.76.9.tgz#605fddf1a54d27762ddba2f636420ae2408862df" - integrity sha512-gcjcfs0l5qIPg0lc5P7pj0x7vPJ97tan+OnEjiYLbKjR1D7Oa78CE93YUPyymUPH6q7VzlzMm1UjT35waEkZUw== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/core" "^7.20.0" - "@babel/generator" "^7.20.0" - "@babel/parser" "^7.20.0" - "@babel/template" "^7.0.0" - "@babel/traverse" "^7.20.0" - "@babel/types" "^7.20.0" - accepts "^1.3.7" - async "^3.2.2" - chalk "^4.0.0" - ci-info "^2.0.0" - connect "^3.6.5" - debug "^2.2.0" - denodeify "^1.2.1" - error-stack-parser "^2.0.6" - graceful-fs "^4.2.4" - hermes-parser "0.12.0" - image-size "^1.0.2" - invariant "^2.2.4" - jest-worker "^27.2.0" - jsc-safe-url "^0.2.2" - lodash.throttle "^4.1.1" - metro-babel-transformer "0.76.9" - metro-cache "0.76.9" - metro-cache-key "0.76.9" - metro-config "0.76.9" - metro-core "0.76.9" - metro-file-map "0.76.9" - metro-inspector-proxy "0.76.9" - metro-minify-uglify "0.76.9" - metro-react-native-babel-preset "0.76.9" - metro-resolver "0.76.9" - metro-runtime "0.76.9" - metro-source-map "0.76.9" - metro-symbolicate "0.76.9" - metro-transform-plugins "0.76.9" - metro-transform-worker "0.76.9" - mime-types "^2.1.27" - node-fetch "^2.2.0" - nullthrows "^1.1.1" - rimraf "^3.0.2" - serialize-error "^2.1.0" - source-map "^0.5.6" - strip-ansi "^6.0.0" - throat "^5.0.0" - ws "^7.5.1" - yargs "^17.6.2" - -metro@0.83.3, metro@^0.83.1: - version "0.83.3" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.83.3.tgz#1e7e04c15519af746f8932c7f9c553d92c39e922" - integrity sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q== - dependencies: - "@babel/code-frame" "^7.24.7" - "@babel/core" "^7.25.2" - "@babel/generator" "^7.25.0" - "@babel/parser" "^7.25.3" - "@babel/template" "^7.25.0" - "@babel/traverse" "^7.25.3" - "@babel/types" "^7.25.2" - accepts "^1.3.7" - chalk "^4.0.0" - ci-info "^2.0.0" - connect "^3.6.5" - debug "^4.4.0" - error-stack-parser "^2.0.6" - flow-enums-runtime "^0.0.6" - graceful-fs "^4.2.4" - hermes-parser "0.32.0" - image-size "^1.0.2" - invariant "^2.2.4" - jest-worker "^29.7.0" - jsc-safe-url "^0.2.2" - lodash.throttle "^4.1.1" - metro-babel-transformer "0.83.3" - metro-cache "0.83.3" - metro-cache-key "0.83.3" - metro-config "0.83.3" - metro-core "0.83.3" - metro-file-map "0.83.3" - metro-resolver "0.83.3" - metro-runtime "0.83.3" - metro-source-map "0.83.3" - metro-symbolicate "0.83.3" - metro-transform-plugins "0.83.3" - metro-transform-worker "0.83.3" - mime-types "^2.1.27" - nullthrows "^1.1.1" - serialize-error "^2.1.0" - source-map "^0.5.6" - throat "^5.0.0" - ws "^7.5.10" - yargs "^17.6.2" - microevent.ts@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" @@ -19530,21 +18893,11 @@ nyc@^15.1.0: test-exclude "^6.0.0" yargs "^15.0.2" -ob1@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.76.1.tgz#83bc8ba5ca7856be43a17491d0cb0f81643cb58e" - integrity sha512-1i5IJGQGGU9c3WZ/vk718F34cEuTGYqBPBDrQD8KHdbfZuM4B84OBXTkTPGNbVEC+VyzA8uo7O2PRVlMCAiCnQ== - ob1@0.76.8: version "0.76.8" resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.76.8.tgz#ac4c459465b1c0e2c29aaa527e09fc463d3ffec8" integrity sha512-dlBkJJV5M/msj9KYA9upc+nUWVwuOFFTbu28X6kZeGwcuW+JxaHSBZ70SYQnk5M+j5JbNLR6yKHmgW4M5E7X5g== -ob1@0.76.9: - version "0.76.9" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.76.9.tgz#a493e4b83a0fb39200de639804b5d06eed5599dc" - integrity sha512-g0I/OLnSxf6OrN3QjSew3bTDJCdbZoWxnh8adh1z36alwCuGF1dgDeRA25bTYSakrG5WULSaWJPOdgnf1O/oQw== - ob1@0.83.3: version "0.83.3" resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.83.3.tgz#2208e20c9070e9beff3ad067f2db458fa6b07014" @@ -21360,11 +20713,6 @@ react-native-html-to-pdf@^0.12.0: resolved "https://registry.yarnpkg.com/react-native-html-to-pdf/-/react-native-html-to-pdf-0.12.0.tgz#2b467296f85c9c9783a7288b19722a7028dcbcb8" integrity sha512-Yb5WO9SfF86s5Yv9PqXQ7fZDr9zZOJ+6jtweT9zFLraPNHWX7pSxe2dSkeg3cGiNrib65ZXGN6ksHymfYLFSSg== -react-native-image-crop-picker@^0.39.0: - version "0.39.0" - resolved "https://registry.yarnpkg.com/react-native-image-crop-picker/-/react-native-image-crop-picker-0.39.0.tgz#9cb8e8ffb0e8ab06f7b3227cadf077169e225eba" - integrity sha512-4aANbQMrmU6zN/4b0rVBA7SbaZ3aa5JESm3Xk751sINybZMt1yz/9h95LkO7U0pbslHDo3ofXjG75PmQRP6a/w== - react-native-image-picker@^7.0.0: version "7.2.3" resolved "https://registry.yarnpkg.com/react-native-image-picker/-/react-native-image-picker-7.2.3.tgz#9c402591462af256cdd9aed796c28083a48f90cd" @@ -21660,7 +21008,7 @@ react-native-vision-camera@3.6.4: resolved "https://registry.yarnpkg.com/react-native-vision-camera/-/react-native-vision-camera-3.6.4.tgz#ac77238f31779f647d03bce3cd4b5eba61f3df85" integrity sha512-DDWb8aB5tE7sM7YuU5f31AuhJVDJS3amZoZMQgHWa8S9h0acp3hY3uyriKzXshj/asYu2UAo5QUUglwl7Mcd1Q== -react-native-walkthrough-tooltip@^1.4.0: +react-native-walkthrough-tooltip@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/react-native-walkthrough-tooltip/-/react-native-walkthrough-tooltip-1.6.0.tgz#a9b9196973d2fdef8f36bbcae57c93069cd9fb3e" integrity sha512-8DHpcqsF+2VULYnKJCSZtllhlFUFiZcWCwOlw5RLkS451ElxPWJn9ShOFVZtkPMcwQAirq3SovumVM7Y/li0Fw== @@ -26400,12 +25748,7 @@ write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@8.13.0, ws@8.16.0, "ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^6.2.2, ws@^6.2.3, ws@^7, ws@^7.0.0, ws@^7.1.2, ws@^7.5.1, ws@^7.5.10, ws@^8.12.0, ws@^8.14.0, ws@^8.17.1, ws@^8.18.0, ws@^8.18.3, ws@^8.2.3, ws@^8.8.0: - version "8.18.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" - integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== - -ws@^8.18.0: +ws@8.13.0, ws@8.16.0, "ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^6.2.2, ws@^6.2.3, ws@^7, ws@^7.0.0, ws@^7.1.2, ws@^7.5.1, ws@^8.12.0, ws@^8.14.0, ws@^8.17.1, ws@^8.18.0, ws@^8.18.3, ws@^8.2.3, ws@^8.8.0: version "8.18.1" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== From 8165a17ac6e3b9e2958545f6b4029d8fbfacbab4 Mon Sep 17 00:00:00 2001 From: Nodirbek Jamoldinov Date: Wed, 7 Jan 2026 11:40:24 +0500 Subject: [PATCH 71/81] remove supabase logic --- app/screens/settings-screen/account/settings/delete.tsx | 1 - app/types/declaration.d.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/app/screens/settings-screen/account/settings/delete.tsx b/app/screens/settings-screen/account/settings/delete.tsx index d88fa2811..1a05f3e6c 100644 --- a/app/screens/settings-screen/account/settings/delete.tsx +++ b/app/screens/settings-screen/account/settings/delete.tsx @@ -20,7 +20,6 @@ import useNostrProfile from "@app/hooks/use-nostr-profile" import { useAccountDeleteContext } from "../account-delete-context" import { useDisplayCurrency } from "@app/hooks/use-display-currency" import { useAccountDeleteMutation, useSettingsScreenQuery } from "@app/graphql/generated" -import { deleteUser } from "@app/supabase" // utils import { CONTACT_EMAIL_ADDRESS } from "@app/config" diff --git a/app/types/declaration.d.ts b/app/types/declaration.d.ts index 1212e4399..ac58c9c79 100644 --- a/app/types/declaration.d.ts +++ b/app/types/declaration.d.ts @@ -31,6 +31,4 @@ declare module "@env" { export const GREENLIGHT_PARTNER_CERT: string export const GREENLIGHT_PARTNER_KEY: string export const GOOGLE_PLACE_API_KEY: string - export const SUPABASE_URL: string - export const SUPABASE_KEY: string } From f19ce66d25ac98284aaca8abaaf2b915368c73c0 Mon Sep 17 00:00:00 2001 From: Nodirbek Jamoldinov Date: Wed, 7 Jan 2026 11:40:53 +0500 Subject: [PATCH 72/81] fix QuickStart component --- app/components/home-screen/QuickStart.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/components/home-screen/QuickStart.tsx b/app/components/home-screen/QuickStart.tsx index c8e4828f3..7c63a64a5 100644 --- a/app/components/home-screen/QuickStart.tsx +++ b/app/components/home-screen/QuickStart.tsx @@ -15,13 +15,17 @@ import GoldWallet from "@app/assets/illustrations/gold-wallet.svg" import SecureWallet from "@app/assets/illustrations/secure-wallet.svg" // components -import { QuickStartAdvancedMode } from "../advanced-mode-modal" +import { AdvancedModeModal } from "../advanced-mode-modal" // hooks import { useI18nContext } from "@app/i18n/i18n-react" import { useNavigation } from "@react-navigation/native" import { usePersistentStateContext } from "@app/store/persistent-state" -import { AccountLevel, useHomeAuthedQuery, useAccountUpgradeRequestStatusQuery } from "@app/graphql/generated" +import { + AccountLevel, + useHomeAuthedQuery, + useAccountUpgradeRequestStatusQuery, +} from "@app/graphql/generated" // utils import { KEYCHAIN_MNEMONIC_KEY } from "@app/utils/breez-sdk-liquid" @@ -63,7 +67,8 @@ const QuickStart = () => { } const { data: upgradeStatusData } = useAccountUpgradeRequestStatusQuery() - const upgradePending = upgradeStatusData?.accountUpgradeRequestStatus?.hasPendingRequest ?? false + const upgradePending = + upgradeStatusData?.accountUpgradeRequestStatus?.hasPendingRequest ?? false let carouselData = [ { @@ -227,7 +232,7 @@ const QuickStart = () => { loop={carouselData.length !== 1} containerStyle={{ marginTop: 10 }} /> - Date: Thu, 8 Jan 2026 18:23:08 +0500 Subject: [PATCH 73/81] improve ui/ux on the account upgrade flow --- .../account-upgrade-flow/InputField.tsx | 4 +++ .../account-upgrade-flow/PhoneNumber.tsx | 9 ++++++- .../account-upgrade-flow/AccountType.tsx | 8 +++--- .../account-upgrade-flow/BankInformation.tsx | 23 ++++++++++------ .../BusinessInformation.tsx | 8 +++++- .../PersonalInformation.tsx | 18 +++++++++---- app/screens/account-upgrade-flow/Success.tsx | 5 +++- .../account-upgrade-flow/Validation.tsx | 26 +++++++++++-------- app/store/redux/slices/accountUpgradeSlice.ts | 3 ++- 9 files changed, 72 insertions(+), 32 deletions(-) diff --git a/app/components/account-upgrade-flow/InputField.tsx b/app/components/account-upgrade-flow/InputField.tsx index 8d5d29547..4bdfd5160 100644 --- a/app/components/account-upgrade-flow/InputField.tsx +++ b/app/components/account-upgrade-flow/InputField.tsx @@ -15,6 +15,8 @@ const InputField: React.FC = ({ isOptional, placeholder, value, + autoCapitalize, + keyboardType, onChangeText, }) => { const styles = useStyles() @@ -41,6 +43,8 @@ const InputField: React.FC = ({ onChangeText={onChangeText} onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)} + autoCapitalize={autoCapitalize} + keyboardType={keyboardType} /> {!!errorMsg && ( diff --git a/app/components/account-upgrade-flow/PhoneNumber.tsx b/app/components/account-upgrade-flow/PhoneNumber.tsx index 9c8d545ec..7fa6f2b3e 100644 --- a/app/components/account-upgrade-flow/PhoneNumber.tsx +++ b/app/components/account-upgrade-flow/PhoneNumber.tsx @@ -78,7 +78,14 @@ const PhoneNumber: React.FC = ({ /> = ({ navigation }) => { const onPress = (accountType: string) => { const numOfSteps = - accountType === "personal" ? 3 : currentLevel === AccountLevel.Zero ? 5 : 4 + accountType === AccountLevel.One ? 3 : currentLevel === AccountLevel.Zero ? 5 : 4 dispatch(setAccountUpgrade({ accountType, numOfSteps })) navigation.navigate("PersonalInformation") @@ -46,7 +46,7 @@ const AccountType: React.FC = ({ navigation }) => { {currentLevel === AccountLevel.Zero && ( - onPress("personal")}> + onPress(AccountLevel.One)}> @@ -60,7 +60,7 @@ const AccountType: React.FC = ({ navigation }) => { )} {(currentLevel === AccountLevel.Zero || currentLevel === AccountLevel.One) && ( - onPress("business")}> + onPress(AccountLevel.Two)}> @@ -73,7 +73,7 @@ const AccountType: React.FC = ({ navigation }) => { )} - onPress("merchant")}> + onPress(AccountLevel.Three)}> diff --git a/app/screens/account-upgrade-flow/BankInformation.tsx b/app/screens/account-upgrade-flow/BankInformation.tsx index 00bc6a592..6f85382d5 100644 --- a/app/screens/account-upgrade-flow/BankInformation.tsx +++ b/app/screens/account-upgrade-flow/BankInformation.tsx @@ -22,6 +22,9 @@ import { useI18nContext } from "@app/i18n/i18n-react" import { useAppDispatch, useAppSelector } from "@app/store/redux" import { setBankInfo } from "@app/store/redux/slices/accountUpgradeSlice" +// gql +import { AccountLevel } from "@app/graphql/generated" + const accountTypes = [ { label: "Select account type", value: null }, { label: "Checking", value: "checking" }, @@ -67,7 +70,7 @@ const BankInformation: React.FC = ({ navigation }) => { const onPressNext = async () => { let hasError = false - if (accountType === "merchant") { + if (accountType === AccountLevel.Three) { if (!bankName || bankName.length < 2) { setNameErr("Bank name is required") hasError = true @@ -100,7 +103,7 @@ const BankInformation: React.FC = ({ navigation }) => { else alert("Something went wrong. Please, try again later.") } } - + const isOptional = accountType === AccountLevel.Two return ( @@ -110,22 +113,24 @@ const BankInformation: React.FC = ({ navigation }) => { placeholder={LL.AccountUpgrade.bankNamePlaceholder()} value={bankName} errorMsg={nameErr} - isOptional={accountType === "business"} + isOptional={isOptional} onChangeText={(val) => { setNameErr(undefined) dispatch(setBankInfo({ bankName: val })) }} + autoCapitalize="words" /> { setBranchErr(undefined) dispatch(setBankInfo({ bankBranch: val })) }} + autoCapitalize="words" /> = ({ navigation }) => { data={accountTypes} value={bankAccountType || ""} errorMsg={accountTypeErr} - isOptional={accountType === "business"} + isOptional={isOptional} onChange={(val) => { setAccountTypeErr(undefined) dispatch(setBankInfo({ bankAccountType: val })) @@ -145,7 +150,7 @@ const BankInformation: React.FC = ({ navigation }) => { data={currencies} value={currency || ""} errorMsg={currencyErr} - isOptional={accountType === "business"} + isOptional={isOptional} onChange={(val) => { setCurrencyErr(undefined) dispatch(setBankInfo({ currency: val })) @@ -156,17 +161,19 @@ const BankInformation: React.FC = ({ navigation }) => { placeholder={LL.AccountUpgrade.accountNumPlaceholder()} value={accountNumber} errorMsg={accountNumErr} - isOptional={accountType === "business"} + isOptional={isOptional} onChangeText={(val) => { setAccountNumErr(undefined) dispatch(setBankInfo({ accountNumber: val })) }} + autoCapitalize="words" + keyboardType="number-pad" /> dispatch(setBankInfo({ idDocument: val }))} setErrorMsg={setIdDocumentErr} /> diff --git a/app/screens/account-upgrade-flow/BusinessInformation.tsx b/app/screens/account-upgrade-flow/BusinessInformation.tsx index 8635193dc..6a5f336f1 100644 --- a/app/screens/account-upgrade-flow/BusinessInformation.tsx +++ b/app/screens/account-upgrade-flow/BusinessInformation.tsx @@ -51,7 +51,12 @@ const BusinessInformation: React.FC = ({ navigation }) => { } return ( - + = ({ navigation }) => { setBusinessNameErr(undefined) dispatch(setBusinessInfo({ businessName: val })) }} + autoCapitalize="words" /> = ({ navigation }) => { const [fullNameErr, setFullNameErr] = useState() const [phoneNumberErr, setPhoneNumberErr] = useState() const { - id, numOfSteps, personalInfo: { fullName, countryCode, phoneNumber, email }, } = useAppSelector((state) => state.accountUpgrade) @@ -61,7 +60,10 @@ const PersonalInformation: React.FC = ({ navigation }) => { setCountryCode, } = useRequestPhoneCodeLogin() - const [userEmailRegistrationInitiateMutation] = useUserEmailRegistrationInitiateMutation() + const { data } = useAuthQuery() + + const [userEmailRegistrationInitiateMutation] = + useUserEmailRegistrationInitiateMutation() useEffect(() => { if (phoneNumber && countryCode) { @@ -128,7 +130,12 @@ const PersonalInformation: React.FC = ({ navigation }) => { } return ( - + = ({ navigation }) => { countryCode={countryCode} phoneNumber={phoneNumber} errorMsg={phoneNumberErr || error} - disabled={!!id} + disabled={!!data?.me?.phone} supportedCountries={supportedCountries as CountryCode[]} setCountryCode={(val) => { setPhoneNumberErr(undefined) @@ -164,6 +171,7 @@ const PersonalInformation: React.FC = ({ navigation }) => { placeholder="your.email@example.com" value={email} onChangeText={(val) => dispatch(setPersonalInfo({ email: val }))} + autoCapitalize={"none"} /> diff --git a/app/screens/account-upgrade-flow/Success.tsx b/app/screens/account-upgrade-flow/Success.tsx index e032641db..6e16d03dc 100644 --- a/app/screens/account-upgrade-flow/Success.tsx +++ b/app/screens/account-upgrade-flow/Success.tsx @@ -18,6 +18,9 @@ import { useI18nContext } from "@app/i18n/i18n-react" import { useAppDispatch, useAppSelector } from "@app/store/redux" import { resetAccountUpgrade } from "@app/store/redux/slices/accountUpgradeSlice" +// gql +import { AccountLevel } from "@app/graphql/generated" + type Props = StackScreenProps const Success: React.FC = ({ navigation }) => { @@ -45,7 +48,7 @@ const Success: React.FC = ({ navigation }) => { })} - {accountType === "merchant" && ( + {accountType === AccountLevel.Three && ( = ({ navigation, route }) => { const { LL } = useI18nContext() const { saveToken } = useAppConfig() const { toggleActivityIndicator } = useActivityIndicator() - const { submitAccountUpgrade, fetchAccountUpgrade } = useAccountUpgrade() const [code, setCode] = useState() const [errorMsg, setErrorMsg] = useState() @@ -51,21 +54,16 @@ const Validation: React.FC = ({ navigation, route }) => { if (authToken) { saveToken(authToken) } - await fetchAccountUpgrade(phone) - if (accountType === "personal") { - const res = await submitAccountUpgrade() - if (res) navigation.replace("AccountUpgradeSuccess") - else alert("Something went wrong. Please, try again later.") + if (accountType === AccountLevel.One) { + navigation.replace("AccountUpgradeSuccess") } else { navigation.replace("BusinessInformation") } } else { - console.log(">>>>>>>>>>>>>>>>>", data?.userLoginUpgrade) setErrorMsg(data?.userLoginUpgrade.errors[0].message) } setCode("") } catch (err) { - console.log("ERROR>>>>>>>>>", err) setCode("") } }, @@ -84,7 +82,12 @@ const Validation: React.FC = ({ navigation, route }) => { } return ( - + @@ -99,6 +102,7 @@ const Validation: React.FC = ({ navigation, route }) => { value={code} errorMsg={errorMsg} onChangeText={onChangeText} + keyboardType="number-pad" /> diff --git a/app/store/redux/slices/accountUpgradeSlice.ts b/app/store/redux/slices/accountUpgradeSlice.ts index 6cfa27546..ba44596c2 100644 --- a/app/store/redux/slices/accountUpgradeSlice.ts +++ b/app/store/redux/slices/accountUpgradeSlice.ts @@ -1,10 +1,11 @@ +import { AccountLevel } from "@app/graphql/generated" import { createSlice } from "@reduxjs/toolkit" import { CountryCode } from "libphonenumber-js" import { Asset } from "react-native-image-picker" interface AccountUpgradeSlice { id?: string - accountType?: "personal" | "business" | "merchant" + accountType?: AccountLevel upgradeCompleted?: boolean personalInfo: { fullName?: string From 11840ae3cde6730810034ccf63eb4fb5c035e37f Mon Sep 17 00:00:00 2001 From: Nodirbek Jamoldinov Date: Thu, 8 Jan 2026 20:10:58 +0500 Subject: [PATCH 74/81] revert back AddressField component to use google places api --- .../account-upgrade-flow/AddressField.tsx | 157 +++++++++--------- 1 file changed, 77 insertions(+), 80 deletions(-) diff --git a/app/components/account-upgrade-flow/AddressField.tsx b/app/components/account-upgrade-flow/AddressField.tsx index 60a3d3436..8bc789c04 100644 --- a/app/components/account-upgrade-flow/AddressField.tsx +++ b/app/components/account-upgrade-flow/AddressField.tsx @@ -1,11 +1,16 @@ -import React, { useState } from "react" -import { ActivityIndicator, TextInput, View } from "react-native" +import React, { useEffect, useRef, useState } from "react" +import { Modal, Platform, TouchableOpacity, View } from "react-native" import { makeStyles, Text, useTheme } from "@rneui/themed" +import { + GooglePlacesAutocomplete, + GooglePlacesAutocompleteRef, +} from "react-native-google-places-autocomplete" // components import { PrimaryBtn } from "../buttons" -import { useBusinessAddressEnrichLazyQuery } from "@app/graphql/generated" +// env +import { GOOGLE_PLACE_API_KEY } from "@env" type Props = { label: string @@ -25,79 +30,71 @@ const AddressField: React.FC = ({ const styles = useStyles() const { colors } = useTheme().theme - const [inputValue, setInputValue] = useState(value || "") - const [loading, setLoading] = useState(false) - const [enrichError, setEnrichError] = useState() + const ref = useRef(null) + const [isFocused, setIsFocused] = useState(false) + const [isVisible, setIsVisible] = useState(false) - const [enrichAddress] = useBusinessAddressEnrichLazyQuery() - - const handleEnrichAddress = async () => { - if (!inputValue || inputValue.length < 3) { - setEnrichError("Please enter a valid address") - return - } - - setLoading(true) - setEnrichError(undefined) - - try { - const { data } = await enrichAddress({ - variables: { address: inputValue }, - }) - - if (data?.businessAddressEnrich?.errors?.length) { - setEnrichError(data.businessAddressEnrich.errors[0].message) - } else if (data?.businessAddressEnrich?.formattedAddress) { - onAddressSelect( - data.businessAddressEnrich.formattedAddress, - data.businessAddressEnrich.latitude ?? undefined, - data.businessAddressEnrich.longitude ?? undefined, - ) - } - } catch (err) { - console.log("Address enrichment error:", err) - setEnrichError("Failed to validate address. Please try again.") - } finally { - setLoading(false) + useEffect(() => { + if (isVisible && ref.current) { + ref.current.focus() } - } + }, [isVisible, ref.current]) return ( {label} - - { - setInputValue(text) - setEnrichError(undefined) - }} - /> - - {(errorMsg || enrichError) && ( + setIsVisible(true)}> + + {!!value ? value : placeholder} + + + {!!errorMsg && ( - {errorMsg || enrichError} + {errorMsg} )} - {value && ( - - - Selected: {value} - + setIsVisible(false)} + > + + console.log("Google places auto complete", err)} + onNotFound={() => console.log("Google places auto complete not found")} + fetchDetails={true} + onPress={(data, details) => { + setIsVisible(false) + onAddressSelect( + data.description, + details?.geometry.location.lat, + details?.geometry.location.lng, + ) + }} + query={{ + key: GOOGLE_PLACE_API_KEY, + language: "en", + }} + styles={{ + textInput: [ + styles.googlePlace, + isFocused ? { borderColor: colors.primary } : {}, + ], + }} + textInputProps={{ + onFocus: () => setIsFocused(true), + onBlur: () => setIsFocused(false), + }} + /> + setIsVisible(false)} /> - )} - - {loading && } + ) } @@ -108,32 +105,32 @@ const useStyles = makeStyles(({ colors }) => ({ container: { marginBottom: 15, }, - inputContainer: { - marginTop: 5, - marginBottom: 2, - }, input: { paddingHorizontal: 15, paddingVertical: 20, + marginTop: 5, + marginBottom: 2, borderRadius: 10, borderWidth: 1, borderColor: colors.grey4, backgroundColor: colors.grey5, - fontSize: 16, - fontFamily: "Sora-Regular", - color: colors.black, - width: "100%", }, - btn: { - marginTop: 10, + modal: { + flex: 1, + backgroundColor: colors.white, + padding: 20, }, - loader: { - marginTop: 10, - }, - selectedAddress: { + googlePlace: { + height: Platform.OS === "ios" ? 51 : 60, + paddingHorizontal: 15, + padding: 20, marginTop: 5, - padding: 8, + marginBottom: 15, + borderWidth: 1, + borderRadius: 10, + borderColor: colors.grey4, backgroundColor: colors.grey5, - borderRadius: 8, + fontSize: 16, + fontFamily: "Sora-Regular", }, })) From 9ead806e68004e3e5260b331e11b9ee6057f2746 Mon Sep 17 00:00:00 2001 From: Nodirbek Jamoldinov Date: Thu, 15 Jan 2026 14:20:46 +0500 Subject: [PATCH 75/81] add an logic to upload id document to digital ocean s3 bucket using pre-signed url and complete account upgrade request api integration --- app/hooks/useAccountUpgrade.tsx | 172 ++++++++++++++++++-------------- 1 file changed, 99 insertions(+), 73 deletions(-) diff --git a/app/hooks/useAccountUpgrade.tsx b/app/hooks/useAccountUpgrade.tsx index 85ec6a341..df4e520c6 100644 --- a/app/hooks/useAccountUpgrade.tsx +++ b/app/hooks/useAccountUpgrade.tsx @@ -1,102 +1,128 @@ -import { Platform } from "react-native" -import DeviceInfo from "react-native-device-info" -import { parsePhoneNumber } from "libphonenumber-js" -import RNFS from "react-native-fs" - -// hooks import { useActivityIndicator } from "./useActivityIndicator" - -// store -import { useAppDispatch, useAppSelector } from "@app/store/redux" +import { useAppSelector } from "@app/store/redux" import { - setAccountUpgrade, - setBankInfo, - setBusinessInfo, - setPersonalInfo, -} from "@app/store/redux/slices/accountUpgradeSlice" + useBusinessAccountUpgradeRequestMutation, + HomeAuthedDocument, + useFileUploadUrlGenerateMutation, +} from "@app/graphql/generated" -import { useBusinessAccountUpgradeRequestMutation, HomeAuthedDocument } from "@app/graphql/generated" +type UpgradeResult = { + success: boolean + errors?: string[] +} export const useAccountUpgrade = () => { - const dispatch = useAppDispatch() - const { userData } = useAppSelector((state) => state.user) - const { id, accountType, personalInfo, businessInfo, bankInfo } = useAppSelector( + const { toggleActivityIndicator } = useActivityIndicator() + const { accountType, personalInfo, businessInfo, bankInfo } = useAppSelector( (state) => state.accountUpgrade, ) - const { toggleActivityIndicator } = useActivityIndicator() - const [businessAccountUpgradeRequestMutation] = useBusinessAccountUpgradeRequestMutation({ + const [generateFileUploadUrl] = useFileUploadUrlGenerateMutation() + const [requestAccountUpgrade] = useBusinessAccountUpgradeRequestMutation({ refetchQueries: [HomeAuthedDocument], }) - const fetchAccountUpgrade = async (phone?: string) => { - // Note: This function previously fetched from Supabase - // Now we rely on the GraphQL backend data from queries - // This function may no longer be needed, but keeping for compatibility - console.log("fetchAccountUpgrade called - functionality moved to GraphQL queries") - } + const uploadIdDocument = async (): Promise => { + const { idDocument } = bankInfo + if (!idDocument?.fileName || !idDocument.type || !idDocument.uri) { + return null + } - const submitAccountUpgrade = async () => { - toggleActivityIndicator(true) - const readableVersion = DeviceInfo.getReadableVersion() - const parsedPhoneNumber = parsePhoneNumber( - personalInfo.phoneNumber || "", - personalInfo.countryCode, - ) + const { data } = await generateFileUploadUrl({ + variables: { + input: { + filename: idDocument.fileName, + contentType: idDocument.type, + }, + }, + }) - let idDocumentBase64 = undefined - if (accountType === "merchant" && bankInfo.idDocument) { - try { - // Convert image file to base64 - const base64String = await RNFS.readFile( - bankInfo.idDocument.uri, - "base64", - ) - idDocumentBase64 = `data:${bankInfo.idDocument.type};base64,${base64String}` - } catch (err) { - console.log("Error converting ID document to base64:", err) - } + if (!data?.fileUploadUrlGenerate.uploadUrl) { + throw new Error("Failed to generate upload URL to upload ID Document") } - const input = { - accountType: accountType, - fullName: personalInfo.fullName, - phone: parsedPhoneNumber.number, - email: personalInfo.email, - businessName: businessInfo.businessName, - businessAddress: businessInfo.businessAddress, - latitude: businessInfo.lat, - longitude: businessInfo.lng, - terminalRequested: businessInfo.terminalRequested, - bankName: bankInfo.bankName, - bankBranch: bankInfo.bankBranch, - bankAccountType: bankInfo.bankAccountType, - accountCurrency: bankInfo.currency, - bankAccountNumber: bankInfo.accountNumber, - idDocumentBase64: idDocumentBase64, - clientVersion: readableVersion, - deviceInfo: Platform.OS, - } + await uploadFileToS3( + data.fileUploadUrlGenerate.uploadUrl, + idDocument.uri, + idDocument.type, + ) + + return data.fileUploadUrlGenerate.fileUrl ?? null + } + + const submitAccountUpgrade = async (): Promise => { + toggleActivityIndicator(true) try { - const { data } = await businessAccountUpgradeRequestMutation({ + const idDocumentUrl = await uploadIdDocument() + + const input = { + accountNumber: Number(bankInfo.accountNumber), + accountType: bankInfo.bankAccountType, + bankBranch: bankInfo.bankBranch, + bankName: bankInfo.bankName, + businessAddress: businessInfo.businessAddress, + businessName: businessInfo.businessName, + currency: bankInfo.currency, + email: personalInfo.email, + fullName: personalInfo.fullName || "", + idDocument: idDocumentUrl, + level: accountType || "ONE", + terminalRequested: businessInfo.terminalRequested, + } + + const { data } = await requestAccountUpgrade({ variables: { input }, }) - toggleActivityIndicator(false) - if (data?.businessAccountUpgradeRequest?.errors?.length) { - console.error("Upgrade request errors:", data.businessAccountUpgradeRequest.errors) - return false + return { + success: false, + errors: data.businessAccountUpgradeRequest.errors.map((e) => e.message), + } } - return data?.businessAccountUpgradeRequest?.success ?? false + return { + success: data?.businessAccountUpgradeRequest?.success ?? false, + } } catch (err) { - console.log("BUSINESS ACCOUNT UPGRADE REQUEST ERR: ", err) + console.error("Account upgrade failed:", err) + return { + success: false, + errors: [err instanceof Error ? err.message : "Unknown error occurred"], + } + } finally { toggleActivityIndicator(false) - return false } } - return { fetchAccountUpgrade, submitAccountUpgrade } + return { submitAccountUpgrade } +} + +const uploadFileToS3 = async ( + uploadUrl: string, + fileUri: string, + contentType: string, +): Promise => { + const blob = await fetch(fileUri).then((res) => res.blob()) + + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest() + xhr.open("PUT", uploadUrl, true) + xhr.setRequestHeader("Content-Type", contentType) + + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + if (xhr.status >= 200 && xhr.status < 300) { + resolve() + } else { + reject(new Error(`Upload ID Document failed with status ${xhr.status}`)) + } + } + } + + xhr.onerror = () => reject(new Error("Network error during upload")) + + xhr.send(blob) + }) } From a64656eef370e805ca56401e653d4f27931bdd0a Mon Sep 17 00:00:00 2001 From: Nodirbek Jamoldinov Date: Thu, 15 Jan 2026 14:51:40 +0500 Subject: [PATCH 76/81] show proper error message on the BankInformation screen if it fails and refactor the account upgrade request flow --- app/screens/account-upgrade-flow/AccountType.tsx | 5 ----- app/screens/account-upgrade-flow/BankInformation.tsx | 4 ++-- app/screens/account-upgrade-flow/Success.tsx | 9 ++++----- app/store/redux/slices/accountUpgradeSlice.ts | 4 ++-- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/app/screens/account-upgrade-flow/AccountType.tsx b/app/screens/account-upgrade-flow/AccountType.tsx index 37665c931..2946d94fc 100644 --- a/app/screens/account-upgrade-flow/AccountType.tsx +++ b/app/screens/account-upgrade-flow/AccountType.tsx @@ -26,11 +26,6 @@ const AccountType: React.FC = ({ navigation }) => { const { colors } = useTheme().theme const { LL } = useI18nContext() const { currentLevel } = useLevel() - const { fetchAccountUpgrade } = useAccountUpgrade() - - useEffect(() => { - fetchAccountUpgrade() - }, []) const onPress = (accountType: string) => { const numOfSteps = diff --git a/app/screens/account-upgrade-flow/BankInformation.tsx b/app/screens/account-upgrade-flow/BankInformation.tsx index 6f85382d5..0faa44c10 100644 --- a/app/screens/account-upgrade-flow/BankInformation.tsx +++ b/app/screens/account-upgrade-flow/BankInformation.tsx @@ -99,8 +99,8 @@ const BankInformation: React.FC = ({ navigation }) => { if (!hasError) { const res = await submitAccountUpgrade() - if (res) navigation.navigate("AccountUpgradeSuccess") - else alert("Something went wrong. Please, try again later.") + if (res.success) navigation.navigate("AccountUpgradeSuccess") + else alert(res.errors) } } const isOptional = accountType === AccountLevel.Two diff --git a/app/screens/account-upgrade-flow/Success.tsx b/app/screens/account-upgrade-flow/Success.tsx index 6e16d03dc..f73b7de12 100644 --- a/app/screens/account-upgrade-flow/Success.tsx +++ b/app/screens/account-upgrade-flow/Success.tsx @@ -15,24 +15,23 @@ import Account from "@app/assets/illustrations/account.svg" import { useI18nContext } from "@app/i18n/i18n-react" // store -import { useAppDispatch, useAppSelector } from "@app/store/redux" -import { resetAccountUpgrade } from "@app/store/redux/slices/accountUpgradeSlice" +import { useAppSelector } from "@app/store/redux" // gql import { AccountLevel } from "@app/graphql/generated" type Props = StackScreenProps +const accountTypeLabel = { ONE: "PERSONAL", TWO: "PRO", THREE: "MERCHANT" } + const Success: React.FC = ({ navigation }) => { const styles = useStyles() const { colors } = useTheme().theme const { LL } = useI18nContext() - const dispatch = useAppDispatch() const { accountType } = useAppSelector((state) => state.accountUpgrade) const onComplete = () => { - dispatch(resetAccountUpgrade()) navigation.reset({ index: 0, routes: [{ name: "Primary" }], @@ -44,7 +43,7 @@ const Success: React.FC = ({ navigation }) => { {LL.AccountUpgrade.successTitle({ - accountType: accountType?.toUpperCase() || "", + accountType: accountTypeLabel[accountType as keyof typeof accountTypeLabel], })} diff --git a/app/store/redux/slices/accountUpgradeSlice.ts b/app/store/redux/slices/accountUpgradeSlice.ts index ba44596c2..f377c9d98 100644 --- a/app/store/redux/slices/accountUpgradeSlice.ts +++ b/app/store/redux/slices/accountUpgradeSlice.ts @@ -5,7 +5,7 @@ import { Asset } from "react-native-image-picker" interface AccountUpgradeSlice { id?: string - accountType?: AccountLevel + accountType: AccountLevel upgradeCompleted?: boolean personalInfo: { fullName?: string @@ -35,7 +35,7 @@ interface AccountUpgradeSlice { const initialState: AccountUpgradeSlice = { id: undefined, - accountType: undefined, + accountType: "ONE", upgradeCompleted: undefined, personalInfo: { fullName: undefined, From 93900d9a113c65be895ea87b6d460798b316d76e Mon Sep 17 00:00:00 2001 From: Nodirbek Jamoldinov Date: Fri, 23 Jan 2026 23:51:31 +0500 Subject: [PATCH 77/81] update upgrade-trial-account component to use new account upgrade flow --- app/i18n/en/index.ts | 2 +- app/i18n/i18n-types.ts | 8 ++-- app/i18n/raw-i18n/source/en.json | 2 +- .../settings/upgrade-trial-account.tsx | 41 ++++--------------- 4 files changed, 15 insertions(+), 38 deletions(-) diff --git a/app/i18n/en/index.ts b/app/i18n/en/index.ts index f4cc00e32..4d6f6c07e 100644 --- a/app/i18n/en/index.ts +++ b/app/i18n/en/index.ts @@ -1078,7 +1078,7 @@ const en: BaseTranslation = { increaseLimits: "Increase your limits", spendingLimits: "Spending Limits", spendingLimitsDescription: "The spending limits shown on this page are denominated in USD. For your convenience, we convert these limits into your local currency based on current foreign exchange rates. Please note that the displayed local currency amount may fluctuate as exchange rates are updated in real-time.", - requestBusiness: "Request Business Account", + requestUpgrade: "Request Upgrade", }, TransactionScreen: { noTransaction: "No transaction to show", diff --git a/app/i18n/i18n-types.ts b/app/i18n/i18n-types.ts index 9ae23fc15..06c2c1fd3 100644 --- a/app/i18n/i18n-types.ts +++ b/app/i18n/i18n-types.ts @@ -3491,9 +3491,9 @@ type RootTranslation = { */ spendingLimitsDescription: string /** - * R​e​q​u​e​s​t​ ​B​u​s​i​n​e​s​s​ ​A​c​c​o​u​n​t + * Request Upgrade */ - requestBusiness: string + requestUpgrade: string } TransactionScreen: { /** @@ -8515,9 +8515,9 @@ export type TranslationFunctions = { */ spendingLimitsDescription: () => LocalizedString /** - * Request Business Account + * Request Upgrade */ - requestBusiness: () => LocalizedString + requestUpgrade: () => LocalizedString } TransactionScreen: { /** diff --git a/app/i18n/raw-i18n/source/en.json b/app/i18n/raw-i18n/source/en.json index 981a84170..f3f0862b5 100644 --- a/app/i18n/raw-i18n/source/en.json +++ b/app/i18n/raw-i18n/source/en.json @@ -1004,7 +1004,7 @@ "increaseLimits": "Increase your limits", "spendingLimits": "Spending Limits", "spendingLimitsDescription": "The spending limits shown on this page are denominated in USD. For your convenience, we convert these limits into your local currency based on current foreign exchange rates. Please note that the displayed local currency amount may fluctuate as exchange rates are updated in real-time.", - "requestBusiness": "Request Business Account" + "requestUpgrade": "Request Upgrade" }, "TransactionScreen": { "noTransaction": "No transaction to show", diff --git a/app/screens/settings-screen/account/settings/upgrade-trial-account.tsx b/app/screens/settings-screen/account/settings/upgrade-trial-account.tsx index 9d096c7d1..8c585d4ab 100644 --- a/app/screens/settings-screen/account/settings/upgrade-trial-account.tsx +++ b/app/screens/settings-screen/account/settings/upgrade-trial-account.tsx @@ -1,11 +1,10 @@ import { useState } from "react" import { View } from "react-native" import { makeStyles, Text } from "@rneui/themed" +import { StackNavigationProp } from "@react-navigation/stack" +import { RootStackParamList } from "@app/navigation/stack-param-lists" // components -import ContactModal, { - SupportChannels, -} from "@app/components/contact-modal/contact-modal" import { PrimaryBtn } from "@app/components/buttons" import { GaloyIcon } from "@app/components/atomic/galoy-icon" import { UpgradeAccountModal } from "@app/components/upgrade-account-modal" @@ -15,24 +14,20 @@ import { GaloySecondaryButton } from "@app/components/atomic/galoy-secondary-but import { useShowWarningSecureAccount } from "../show-warning-secure-account-hook" import { AccountLevel, useLevel } from "@app/graphql/level-context" import { useI18nContext } from "@app/i18n/i18n-react" -import { useAppConfig } from "@app/hooks" +import { useNavigation } from "@react-navigation/native" export const UpgradeTrialAccount: React.FC = () => { const styles = useStyles() const { LL } = useI18nContext() const { currentLevel } = useLevel() - const { appConfig } = useAppConfig() + const navigation = useNavigation>() const hasBalance = useShowWarningSecureAccount() const [upgradeAccountModalVisible, setUpgradeAccountModalVisible] = useState(false) - const [isContactModalVisible, setIsContactModalVisible] = useState(false) - - const { name: bankName } = appConfig.galoyInstance const closeUpgradeAccountModal = () => setUpgradeAccountModalVisible(false) const openUpgradeAccountModal = () => setUpgradeAccountModalVisible(true) - const toggleContactModal = () => setIsContactModalVisible(!isContactModalVisible) if (currentLevel === AccountLevel.Zero) { return ( @@ -63,30 +58,12 @@ export const UpgradeTrialAccount: React.FC = () => { ) } else if (currentLevel === AccountLevel.One) { - const messageBody = LL.TransactionLimitsScreen.contactUsMessageBody({ - bankName, - }) - const messageSubject = LL.TransactionLimitsScreen.contactUsMessageSubject() - return ( - <> - - - + navigation.navigate("AccountType")} + /> ) } else { return null From ad047d58c876f7f548396b53d755394febaf760f Mon Sep 17 00:00:00 2001 From: Nodirbek Jamoldinov Date: Fri, 23 Jan 2026 23:53:57 +0500 Subject: [PATCH 78/81] use updated mutation to generate pre-signed url to upload the id document to the digital ocean --- app/graphql/front-end-mutations.ts | 17 +- app/graphql/generated.gql | 36 ++-- app/graphql/generated.ts | 326 ++++++++++++++++++++--------- app/hooks/useAccountUpgrade.tsx | 16 +- 4 files changed, 258 insertions(+), 137 deletions(-) diff --git a/app/graphql/front-end-mutations.ts b/app/graphql/front-end-mutations.ts index a59c6651e..7730dbe1f 100644 --- a/app/graphql/front-end-mutations.ts +++ b/app/graphql/front-end-mutations.ts @@ -166,9 +166,7 @@ gql` amount } } -` -gql` mutation userUpdateNpub($input: UserUpdateNpubInput!) { userUpdateNpub(input: $input) { errors { @@ -180,9 +178,7 @@ gql` } } } -` -gql` mutation userUpdateUsername($input: UserUpdateUsernameInput!) { userUpdateUsername(input: $input) { errors { @@ -194,9 +190,7 @@ gql` } } } -` -gql` mutation businessAccountUpgradeRequest($input: BusinessAccountUpgradeRequestInput!) { businessAccountUpgradeRequest(input: $input) { errors { @@ -206,4 +200,15 @@ gql` success } } + + mutation IdDocumentUploadUrlGenerate($input: IdDocumentUploadUrlGenerateInput!) { + idDocumentUploadUrlGenerate(input: $input) { + errors { + code + message + } + fileKey + uploadUrl + } + } ` diff --git a/app/graphql/generated.gql b/app/graphql/generated.gql index af4f9752d..6d0b446d3 100644 --- a/app/graphql/generated.gql +++ b/app/graphql/generated.gql @@ -81,6 +81,19 @@ fragment TransactionList on TransactionConnection { __typename } +mutation IdDocumentUploadUrlGenerate($input: IdDocumentUploadUrlGenerateInput!) { + idDocumentUploadUrlGenerate(input: $input) { + errors { + code + message + __typename + } + fileKey + uploadUrl + __typename + } +} + mutation MerchantMapSuggest($input: MerchantMapSuggestInput!) { merchantMapSuggest(input: $input) { errors { @@ -890,15 +903,6 @@ query accountScreen { } } -query accountUpgradeRequestStatus { - accountUpgradeRequestStatus { - hasPendingRequest - requestedLevel - errors - __typename - } -} - query addressScreen { me { id @@ -968,20 +972,6 @@ query btcPriceList($range: PriceGraphRange!) { } } -query businessAddressEnrich($address: String!) { - businessAddressEnrich(address: $address) { - formattedAddress - latitude - longitude - errors { - message - code - __typename - } - __typename - } -} - query businessMapMarkers { businessMapMarkers { username diff --git a/app/graphql/generated.ts b/app/graphql/generated.ts index a93b04f02..67e910a53 100644 --- a/app/graphql/generated.ts +++ b/app/graphql/generated.ts @@ -36,7 +36,7 @@ export type Scalars = { EndpointUrl: { input: string; output: string; } /** Feedback shared with our user */ Feedback: { input: string; output: string; } - /** (Positive) Cent amount (1/100 of a dollar) as a float */ + /** Cent amount (1/100 of a dollar) as a float, can be positive or negative */ FractionalCentAmount: { input: number; output: number; } /** Hex-encoded string of 32 bytes */ Hex32Bytes: { input: string; output: string; } @@ -198,13 +198,6 @@ export type AccountUpdateNotificationSettingsPayload = { readonly errors: ReadonlyArray; }; -export type AccountUpgradeRequestStatus = { - readonly __typename: 'AccountUpgradeRequestStatus'; - readonly errors: ReadonlyArray; - readonly hasPendingRequest: Scalars['Boolean']['output']; - readonly requestedLevel?: Maybe; -}; - export type AuthTokenPayload = { readonly __typename: 'AuthTokenPayload'; readonly authToken?: Maybe; @@ -217,7 +210,7 @@ export type BtcWallet = Wallet & { readonly __typename: 'BTCWallet'; readonly accountId: Scalars['ID']['output']; /** A balance stored in BTC. */ - readonly balance: Scalars['SignedAmount']['output']; + readonly balance: Scalars['FractionalCentAmount']['output']; readonly id: Scalars['ID']['output']; readonly lnurlp?: Maybe; /** An unconfirmed incoming onchain balance. */ @@ -254,23 +247,19 @@ export type BuildInformation = { }; export type BusinessAccountUpgradeRequestInput = { - readonly additionalInfo?: InputMaybe; - readonly businessAddress: Scalars['String']['input']; - readonly businessName: Scalars['String']['input']; - readonly businessPhone: Scalars['String']['input']; - readonly businessType: Scalars['String']['input']; + readonly accountNumber?: InputMaybe; + readonly accountType?: InputMaybe; + readonly bankBranch?: InputMaybe; + readonly bankName?: InputMaybe; + readonly businessAddress?: InputMaybe; + readonly businessName?: InputMaybe; + readonly currency?: InputMaybe; + readonly email?: InputMaybe; + readonly fullName: Scalars['String']['input']; + readonly idDocument?: InputMaybe; readonly level: AccountLevel; -}; - -export type BusinessAddressEnrichPayload = { - readonly __typename: 'BusinessAddressEnrichPayload'; - readonly errors: ReadonlyArray; - /** The standardized/formatted address returned by Google Places API */ - readonly formattedAddress?: Maybe; - /** Geographic latitude coordinate */ - readonly latitude?: Maybe; - /** Geographic longitude coordinate */ - readonly longitude?: Maybe; + readonly phoneNumber?: InputMaybe; + readonly terminalRequested?: InputMaybe; }; export type CallbackEndpoint = { @@ -471,6 +460,22 @@ export type GraphQlApplicationError = Error & { readonly path?: Maybe>>; }; +export type IdDocumentUploadUrlGenerateInput = { + /** MIME type (image/jpeg, image/png, image/webp) */ + readonly contentType: Scalars['String']['input']; + /** Original filename */ + readonly filename: Scalars['String']['input']; +}; + +export type IdDocumentUploadUrlPayload = { + readonly __typename: 'IdDocumentUploadUrlPayload'; + readonly errors: ReadonlyArray; + /** Storage key for the uploaded file (use to generate read URLs) */ + readonly fileKey?: Maybe; + /** Pre-signed URL for uploading the ID document directly to storage */ + readonly uploadUrl?: Maybe; +}; + export type InitiateCashoutInput = { /** The id of the offer being executed. */ readonly offerId: Scalars['ID']['input']; @@ -770,6 +775,7 @@ export type Mutation = { readonly captchaRequestAuthCode: SuccessPayload; readonly deviceNotificationTokenCreate: SuccessPayload; readonly feedbackSubmit: SuccessPayload; + readonly idDocumentUploadUrlGenerate: IdDocumentUploadUrlPayload; /** * Start the Cashout process; * User sends USD to Flash via Ibex and receives USD or JMD to bank account. @@ -945,6 +951,11 @@ export type MutationFeedbackSubmitArgs = { }; +export type MutationIdDocumentUploadUrlGenerateArgs = { + input: IdDocumentUploadUrlGenerateInput; +}; + + export type MutationInitiateCashoutArgs = { input: InitiateCashoutInput; }; @@ -1374,12 +1385,10 @@ export type PublicWallet = { export type Query = { readonly __typename: 'Query'; readonly accountDefaultWallet: PublicWallet; - readonly accountUpgradeRequestStatus: AccountUpgradeRequestStatus; readonly beta: Scalars['Boolean']['output']; /** @deprecated Deprecated in favor of realtimePrice */ readonly btcPrice?: Maybe; readonly btcPriceList?: Maybe>>; - readonly businessAddressEnrich: BusinessAddressEnrichPayload; readonly businessMapMarkers: ReadonlyArray; readonly colorScheme: Scalars['String']['output']; readonly currencyList: ReadonlyArray; @@ -1401,6 +1410,7 @@ export type Query = { readonly quizQuestions?: Maybe>>; /** Returns 1 Sat and 1 Usd Cent price for the given currency */ readonly realtimePrice: RealtimePrice; + readonly transactionDetails: TransactionDetailsPayload; /** @deprecated will be migrated to AccountDefaultWalletId */ readonly userDefaultWalletId: Scalars['WalletId']['output']; readonly usernameAvailable?: Maybe; @@ -1423,11 +1433,6 @@ export type QueryBtcPriceListArgs = { }; -export type QueryBusinessAddressEnrichArgs = { - address: Scalars['String']['input']; -}; - - export type QueryIsFlashNpubArgs = { input: IsFlashNpubInput; }; @@ -1472,6 +1477,11 @@ export type QueryRealtimePriceArgs = { }; +export type QueryTransactionDetailsArgs = { + input: TransactionDetailsInput; +}; + + export type QueryUserDefaultWalletIdArgs = { username: Scalars['Username']['input']; }; @@ -1641,6 +1651,60 @@ export type TransactionConnection = { readonly pageInfo: PageInfo; }; +export type TransactionDetails = { + readonly __typename: 'TransactionDetails'; + /** Account ID associated with the transaction */ + readonly accountId?: Maybe; + /** Bitcoin address for onchain transactions */ + readonly address?: Maybe; + /** Transaction amount */ + readonly amount?: Maybe; + /** Number of confirmations for onchain transactions */ + readonly confirmations?: Maybe; + /** Transaction creation timestamp */ + readonly createdAt?: Maybe; + /** Transaction currency */ + readonly currency?: Maybe; + /** Transaction fee */ + readonly fee?: Maybe; + /** Transaction ID */ + readonly id: Scalars['String']['output']; + /** Lightning invoice (bolt11) */ + readonly invoice?: Maybe; + /** Transaction memo/description */ + readonly memo?: Maybe; + /** Lightning payment hash */ + readonly paymentHash?: Maybe; + /** Lightning payment preimage */ + readonly paymentPreimage?: Maybe; + /** Transaction status */ + readonly status?: Maybe; + /** Bitcoin transaction ID for onchain transactions */ + readonly txid?: Maybe; + /** Transaction type (lightning/onchain) */ + readonly type?: Maybe; + /** Transaction last update timestamp */ + readonly updatedAt?: Maybe; + /** Output index for onchain transactions */ + readonly vout?: Maybe; +}; + +export type TransactionDetailsError = { + readonly __typename: 'TransactionDetailsError'; + readonly message: Scalars['String']['output']; +}; + +export type TransactionDetailsInput = { + /** Transaction ID to fetch details for */ + readonly transactionId: Scalars['String']['input']; +}; + +export type TransactionDetailsPayload = { + readonly __typename: 'TransactionDetailsPayload'; + readonly errors: ReadonlyArray; + readonly transactionDetails?: Maybe; +}; + /** An edge in a connection. */ export type TransactionEdge = { readonly __typename: 'TransactionEdge'; @@ -1684,7 +1748,7 @@ export type UpgradePayload = { export type UsdWallet = Wallet & { readonly __typename: 'UsdWallet'; readonly accountId: Scalars['ID']['output']; - readonly balance: Scalars['SignedAmount']['output']; + readonly balance: Scalars['FractionalCentAmount']['output']; readonly id: Scalars['ID']['output']; readonly lnurlp?: Maybe; /** An unconfirmed incoming onchain balance. */ @@ -1942,7 +2006,7 @@ export type UserUpdateUsernamePayload = { /** A generic wallet which stores value in one of our supported currencies. */ export type Wallet = { readonly accountId: Scalars['ID']['output']; - readonly balance: Scalars['SignedAmount']['output']; + readonly balance: Scalars['FractionalCentAmount']['output']; readonly id: Scalars['ID']['output']; readonly lnurlp?: Maybe; readonly pendingIncomingBalance: Scalars['SignedAmount']['output']; @@ -2165,6 +2229,13 @@ export type BusinessAccountUpgradeRequestMutationVariables = Exact<{ export type BusinessAccountUpgradeRequestMutation = { readonly __typename: 'Mutation', readonly businessAccountUpgradeRequest: { readonly __typename: 'SuccessPayload', readonly success?: boolean | null, readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly message: string, readonly code?: string | null }> } }; +export type IdDocumentUploadUrlGenerateMutationVariables = Exact<{ + input: IdDocumentUploadUrlGenerateInput; +}>; + + +export type IdDocumentUploadUrlGenerateMutation = { readonly __typename: 'Mutation', readonly idDocumentUploadUrlGenerate: { readonly __typename: 'IdDocumentUploadUrlPayload', readonly fileKey?: string | null, readonly uploadUrl?: string | null, readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly code?: string | null, readonly message: string }> } }; + export type AuthQueryVariables = Exact<{ [key: string]: never; }>; @@ -2266,17 +2337,12 @@ export type RealtimePriceUnauthedQueryVariables = Exact<{ export type RealtimePriceUnauthedQuery = { readonly __typename: 'Query', readonly realtimePrice: { readonly __typename: 'RealtimePrice', readonly timestamp: number, readonly denominatorCurrency: string, readonly btcSatPrice: { readonly __typename: 'PriceOfOneSatInMinorUnit', readonly base: number, readonly offset: number }, readonly usdCentPrice: { readonly __typename: 'PriceOfOneUsdCentInMinorUnit', readonly base: number, readonly offset: number } } }; -export type BusinessAddressEnrichQueryVariables = Exact<{ - address: Scalars['String']['input']; +export type NpubByUsernameQueryVariables = Exact<{ + username: Scalars['Username']['input']; }>; -export type BusinessAddressEnrichQuery = { readonly __typename: 'Query', readonly businessAddressEnrich: { readonly __typename: 'BusinessAddressEnrichPayload', readonly formattedAddress?: string | null, readonly latitude?: number | null, readonly longitude?: number | null, readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly message: string, readonly code?: string | null }> } }; - -export type AccountUpgradeRequestStatusQueryVariables = Exact<{ [key: string]: never; }>; - - -export type AccountUpgradeRequestStatusQuery = { readonly __typename: 'Query', readonly accountUpgradeRequestStatus: { readonly __typename: 'AccountUpgradeRequestStatus', readonly hasPendingRequest: boolean, readonly requestedLevel?: AccountLevel | null, readonly errors: ReadonlyArray } }; +export type NpubByUsernameQuery = { readonly __typename: 'Query', readonly npubByUsername?: { readonly __typename: 'npubByUsername', readonly npub?: string | null, readonly username?: string | null } | null }; export type RealtimePriceWsSubscriptionVariables = Exact<{ currency: Scalars['DisplayCurrency']['input']; @@ -2285,6 +2351,13 @@ export type RealtimePriceWsSubscriptionVariables = Exact<{ export type RealtimePriceWsSubscription = { readonly __typename: 'Subscription', readonly realtimePrice: { readonly __typename: 'RealtimePricePayload', readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly message: string }>, readonly realtimePrice?: { readonly __typename: 'RealtimePrice', readonly timestamp: number, readonly denominatorCurrency: string, readonly btcSatPrice: { readonly __typename: 'PriceOfOneSatInMinorUnit', readonly base: number, readonly offset: number }, readonly usdCentPrice: { readonly __typename: 'PriceOfOneUsdCentInMinorUnit', readonly base: number, readonly offset: number } } | null } }; +export type TransactionDetailsQueryVariables = Exact<{ + input: TransactionDetailsInput; +}>; + + +export type TransactionDetailsQuery = { readonly __typename: 'Query', readonly transactionDetails: { readonly __typename: 'TransactionDetailsPayload', readonly errors: ReadonlyArray<{ readonly __typename: 'TransactionDetailsError', readonly message: string }>, readonly transactionDetails?: { readonly __typename: 'TransactionDetails', readonly id: string, readonly accountId?: string | null, readonly amount?: number | null, readonly currency?: string | null, readonly status?: string | null, readonly type?: string | null, readonly createdAt?: string | null, readonly updatedAt?: string | null, readonly invoice?: string | null, readonly paymentHash?: string | null, readonly paymentPreimage?: string | null, readonly memo?: string | null, readonly address?: string | null, readonly txid?: string | null, readonly vout?: number | null, readonly confirmations?: number | null, readonly fee?: number | null } | null } }; + export type NetworkQueryVariables = Exact<{ [key: string]: never; }>; @@ -3719,6 +3792,44 @@ export function useBusinessAccountUpgradeRequestMutation(baseOptions?: Apollo.Mu export type BusinessAccountUpgradeRequestMutationHookResult = ReturnType; export type BusinessAccountUpgradeRequestMutationResult = Apollo.MutationResult; export type BusinessAccountUpgradeRequestMutationOptions = Apollo.BaseMutationOptions; +export const IdDocumentUploadUrlGenerateDocument = gql` + mutation IdDocumentUploadUrlGenerate($input: IdDocumentUploadUrlGenerateInput!) { + idDocumentUploadUrlGenerate(input: $input) { + errors { + code + message + } + fileKey + uploadUrl + } +} + `; +export type IdDocumentUploadUrlGenerateMutationFn = Apollo.MutationFunction; + +/** + * __useIdDocumentUploadUrlGenerateMutation__ + * + * To run a mutation, you first call `useIdDocumentUploadUrlGenerateMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useIdDocumentUploadUrlGenerateMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [idDocumentUploadUrlGenerateMutation, { data, loading, error }] = useIdDocumentUploadUrlGenerateMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useIdDocumentUploadUrlGenerateMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(IdDocumentUploadUrlGenerateDocument, options); + } +export type IdDocumentUploadUrlGenerateMutationHookResult = ReturnType; +export type IdDocumentUploadUrlGenerateMutationResult = Apollo.MutationResult; +export type IdDocumentUploadUrlGenerateMutationOptions = Apollo.BaseMutationOptions; export const AuthDocument = gql` query auth { me { @@ -4499,83 +4610,42 @@ export function useRealtimePriceUnauthedLazyQuery(baseOptions?: Apollo.LazyQuery export type RealtimePriceUnauthedQueryHookResult = ReturnType; export type RealtimePriceUnauthedLazyQueryHookResult = ReturnType; export type RealtimePriceUnauthedQueryResult = Apollo.QueryResult; -export const BusinessAddressEnrichDocument = gql` - query businessAddressEnrich($address: String!) { - businessAddressEnrich(address: $address) { - formattedAddress - latitude - longitude - errors { - message - code - } - } -} - `; - -/** - * __useBusinessAddressEnrichQuery__ - * - * To run a query within a React component, call `useBusinessAddressEnrichQuery` and pass it any options that fit your needs. - * When your component renders, `useBusinessAddressEnrichQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useBusinessAddressEnrichQuery({ - * variables: { - * address: // value for 'address' - * }, - * }); - */ -export function useBusinessAddressEnrichQuery(baseOptions: Apollo.QueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(BusinessAddressEnrichDocument, options); - } -export function useBusinessAddressEnrichLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(BusinessAddressEnrichDocument, options); - } -export type BusinessAddressEnrichQueryHookResult = ReturnType; -export type BusinessAddressEnrichLazyQueryHookResult = ReturnType; -export type BusinessAddressEnrichQueryResult = Apollo.QueryResult; -export const AccountUpgradeRequestStatusDocument = gql` - query accountUpgradeRequestStatus { - accountUpgradeRequestStatus { - hasPendingRequest - requestedLevel - errors +export const NpubByUsernameDocument = gql` + query npubByUsername($username: Username!) { + npubByUsername(username: $username) { + npub + username } } `; /** - * __useAccountUpgradeRequestStatusQuery__ + * __useNpubByUsernameQuery__ * - * To run a query within a React component, call `useAccountUpgradeRequestStatusQuery` and pass it any options that fit your needs. - * When your component renders, `useAccountUpgradeRequestStatusQuery` returns an object from Apollo Client that contains loading, error, and data properties + * To run a query within a React component, call `useNpubByUsernameQuery` and pass it any options that fit your needs. + * When your component renders, `useNpubByUsernameQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example - * const { data, loading, error } = useAccountUpgradeRequestStatusQuery({ + * const { data, loading, error } = useNpubByUsernameQuery({ * variables: { + * username: // value for 'username' * }, * }); */ -export function useAccountUpgradeRequestStatusQuery(baseOptions?: Apollo.QueryHookOptions) { +export function useNpubByUsernameQuery(baseOptions: Apollo.QueryHookOptions) { const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(AccountUpgradeRequestStatusDocument, options); + return Apollo.useQuery(NpubByUsernameDocument, options); } -export function useAccountUpgradeRequestStatusLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { +export function useNpubByUsernameLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(AccountUpgradeRequestStatusDocument, options); + return Apollo.useLazyQuery(NpubByUsernameDocument, options); } -export type AccountUpgradeRequestStatusQueryHookResult = ReturnType; -export type AccountUpgradeRequestStatusLazyQueryHookResult = ReturnType; -export type AccountUpgradeRequestStatusQueryResult = Apollo.QueryResult; +export type NpubByUsernameQueryHookResult = ReturnType; +export type NpubByUsernameLazyQueryHookResult = ReturnType; +export type NpubByUsernameQueryResult = Apollo.QueryResult; export const RealtimePriceWsDocument = gql` subscription realtimePriceWs($currency: DisplayCurrency!) { realtimePrice(input: {currency: $currency}) { @@ -4620,6 +4690,62 @@ export function useRealtimePriceWsSubscription(baseOptions: Apollo.SubscriptionH } export type RealtimePriceWsSubscriptionHookResult = ReturnType; export type RealtimePriceWsSubscriptionResult = Apollo.SubscriptionResult; +export const TransactionDetailsDocument = gql` + query transactionDetails($input: TransactionDetailsInput!) { + transactionDetails(input: $input) { + errors { + message + } + transactionDetails { + id + accountId + amount + currency + status + type + createdAt + updatedAt + invoice + paymentHash + paymentPreimage + memo + address + txid + vout + confirmations + fee + } + } +} + `; + +/** + * __useTransactionDetailsQuery__ + * + * To run a query within a React component, call `useTransactionDetailsQuery` and pass it any options that fit your needs. + * When your component renders, `useTransactionDetailsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useTransactionDetailsQuery({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useTransactionDetailsQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(TransactionDetailsDocument, options); + } +export function useTransactionDetailsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(TransactionDetailsDocument, options); + } +export type TransactionDetailsQueryHookResult = ReturnType; +export type TransactionDetailsLazyQueryHookResult = ReturnType; +export type TransactionDetailsQueryResult = Apollo.QueryResult; export const NetworkDocument = gql` query network { globals { diff --git a/app/hooks/useAccountUpgrade.tsx b/app/hooks/useAccountUpgrade.tsx index df4e520c6..d4b55151d 100644 --- a/app/hooks/useAccountUpgrade.tsx +++ b/app/hooks/useAccountUpgrade.tsx @@ -3,7 +3,7 @@ import { useAppSelector } from "@app/store/redux" import { useBusinessAccountUpgradeRequestMutation, HomeAuthedDocument, - useFileUploadUrlGenerateMutation, + useIdDocumentUploadUrlGenerateMutation, } from "@app/graphql/generated" type UpgradeResult = { @@ -17,7 +17,7 @@ export const useAccountUpgrade = () => { (state) => state.accountUpgrade, ) - const [generateFileUploadUrl] = useFileUploadUrlGenerateMutation() + const [generateIdDocumentUploadUrl] = useIdDocumentUploadUrlGenerateMutation() const [requestAccountUpgrade] = useBusinessAccountUpgradeRequestMutation({ refetchQueries: [HomeAuthedDocument], }) @@ -28,7 +28,7 @@ export const useAccountUpgrade = () => { return null } - const { data } = await generateFileUploadUrl({ + const { data } = await generateIdDocumentUploadUrl({ variables: { input: { filename: idDocument.fileName, @@ -37,24 +37,24 @@ export const useAccountUpgrade = () => { }, }) - if (!data?.fileUploadUrlGenerate.uploadUrl) { + if (!data?.idDocumentUploadUrlGenerate.uploadUrl) { throw new Error("Failed to generate upload URL to upload ID Document") } await uploadFileToS3( - data.fileUploadUrlGenerate.uploadUrl, + data.idDocumentUploadUrlGenerate.uploadUrl, idDocument.uri, idDocument.type, ) - return data.fileUploadUrlGenerate.fileUrl ?? null + return data.idDocumentUploadUrlGenerate.fileKey ?? null } const submitAccountUpgrade = async (): Promise => { toggleActivityIndicator(true) try { - const idDocumentUrl = await uploadIdDocument() + const idDocument = await uploadIdDocument() const input = { accountNumber: Number(bankInfo.accountNumber), @@ -66,7 +66,7 @@ export const useAccountUpgrade = () => { currency: bankInfo.currency, email: personalInfo.email, fullName: personalInfo.fullName || "", - idDocument: idDocumentUrl, + idDocument: idDocument, level: accountType || "ONE", terminalRequested: businessInfo.terminalRequested, } From bc061045036edcf4a9d0042e8d7d5250a3c9ec04 Mon Sep 17 00:00:00 2001 From: Nodirbek Jamoldinov Date: Tue, 27 Jan 2026 15:57:02 +0500 Subject: [PATCH 79/81] remove TestTransaction screen and clean up the flow --- app/i18n/en/index.ts | 5 - app/i18n/i18n-types.ts | 194 +----------------- app/i18n/raw-i18n/source/en.json | 4 - app/navigation/root-navigator.tsx | 6 - app/navigation/stack-param-lists.ts | 1 - app/screens/account-upgrade-flow/Success.tsx | 14 -- .../account-upgrade-flow/TestTransaction.tsx | 79 ------- app/screens/account-upgrade-flow/index.ts | 2 - 8 files changed, 3 insertions(+), 302 deletions(-) delete mode 100644 app/screens/account-upgrade-flow/TestTransaction.tsx diff --git a/app/i18n/en/index.ts b/app/i18n/en/index.ts index 4d6f6c07e..72dd9a6f5 100644 --- a/app/i18n/en/index.ts +++ b/app/i18n/en/index.ts @@ -1502,11 +1502,6 @@ const en: BaseTranslation = { accountNumPlaceholder: "Enter your account number", uploadId: "Upload ID Document", successTitle: "You successfully requested to upgrade your account to {accountType: string}", - successDesc: "* Please enter the test transaction amount to confirm your bank details.", - transactionVerification: "Transaction Verification", - transactionTitle: "To complete upgrading your account to MERCHANT, enter the test transaction amount we sent to your bank account {accountNum: string} to confirm your bank details.", - transactionAmount: "Transaction amount", - } } diff --git a/app/i18n/i18n-types.ts b/app/i18n/i18n-types.ts index 06c2c1fd3..9f1e15e0d 100644 --- a/app/i18n/i18n-types.ts +++ b/app/i18n/i18n-types.ts @@ -4597,7 +4597,6 @@ type RootTranslation = { * T​r​a​n​s​a​c​t​i​o​n​ ​I​D */ txId: string -<<<<<<< HEAD }, AccountUpgrade: { /** @@ -4737,23 +4736,6 @@ type RootTranslation = { * @param {string} accountType */ successTitle: RequiredParams<'accountType'> - /** - * * Please enter the test transaction amount to confirm your bank details. - */ - successDesc: string - /** - * * Transaction Verification - */ - transactionVerification: string - /** - * To complete upgrading your account to MERCHANT, enter the test transaction amount we sent to your bank account {accountNum} to confirm your bank details. - * @param {string} accountNum - */ - transactionTitle: RequiredParams<'accountNum'> - /** - * Transaction amount - */ - transactionAmount: string } Nostr: { /** @@ -4960,163 +4942,8 @@ type RootTranslation = { goToSettings: string } } - AccountUpgrade: { - /** - * A​c​c​o​u​n​t​ ​T​y​p​e - */ - accountType: string - /** - * P​e​r​s​o​n​a​l - */ - personal: string - /** - * S​e​c​u​r​e​ ​y​o​u​r​ ​w​a​l​l​e​t​ ​w​i​t​h​ ​p​h​o​n​e​ ​a​n​d​ ​e​m​a​i​l​.​ ​S​t​a​y​ ​s​a​f​e​ ​a​n​d​ ​r​e​c​o​v​e​r​ ​e​a​s​i​l​y​ ​i​f​ ​n​e​e​d​e​d - */ - personalDesc: string - /** - * P​r​o - */ - pro: string - /** - * A​c​c​e​p​t​ ​p​a​y​m​e​n​t​s​ ​a​n​d​ ​g​e​t​ ​d​i​s​c​o​v​e​r​e​d​ ​o​n​ ​t​h​e​ ​m​a​p​.​ ​R​e​q​u​i​r​e​s​ ​a​ ​b​u​s​i​n​e​s​s​ ​n​a​m​e​ ​a​n​d​ ​l​o​c​a​t​i​o​n​. - */ - proDesc: string - /** - * M​e​r​c​h​a​n​t - */ - merchant: string - /** - * G​i​v​e​ ​r​e​w​a​r​d​s​,​ ​a​p​p​e​a​r​ ​o​n​ ​t​h​e​ ​m​a​p​,​ ​a​n​d​ ​s​e​t​t​l​e​ ​t​o​ ​y​o​u​r​ ​b​a​n​k​.​ ​I​D​ ​a​n​d​ ​b​a​n​k​ ​i​n​f​o​ ​r​e​q​u​i​r​e​d​. - */ - merchantDesc: string - /** - * P​e​r​s​o​n​a​l​ ​I​n​f​o​r​m​a​t​i​o​n - */ - personalInfo: string - /** - * F​u​l​l​ ​n​a​m​e - */ - fullName: string - /** - * P​h​o​n​e​ ​N​u​m​b​e​r - */ - phoneNumber: string - /** - * E​m​a​i​l​ ​A​d​d​r​e​s​s - */ - email: string - /** - * ​(​O​p​t​i​o​n​a​l​) - */ - optional: string - /** - * V​a​l​i​d​a​t​i​o​n - */ - validation: string - /** - * V​a​l​i​d​a​t​i​o​n​ ​c​o​d​e - */ - validationCode: string - /** - * B​u​s​i​n​e​s​s​ ​I​n​f​o​r​m​a​t​i​o​n - */ - businessInfo: string - /** - * B​u​s​i​n​e​s​s​ ​N​a​m​e - */ - businessName: string - /** - * E​n​t​e​r​ ​y​o​u​r​ ​b​u​s​i​n​e​s​s​ ​n​a​m​e - */ - businessNamePlaceholder: string - /** - * B​u​s​i​n​e​s​s​ ​A​d​d​r​e​s​s - */ - businessAddress: string - /** - * E​n​t​e​r​ ​y​o​u​r​ ​b​u​s​i​n​e​s​s​ ​a​d​d​r​e​s​s - */ - businessAddressPlaceholder: string - /** - * D​o​ ​y​o​u​ ​w​a​n​t​ ​a​ ​F​l​a​s​h​ ​t​e​r​m​i​n​a​l​? - */ - flashTerminal: string - /** - * A​ ​F​l​a​s​h​ ​T​e​r​m​i​n​a​l​ ​i​s​ ​a​ ​s​m​a​r​t​ ​d​e​v​i​c​e​ ​t​h​a​t​ ​c​a​n​ ​a​c​c​e​p​t​ ​p​a​y​m​e​n​t​ ​v​i​a​ ​F​l​a​s​h​ ​f​o​r​ ​y​o​u​r​ ​b​u​s​i​n​e​s​s​ ​a​n​d​ ​p​r​i​n​t​ ​r​e​c​e​i​p​t​s​.​ ​A​ ​c​u​s​t​o​m​e​r​ ​s​e​r​v​i​c​e​ ​r​e​p​r​e​s​e​n​t​a​t​i​v​e​ ​w​i​l​l​ ​c​o​n​t​a​c​t​ ​y​o​u​ ​i​f​ ​y​o​u​ ​c​h​e​c​k​ ​t​h​i​s​ ​b​o​x​. - */ - flashTerminalTooltip: string - /** - * B​a​n​k​i​n​g​ ​I​n​f​o​r​m​a​t​i​o​n - */ - bankingInfo: string - /** - * B​a​n​k​ ​N​a​m​e - */ - bankName: string - /** - * E​n​t​e​r​ ​y​o​u​r​ ​b​a​n​k​ ​n​a​m​e - */ - bankNamePlaceholder: string - /** - * B​a​n​k​ ​B​r​a​n​c​h - */ - bankBranch: string - /** - * E​n​t​e​r​ ​y​o​u​r​ ​b​a​n​k​ ​b​r​a​n​c​h - */ - bankBranchPlaceholder: string - /** - * A​c​c​o​u​n​t​ ​T​y​p​e - */ - bankAccountType: string - /** - * S​e​l​e​c​t​ ​a​c​c​o​u​n​t​ ​t​y​p​e - */ - selectBankAccountType: string - /** - * C​u​r​r​e​n​c​y - */ - currency: string - /** - * S​e​l​e​c​t​ ​C​u​r​r​e​n​c​y - */ - selectCurrency: string - /** - * A​c​c​o​u​n​t​ ​N​u​m​b​e​r - */ - accountNum: string - /** - * E​n​t​e​r​ ​y​o​u​r​ ​a​c​c​o​u​n​t​ ​n​u​m​b​e​r - */ - accountNumPlaceholder: string - /** - * U​p​l​o​a​d​ ​I​D​ ​D​o​c​u​m​e​n​t - */ - uploadId: string - /** - * Y​o​u​ ​s​u​c​c​e​s​s​f​u​l​l​y​ ​r​e​q​u​e​s​t​e​d​ ​t​o​ ​u​p​g​r​a​d​e​ ​y​o​u​r​ ​a​c​c​o​u​n​t​ ​t​o​ ​{​a​c​c​o​u​n​t​T​y​p​e​} - * @param {string} accountType - */ - successTitle: RequiredParams<'accountType'> - /** - * *​ ​P​l​e​a​s​e​ ​e​n​t​e​r​ ​t​h​e​ ​t​e​s​t​ ​t​r​a​n​s​a​c​t​i​o​n​ ​a​m​o​u​n​t​ ​t​o​ ​c​o​n​f​i​r​m​ ​y​o​u​r​ ​b​a​n​k​ ​d​e​t​a​i​l​s​. - */ - successDesc: string - /** - * T​r​a​n​s​a​c​t​i​o​n​ ​V​e​r​i​f​i​c​a​t​i​o​n - */ - transactionVerification: string - /** - * T​o​ ​c​o​m​p​l​e​t​e​ ​u​p​g​r​a​d​i​n​g​ ​y​o​u​r​ ​a​c​c​o​u​n​t​ ​t​o​ ​M​E​R​C​H​A​N​T​,​ ​e​n​t​e​r​ ​t​h​e​ ​t​e​s​t​ ​t​r​a​n​s​a​c​t​i​o​n​ ​a​m​o​u​n​t​ ​w​e​ ​s​e​n​t​ ​t​o​ ​y​o​u​r​ ​b​a​n​k​ ​a​c​c​o​u​n​t​ ​{​a​c​c​o​u​n​t​N​u​m​}​ ​t​o​ ​c​o​n​f​i​r​m​ ​y​o​u​r​ ​b​a​n​k​ ​d​e​t​a​i​l​s​. - * @param {string} accountNum - */ - transactionTitle: RequiredParams<'accountNum'> - /** - * T​r​a​n​s​a​c​t​i​o​n​ ​a​m​o​u​n​t - */ - transactionAmount: string } -} + export type TranslationFunctions = { GaloyAddressScreen: { @@ -9938,25 +9765,10 @@ export type TranslationFunctions = { * You successfully requested to upgrade your account to {accountType} */ successTitle: (arg: { accountType: string }) => LocalizedString - /** - * * Please enter the test transaction amount to confirm your bank details. - */ - successDesc: () => LocalizedString - /** - * Transaction Verification - */ - transactionVerification: () => LocalizedString - /** - * To complete upgrading your account to MERCHANT, enter the test transaction amount we sent to your bank account {accountNum} to confirm your bank details. - */ - transactionTitle: (arg: { accountNum: string }) => LocalizedString - /** - * Transaction amount - */ - transactionAmount: () => LocalizedString + } } -} + export type Formatters = { sats: (value: unknown) => unknown diff --git a/app/i18n/raw-i18n/source/en.json b/app/i18n/raw-i18n/source/en.json index f3f0862b5..b1bf045e1 100644 --- a/app/i18n/raw-i18n/source/en.json +++ b/app/i18n/raw-i18n/source/en.json @@ -1406,9 +1406,5 @@ "accountNumPlaceholder": "Enter your account number", "uploadId": "Upload ID Document", "successTitle": "You successfully requested to upgrade your account to {accountType: string}", - "successDesc": "* Please enter the test transaction amount to confirm your bank details.", - "transactionVerification": "Transaction Verification", - "transactionTitle": "To complete upgrading your account to MERCHANT, enter the test transaction amount we sent to your bank account {accountNum: string} to confirm your bank details.", - "transactionAmount": "Transaction amount" } } diff --git a/app/navigation/root-navigator.tsx b/app/navigation/root-navigator.tsx index 405122f16..1ed372e5c 100644 --- a/app/navigation/root-navigator.tsx +++ b/app/navigation/root-navigator.tsx @@ -125,7 +125,6 @@ import { AccountType, Validation, Success, - TestTransaction, } from "@app/screens/account-upgrade-flow" const useStyles = makeStyles(({ colors }) => ({ @@ -625,11 +624,6 @@ export const RootStack = () => { component={Success} options={{ headerShown: false }} /> - ) } diff --git a/app/navigation/stack-param-lists.ts b/app/navigation/stack-param-lists.ts index 9c865136f..bf6aae75e 100644 --- a/app/navigation/stack-param-lists.ts +++ b/app/navigation/stack-param-lists.ts @@ -163,7 +163,6 @@ export type RootStackParamList = { channel: PhoneCodeChannelType } AccountUpgradeSuccess: undefined - TestTransaction: undefined } export type ChatStackParamList = { diff --git a/app/screens/account-upgrade-flow/Success.tsx b/app/screens/account-upgrade-flow/Success.tsx index f73b7de12..adfa35657 100644 --- a/app/screens/account-upgrade-flow/Success.tsx +++ b/app/screens/account-upgrade-flow/Success.tsx @@ -13,13 +13,8 @@ import Account from "@app/assets/illustrations/account.svg" // hooks import { useI18nContext } from "@app/i18n/i18n-react" - -// store import { useAppSelector } from "@app/store/redux" -// gql -import { AccountLevel } from "@app/graphql/generated" - type Props = StackScreenProps const accountTypeLabel = { ONE: "PERSONAL", TWO: "PRO", THREE: "MERCHANT" } @@ -47,15 +42,6 @@ const Success: React.FC = ({ navigation }) => { })} - {accountType === AccountLevel.Three && ( - - {LL.AccountUpgrade.successDesc()} - - )} - -const TestTransaction: React.FC = ({ navigation, route }) => { - const styles = useStyles() - const { LL } = useI18nContext() - const { fetchAccountUpgrade } = useAccountUpgrade() - const { toggleActivityIndicator } = useActivityIndicator() - - const [errorMsg, setErrorMsg] = useState() - const [amount, setAmount] = useState() - - const { bankInfo } = useAppSelector((state) => state.accountUpgrade) - - useEffect(() => { - fetchAccountUpgrade() - }, []) - - const onConfirm = () => {} - - return ( - - - - {LL.AccountUpgrade.transactionTitle({ - accountNum: "****" + bankInfo.accountNumber?.slice(-4) || "", - })} - - - - - - ) -} - -export default TestTransaction - -const useStyles = makeStyles(() => ({ - wrapper: { - flex: 1, - paddingVertical: 10, - paddingHorizontal: 20, - }, - header: { - marginBottom: 30, - }, - btn: { - marginHorizontal: 20, - marginVertical: 10, - }, -})) diff --git a/app/screens/account-upgrade-flow/index.ts b/app/screens/account-upgrade-flow/index.ts index b6159ce8e..372d50471 100644 --- a/app/screens/account-upgrade-flow/index.ts +++ b/app/screens/account-upgrade-flow/index.ts @@ -4,7 +4,6 @@ import BusinessInformation from "./BusinessInformation" import BankInformation from "./BankInformation" import Validation from "./Validation" import Success from "./Success" -import TestTransaction from "./TestTransaction" export { AccountType, @@ -13,5 +12,4 @@ export { BankInformation, Validation, Success, - TestTransaction, } From 6f09e1061fce68fdeb19cd48a7b347555ff02c47 Mon Sep 17 00:00:00 2001 From: Nodirbek Jamoldinov Date: Tue, 27 Jan 2026 16:11:04 +0500 Subject: [PATCH 80/81] use AccountUpgradeRequest query on the useAccountUpgrade hook to fetch the request details and set to the global state --- app/graphql/front-end-queries.ts | 29 ++++++------ app/graphql/generated.gql | 24 ++++++++++ app/graphql/generated.ts | 77 ++++++++++++++++++++++++++++++++ app/hooks/useAccountUpgrade.tsx | 54 +++++++++++++++++++++- 4 files changed, 169 insertions(+), 15 deletions(-) diff --git a/app/graphql/front-end-queries.ts b/app/graphql/front-end-queries.ts index e9af3fecb..38a38212c 100644 --- a/app/graphql/front-end-queries.ts +++ b/app/graphql/front-end-queries.ts @@ -289,23 +289,24 @@ gql` } } - query businessAddressEnrich($address: String!) { - businessAddressEnrich(address: $address) { - formattedAddress - latitude - longitude + query AccountUpgradeRequest { + accountUpgradeRequest { + upgradeRequest { + businessAddress + businessName + currentLevel + email + fullName + name + phoneNumber + requestedLevel + status + username + } errors { - message code + message } } } - - query accountUpgradeRequestStatus { - accountUpgradeRequestStatus { - hasPendingRequest - requestedLevel - errors - } - } ` diff --git a/app/graphql/generated.gql b/app/graphql/generated.gql index 6d0b446d3..b888d2e3b 100644 --- a/app/graphql/generated.gql +++ b/app/graphql/generated.gql @@ -827,6 +827,30 @@ mutation userUpdateUsername($input: UserUpdateUsernameInput!) { } } +query AccountUpgradeRequest { + accountUpgradeRequest { + upgradeRequest { + businessAddress + businessName + currentLevel + email + fullName + name + phoneNumber + requestedLevel + status + username + __typename + } + errors { + code + message + __typename + } + __typename + } +} + query ExportCsvSetting($walletIds: [WalletId!]!) { me { id diff --git a/app/graphql/generated.ts b/app/graphql/generated.ts index 67e910a53..d94ff648d 100644 --- a/app/graphql/generated.ts +++ b/app/graphql/generated.ts @@ -198,6 +198,28 @@ export type AccountUpdateNotificationSettingsPayload = { readonly errors: ReadonlyArray; }; +export type AccountUpgradeRequest = { + readonly __typename: 'AccountUpgradeRequest'; + readonly businessAddress?: Maybe; + readonly businessName?: Maybe; + readonly currentLevel: AccountLevel; + readonly email?: Maybe; + readonly fullName: Scalars['String']['output']; + /** ERPNext document name */ + readonly name: Scalars['String']['output']; + readonly phoneNumber?: Maybe; + readonly requestedLevel: AccountLevel; + /** Workflow status of the upgrade request */ + readonly status: Scalars['String']['output']; + readonly username: Scalars['String']['output']; +}; + +export type AccountUpgradeRequestPayload = { + readonly __typename: 'AccountUpgradeRequestPayload'; + readonly errors: ReadonlyArray; + readonly upgradeRequest?: Maybe; +}; + export type AuthTokenPayload = { readonly __typename: 'AuthTokenPayload'; readonly authToken?: Maybe; @@ -1385,6 +1407,7 @@ export type PublicWallet = { export type Query = { readonly __typename: 'Query'; readonly accountDefaultWallet: PublicWallet; + readonly accountUpgradeRequest: AccountUpgradeRequestPayload; readonly beta: Scalars['Boolean']['output']; /** @deprecated Deprecated in favor of realtimePrice */ readonly btcPrice?: Maybe; @@ -2344,6 +2367,11 @@ export type NpubByUsernameQueryVariables = Exact<{ export type NpubByUsernameQuery = { readonly __typename: 'Query', readonly npubByUsername?: { readonly __typename: 'npubByUsername', readonly npub?: string | null, readonly username?: string | null } | null }; +export type AccountUpgradeRequestQueryVariables = Exact<{ [key: string]: never; }>; + + +export type AccountUpgradeRequestQuery = { readonly __typename: 'Query', readonly accountUpgradeRequest: { readonly __typename: 'AccountUpgradeRequestPayload', readonly upgradeRequest?: { readonly __typename: 'AccountUpgradeRequest', readonly businessAddress?: string | null, readonly businessName?: string | null, readonly currentLevel: AccountLevel, readonly email?: string | null, readonly fullName: string, readonly name: string, readonly phoneNumber?: string | null, readonly requestedLevel: AccountLevel, readonly status: string, readonly username: string } | null, readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly code?: string | null, readonly message: string }> } }; + export type RealtimePriceWsSubscriptionVariables = Exact<{ currency: Scalars['DisplayCurrency']['input']; }>; @@ -4646,6 +4674,55 @@ export function useNpubByUsernameLazyQuery(baseOptions?: Apollo.LazyQueryHookOpt export type NpubByUsernameQueryHookResult = ReturnType; export type NpubByUsernameLazyQueryHookResult = ReturnType; export type NpubByUsernameQueryResult = Apollo.QueryResult; +export const AccountUpgradeRequestDocument = gql` + query AccountUpgradeRequest { + accountUpgradeRequest { + upgradeRequest { + businessAddress + businessName + currentLevel + email + fullName + name + phoneNumber + requestedLevel + status + username + } + errors { + code + message + } + } +} + `; + +/** + * __useAccountUpgradeRequestQuery__ + * + * To run a query within a React component, call `useAccountUpgradeRequestQuery` and pass it any options that fit your needs. + * When your component renders, `useAccountUpgradeRequestQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useAccountUpgradeRequestQuery({ + * variables: { + * }, + * }); + */ +export function useAccountUpgradeRequestQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(AccountUpgradeRequestDocument, options); + } +export function useAccountUpgradeRequestLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(AccountUpgradeRequestDocument, options); + } +export type AccountUpgradeRequestQueryHookResult = ReturnType; +export type AccountUpgradeRequestLazyQueryHookResult = ReturnType; +export type AccountUpgradeRequestQueryResult = Apollo.QueryResult; export const RealtimePriceWsDocument = gql` subscription realtimePriceWs($currency: DisplayCurrency!) { realtimePrice(input: {currency: $currency}) { diff --git a/app/hooks/useAccountUpgrade.tsx b/app/hooks/useAccountUpgrade.tsx index d4b55151d..c3441bf68 100644 --- a/app/hooks/useAccountUpgrade.tsx +++ b/app/hooks/useAccountUpgrade.tsx @@ -1,27 +1,77 @@ +import { useEffect } from "react" +import { parsePhoneNumber } from "libphonenumber-js" + +// hooks import { useActivityIndicator } from "./useActivityIndicator" -import { useAppSelector } from "@app/store/redux" +import { useAppDispatch, useAppSelector } from "@app/store/redux" import { useBusinessAccountUpgradeRequestMutation, HomeAuthedDocument, useIdDocumentUploadUrlGenerateMutation, + AccountLevel, + useAccountUpgradeRequestQuery, } from "@app/graphql/generated" +// store +import { + setAccountUpgrade, + setBusinessInfo, + setPersonalInfo, +} from "@app/store/redux/slices/accountUpgradeSlice" + type UpgradeResult = { success: boolean errors?: string[] } export const useAccountUpgrade = () => { + const dispatch = useAppDispatch() const { toggleActivityIndicator } = useActivityIndicator() const { accountType, personalInfo, businessInfo, bankInfo } = useAppSelector( (state) => state.accountUpgrade, ) + const { data } = useAccountUpgradeRequestQuery({ fetchPolicy: "cache-and-network" }) + const upgradeData = data?.accountUpgradeRequest.upgradeRequest + const [generateIdDocumentUploadUrl] = useIdDocumentUploadUrlGenerateMutation() const [requestAccountUpgrade] = useBusinessAccountUpgradeRequestMutation({ refetchQueries: [HomeAuthedDocument], }) + useEffect(() => { + if (upgradeData && !personalInfo.fullName) { + setAccountUpgradeData() + } + }, [upgradeData]) + + const setAccountUpgradeData = () => { + if (upgradeData) { + const parsedPhone = upgradeData.phoneNumber + ? parsePhoneNumber(upgradeData.phoneNumber) + : undefined + dispatch( + setAccountUpgrade({ + upgradeCompleted: upgradeData.requestedLevel === AccountLevel.Three, + }), + ) + dispatch( + setPersonalInfo({ + fullName: upgradeData.fullName, + countryCode: parsedPhone?.country, + phoneNumber: parsedPhone?.nationalNumber, + email: upgradeData.email, + }), + ) + dispatch( + setBusinessInfo({ + businessName: upgradeData.businessName, + businessAddress: upgradeData.businessAddress, + }), + ) + } + } + const uploadIdDocument = async (): Promise => { const { idDocument } = bankInfo if (!idDocument?.fileName || !idDocument.type || !idDocument.uri) { @@ -81,6 +131,8 @@ export const useAccountUpgrade = () => { errors: data.businessAccountUpgradeRequest.errors.map((e) => e.message), } } + if (accountType === AccountLevel.Three) + dispatch(setAccountUpgrade({ upgradeCompleted: true })) return { success: data?.businessAccountUpgradeRequest?.success ?? false, From c733d6b57a9a17e6c7e0dfaed01e0f37f504d92b Mon Sep 17 00:00:00 2001 From: Nodirbek Jamoldinov Date: Tue, 27 Jan 2026 16:15:11 +0500 Subject: [PATCH 81/81] make email and id document required for PRO upgrade level request --- app/components/account-upgrade-flow/InputField.tsx | 2 ++ .../account-upgrade-flow/BankInformation.tsx | 10 +++++----- .../account-upgrade-flow/PersonalInformation.tsx | 13 +++++++++++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/components/account-upgrade-flow/InputField.tsx b/app/components/account-upgrade-flow/InputField.tsx index 4bdfd5160..e804856d3 100644 --- a/app/components/account-upgrade-flow/InputField.tsx +++ b/app/components/account-upgrade-flow/InputField.tsx @@ -15,6 +15,7 @@ const InputField: React.FC = ({ isOptional, placeholder, value, + editable, autoCapitalize, keyboardType, onChangeText, @@ -40,6 +41,7 @@ const InputField: React.FC = ({ placeholder={placeholder} placeholderTextColor={colors.placeholder} value={value} + editable={editable} onChangeText={onChangeText} onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)} diff --git a/app/screens/account-upgrade-flow/BankInformation.tsx b/app/screens/account-upgrade-flow/BankInformation.tsx index 0faa44c10..4ac047269 100644 --- a/app/screens/account-upgrade-flow/BankInformation.tsx +++ b/app/screens/account-upgrade-flow/BankInformation.tsx @@ -91,10 +91,11 @@ const BankInformation: React.FC = ({ navigation }) => { setAccountNumErr("Account number is required") hasError = true } - if (!idDocument) { - setIdDocumentErr("You must upload an ID document before proceeding") - hasError = true - } + } + + if (!idDocument) { + setIdDocumentErr("You must upload an ID document before proceeding") + hasError = true } if (!hasError) { @@ -173,7 +174,6 @@ const BankInformation: React.FC = ({ navigation }) => { label={LL.AccountUpgrade.uploadId()} photo={idDocument} errorMsg={idDocumentErr} - isOptional={isOptional} onPhotoUpload={(val) => dispatch(setBankInfo({ idDocument: val }))} setErrorMsg={setIdDocumentErr} /> diff --git a/app/screens/account-upgrade-flow/PersonalInformation.tsx b/app/screens/account-upgrade-flow/PersonalInformation.tsx index 16fbfa7c7..605fb07c0 100644 --- a/app/screens/account-upgrade-flow/PersonalInformation.tsx +++ b/app/screens/account-upgrade-flow/PersonalInformation.tsx @@ -41,7 +41,10 @@ const PersonalInformation: React.FC = ({ navigation }) => { const [fullNameErr, setFullNameErr] = useState() const [phoneNumberErr, setPhoneNumberErr] = useState() + const [emailErr, setEmailErr] = useState() + const { + accountType, numOfSteps, personalInfo: { fullName, countryCode, phoneNumber, email }, } = useAppSelector((state) => state.accountUpgrade) @@ -104,6 +107,10 @@ const PersonalInformation: React.FC = ({ navigation }) => { setPhoneNumberErr("Please enter a valid phone number") hasError = true } + if (accountType !== AccountLevel.One && !email) { + setEmailErr("Please enter a valid email address") + hasError = true + } if (!hasError) { if (currentLevel === AccountLevel.Zero && channel) { submitPhoneNumber(channel) @@ -167,9 +174,11 @@ const PersonalInformation: React.FC = ({ navigation }) => { /> dispatch(setPersonalInfo({ email: val }))} autoCapitalize={"none"} />