diff --git a/CHANGELOG.md b/CHANGELOG.md index ee4344c..05c8cad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,26 +2,45 @@ > This changelog keeps track of all updates and improvements made to `W3sSampleWallet`. +## v2.1.0 - _(2026-04-13)_ + +### ๐Ÿš€ What's New + +- Bumped `@circle-fin/w3s-pw-react-native-sdk` to **v2.2.1** [(npm)](https://www.npmjs.com/package/@circle-fin/w3s-pw-react-native-sdk); bumped app metadata to **2.1.0**. +- Updated README for **iOS/Android** requirements aligned with SDK **2.2.1** (including **Android SDK Platform 36** where applicable). +- Fixed **iOS tab switching** when using `react-native-collapsible-tab-view` v8 with React Native **0.81**. +- Added **AGENTS.md** with local verification steps for contributors and agents. +- CI: **ESLint**, **Prettier**, and **TypeScript** (`tsc --noEmit`) on pull requests; **AI PR** optional review workflow; internal **quality-checks** wired for **Cloudsmith** auth during `npm ci`. +- Dependency and security-related upgrades, including **tar**, **minimatch**, **picomatch**, **undici**, **node-forge**, **flatted**, and **brace-expansion** (addressing reported advisories). + ## v2.0.0 - _(2026-03-10)_ + ### ๐Ÿš€ What's New + - Migrated from **bare React Native** to **Expo 54** - Upgraded `@circle-fin/w3s-pw-react-native-sdk` from v1 to v2 - Updated README with configuration details - Added MIGRATION_GUIDE ## v1.1.6 - _(2026-03-06)_ + ### ๐Ÿš€ What's New + - Bumped `@circle-fin/w3s-pw-react-native-sdk` to v1.1.7 [(npm)](https://www.npmjs.com/package/@circle-fin/w3s-pw-react-native-sdk) - Upgraded dependencies and improved SDK integration. ## v1.1.2 - _(2025-03-19)_ + ### ๐Ÿš€ What's New + - Upgrade React Native version from _0.73.6_ to _0.75.5_. - Recommended upgrade to `w3s-react-native-sdk` v1.1.2 [(npm)](https://www.npmjs.com/package/@circle-fin/w3s-pw-react-native-sdk) - Upgraded dependencies and improved SDK integration. ## v1.0.1 - _(2024-09-04)_ + ### ๐Ÿš€ Initial Release + - First official release of `W3sSampleWallet`. - Example project included to demonstrate SDK integration. - Integrated with `w3s-react-native-sdk` v1.1.1 [(npm)](https://www.npmjs.com/package/@circle-fin/w3s-pw-react-native-sdk). diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 0b7b4ce..b28cb57 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -12,17 +12,18 @@ If you have an existing bare React Native project using SDK v1 and want to upgra - [Path 2: Migrate to Expo-Managed Workflow](#path-2-migrate-to-expo-managed-workflow) - [API Changes](#api-changes) - --- ### Understanding the Architecture Change **SDK v1**: Traditional bare React Native architecture + - Package: `"@circle-fin/w3s-pw-react-native-sdk": "^1.0.0"` - Architecture: Bare React Native - Build: Manual native project setup **SDK v2**: Expo Modules architecture + - Package: `"@circle-fin/w3s-pw-react-native-sdk": "^2.0.0"` - Architecture: Expo Modules - Build: Expo Prebuild @@ -43,11 +44,13 @@ Choose the migration path based on your project's native code customizations: This approach preserves your existing native projects and adds Expo Modules support to them. **Best for:** + - Projects with **custom native code** (native modules, custom configurations, etc.) - Teams that want to **continue managing** `ios/` and `android/` directories manually - Minimal changes to existing workflow **How it works:** + - Uses `npx install-expo-modules@latest` to automatically configure native projects - Keeps your existing `ios/` and `android/` directories - Adds necessary Expo Modules configuration without removing customizations @@ -76,11 +79,13 @@ npm install @circle-fin/w3s-pw-react-native-sdk@^2.0.0 4. **Build and test:** For iOS: + ```bash npm run ios ``` For Android: + ```bash npm run android ``` @@ -92,11 +97,13 @@ npm run android This approach converts your project to use Expo's managed workflow where `expo prebuild` generates and manages native code. **Best for:** + - Projects with **minimal or no custom native code** - Teams that want **Expo to manage** native configurations - Simpler maintenance through declarative `app.json` configuration **How it works:** + - Deletes and regenerates `ios/` and `android/` directories from `app.json` - Native code is managed by Expo (don't edit directly) - Run `expo prebuild` whenever you change native configuration @@ -146,11 +153,13 @@ The `--clean` flag will delete and regenerate the native directories. 5. **Build and test:** For iOS: + ```bash npx expo run:ios ``` For Android: + ```bash npx expo run:android ``` @@ -167,31 +176,34 @@ npx expo run:android **Add listener:** SDK v1: + ```typescript WalletSdk.addListener((event) => { // Handle event -}) +}); ``` SDK v2: + ```typescript const eventListener = ProgrammablewalletRnSdk.addListener( - 'CirclePwOnEvent', + "CirclePwOnEvent", (event) => { // Handle event - } -) + }, +); ``` **Remove listener:** SDK v1: + ```typescript -WalletSdk.removeAllListeners() +WalletSdk.removeAllListeners(); ``` SDK v2: + ```typescript -eventListener.remove() +eventListener.remove(); ``` - diff --git a/README.md b/README.md index 2c2f711..507df2f 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,16 @@ This sample app supports three authentication methods: PIN Authentication  Email OTP Authentication  Social Login Authentication ### 1. PIN Code (Recommended for quick start) + - **Simplest setup** - Only requires Circle App ID ### 2. Email OTP + - **Backend configuration** - Requires email service setup on Circle developer console - No additional client-side configuration needed ### 3. Social Login (Google, Facebook, Apple) + - **Advanced setup** - Requires OAuth provider accounts and platform-specific configuration - Additional setup for iOS and Android @@ -33,22 +36,25 @@ This sample app supports three authentication methods: ## Requirements ### Development Environment + - Node.js 20.19.4 or higher - npm - Expo CLI ### Android + - Java 17 (JDK 17 recommended) - Android Studio and Android SDK -- Android SDK Platform 35 (Android 15 VanillaIceCream) +- Android SDK Platform 36 (Android 16 Baklava) - Android SDK Build-Tools 36.0.0 -- Android device or emulator (API level 35+) +- Android device or emulator (API level 36+) ### iOS -- macOS with Xcode 16.1+ (Xcode 26 is recommended) + +- macOS with Xcode 16.3+ (Xcode 26 is recommended) - Xcode Command Line Tools - CocoaPods -- iOS device (iOS 15.0+) or Simulator +- iOS device (iOS 15.1+) or Simulator - Apple Developer account (for physical device testing) --- @@ -56,6 +62,7 @@ This sample app supports three authentication methods: ## Quick Start (PIN Mode) ### Prerequisites + 1. **Circle Developer Console Account** - [Sign up here](https://console.circle.com) 2. **App ID** - Get from [Circle Developer Console](https://console.circle.com/wallets/user/configurator) โ†’ Wallets โ†’ User Controlled โ†’ Configurator 3. **GitHub PAT** (Android only) - [Create token](https://github.com/settings/tokens) with `read:packages` permission @@ -95,11 +102,13 @@ npx expo prebuild **Step 5: Run the app** For iOS: + ```bash npm run ios ``` For Android: + ```bash npm run android ``` @@ -123,6 +132,7 @@ Email OTP authentication requires an SMTP email service configured on your backe Follow [our tutorial](https://developers.circle.com/wallets/user-controlled/create-user-wallets-with-email#step-1-get-mailtrap-smtp-credentials) to configure your SMTP credentials. The tutorial uses [Mailtrap](https://mailtrap.io/) for testing. The tutorial covers: + - **Step 1:** Getting SMTP credentials from your email provider - **Step 2:** Configuring SMTP settings in Circle Developer Console @@ -146,6 +156,7 @@ Before configuring your app, you need to obtain IDs from each social provider. Google **What you need:** + - Web Client ID - `google-services.json` file from Firebase (for Android) - iOS Client ID (extracted from `GoogleService-Info.plist`) @@ -159,9 +170,9 @@ Before configuring your app, you need to obtain IDs from each social provider. - Click "Add app" โ†’ Select Android - Package name: `com.circle.w3s.rn.sample.wallet` - Download the `google-services.json` file - - For detailed Android setup steps, see [Firebase Android setup guide](https://firebase.google.com/docs/android/setup#create-firebase-project) - + + For detailed Android setup steps, see [Firebase Android setup guide](https://firebase.google.com/docs/android/setup#create-firebase-project) + 3. Add an **iOS app** to your project: - Go to Project Settings โ†’ Your apps - Click "Add app" โ†’ Select iOS @@ -178,6 +189,7 @@ Before configuring your app, you need to obtain IDs from each social provider. - In the **Web SDK configuration** panel, copy the **Web client ID** **Save these values:** + ``` โœ“ Web Client ID: xxxxx.apps.googleusercontent.com โœ“ google-services.json: Downloaded @@ -190,6 +202,7 @@ Before configuring your app, you need to obtain IDs from each social provider. Facebook **What you need:** + - Facebook App ID - Facebook Client Token - Facebook Display Name @@ -205,6 +218,7 @@ Before configuring your app, you need to obtain IDs from each social provider. - **Facebook Display Name** (your app name) **Save these values:** + ``` โœ“ Facebook App ID: xxxxxxxxxxxx โœ“ Facebook Client Token: xxxxxxxxxxxxxxxx @@ -217,6 +231,7 @@ Before configuring your app, you need to obtain IDs from each social provider. Apple **What you need:** + - Service ID (for Sign in with Apple on Android) **Steps:** @@ -225,6 +240,7 @@ Before configuring your app, you need to obtain IDs from each social provider. 2. Note down your **Service ID** **Save this value:** + ``` โœ“ Apple Service ID: com.yourcompany.yourapp ``` @@ -311,6 +327,7 @@ Edit `prebuild-sync-src/android/app/src/main/res/values/strings.xml`: ``` Replace: + - `YOUR_FACEBOOK_APP_ID` with your Facebook App ID - `YOUR_FACEBOOK_CLIENT_TOKEN` with your Facebook Client Token @@ -340,6 +357,7 @@ Replace `YOUR_APPLE_SERVICE_ID` with your Apple Service ID (e.g., `com.yourcompa If you encounter build or Metro bundler errors: 1. **Clean install and rebuild:** + ```sh rm -rf node_modules package-lock.json android ios .expo npm install @@ -347,6 +365,7 @@ If you encounter build or Metro bundler errors: ``` 2. **Clear Metro bundler cache:** + ```sh npx expo start --clear ``` diff --git a/SECURITY.md b/SECURITY.md index 014997a..bf0c437 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,4 +2,4 @@ ## Reporting a Vulnerability -Please do not file public issues on Github for security vulnerabilities. All security vulnerabilities should be reported to Circle privately, through Circle's [Vulnerability Disclosure Program](https://hackerone.com/circle). Please read through the program policy before submitting a report. \ No newline at end of file +Please do not file public issues on GitHub for security vulnerabilities. All security vulnerabilities should be reported to Circle privately, through Circle's [Vulnerability Disclosure Program](https://hackerone.com/circle). Please read through the program policy before submitting a report. diff --git a/app/ExecuteScreen.tsx b/app/ExecuteScreen.tsx index ff58021..350f83f 100644 --- a/app/ExecuteScreen.tsx +++ b/app/ExecuteScreen.tsx @@ -16,104 +16,133 @@ * limitations under the License. */ -import * as React from "react" -import { Text, View, ScrollView, Image, TouchableOpacity, StyleSheet } from "react-native" -import { SafeAreaView } from "react-native-safe-area-context" -import { colors, commonStyles } from "../styles" -import { useTranslation } from 'react-i18next' -import { MainButton } from "../components/MainButton" -import { AuthMode } from "../redux/types" -import CommonInputText from "../components/CommonInputText" -import useWalletSdk from "../utils/useWalletSdk" -import { useSnackBar } from "../utils/useSnackBar" -import CopyableText from "../components/CopyableText" -import RequiredMarkText from "../components/RequiredMarkText" -import { useRouter, useLocalSearchParams } from "expo-router" -import { SnackBar } from "../components/SnackBar" -import { useSelector } from "react-redux" +import * as React from "react"; +import { + Text, + View, + ScrollView, + Image, + TouchableOpacity, + StyleSheet, +} from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; +import { colors, commonStyles } from "../styles"; +import { useTranslation } from "react-i18next"; +import { MainButton } from "../components/MainButton"; +import { AuthMode } from "../redux/types"; +import CommonInputText from "../components/CommonInputText"; +import useWalletSdk from "../utils/useWalletSdk"; +import { useSnackBar } from "../utils/useSnackBar"; +import CopyableText from "../components/CopyableText"; +import RequiredMarkText from "../components/RequiredMarkText"; +import { useRouter, useLocalSearchParams } from "expo-router"; +import { SnackBar } from "../components/SnackBar"; +import { useSelector } from "react-redux"; import { selectEmailEncryptionKey, selectEmailUserToken, selectSocialEncryptionKey, - selectSocialUserToken -} from "../features/result/resultSlice" + selectSocialUserToken, +} from "../features/result/resultSlice"; export default function ExecuteScreen() { - const { t } = useTranslation() - const [challengeId, setChallengeId] = React.useState("") - const { visible, message, isSuccess, showSnackBar, hideSnackBar } = useSnackBar() - const { execute } = useWalletSdk(showSnackBar) - const router = useRouter() - const params = useLocalSearchParams() - + const { t } = useTranslation(); + const [challengeId, setChallengeId] = React.useState(""); + const { visible, message, isSuccess, showSnackBar, hideSnackBar } = + useSnackBar(); + const { execute } = useWalletSdk(showSnackBar); + const router = useRouter(); + const params = useLocalSearchParams(); + // Get authMode from route parameters - const authMode = params.authMode as AuthMode || AuthMode.social - + const authMode = (params.authMode as AuthMode) || AuthMode.social; + // Use Redux selectors to get tokens and keys - const socialUserToken = useSelector(selectSocialUserToken) - const socialEncryptionKey = useSelector(selectSocialEncryptionKey) - const emailUserToken = useSelector(selectEmailUserToken) - const emailEncryptionKey = useSelector(selectEmailEncryptionKey) - + const socialUserToken = useSelector(selectSocialUserToken); + const socialEncryptionKey = useSelector(selectSocialEncryptionKey); + const emailUserToken = useSelector(selectEmailUserToken); + const emailEncryptionKey = useSelector(selectEmailEncryptionKey); + // Determine which token and key to use based on authMode - const userToken = authMode === AuthMode.social ? socialUserToken : emailUserToken - const encryptionKey = authMode === AuthMode.social ? socialEncryptionKey : emailEncryptionKey - + const userToken = + authMode === AuthMode.social ? socialUserToken : emailUserToken; + const encryptionKey = + authMode === AuthMode.social ? socialEncryptionKey : emailEncryptionKey; + // Component to display values - const ValueComponent = (value: string | undefined | null, accessibilityLabel: string) => { + const ValueComponent = ( + value: string | undefined | null, + accessibilityLabel: string, + ) => { if (value) { return ( + style={[commonStyles.normalValueText]} + > {value} - ) + ); } - return null - } - + return null; + }; + return ( { - router.back() - }}> - + router.back(); + }} + > + - - {t('go_execute_bt')} - + + {t("go_execute_bt")} + {t("encryption_title")} {ValueComponent(encryptionKey, `executeEncryptionKey`)} - + {t("user_token_title")} - - - + + + - + { if (userToken && encryptionKey) { - execute(userToken, encryptionKey, challengeId, result => { - showSnackBar(t('execute_success'), true) - if (__DEV__) { - console.log(JSON.stringify(result)) - } - }, e => { - showSnackBar(e.message, false) - }) + execute( + userToken, + encryptionKey, + challengeId, + (result) => { + showSnackBar(t("execute_success"), true); + if (__DEV__) { + console.log(JSON.stringify(result)); + } + }, + (e) => { + showSnackBar(e?.message || "Unknown error", false); + }, + ); } }} disabled={challengeId.length === 0 || !userToken || !encryptionKey} @@ -127,7 +156,7 @@ export default function ExecuteScreen() { onDismiss={hideSnackBar} /> - ) + ); } const styles = StyleSheet.create({ @@ -135,7 +164,7 @@ const styles = StyleSheet.create({ color: colors.execute_challenge_screen_title, fontWeight: "700", fontSize: 22, - textAlign: 'center', - marginBottom: 8 + textAlign: "center", + marginBottom: 8, }, -}) \ No newline at end of file +}); diff --git a/app/_layout.tsx b/app/_layout.tsx index 354df11..43565d5 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -16,69 +16,78 @@ * limitations under the License. */ -import FontAwesome from '@expo/vector-icons/FontAwesome' -import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native' -import { useFonts } from 'expo-font' -import { Stack } from 'expo-router' -import * as SplashScreen from 'expo-splash-screen' -import { SafeAreaProvider } from 'react-native-safe-area-context' -import { useEffect } from 'react' -import { useColorScheme } from 'react-native' -import { Provider } from 'react-redux' -import { store } from '../redux/store' -import { GestureHandlerRootView } from 'react-native-gesture-handler' -import '../utils/i18n' +import FontAwesome from "@expo/vector-icons/FontAwesome"; +import { + DarkTheme, + DefaultTheme, + ThemeProvider, +} from "@react-navigation/native"; +import { useFonts } from "expo-font"; +import { Stack } from "expo-router"; +import * as SplashScreen from "expo-splash-screen"; +import { SafeAreaProvider } from "react-native-safe-area-context"; +import { useEffect } from "react"; +import { useColorScheme } from "react-native"; +import { Provider } from "react-redux"; +import { store } from "../redux/store"; +import { GestureHandlerRootView } from "react-native-gesture-handler"; +import "../utils/i18n"; export { // Catch any errors thrown by the Layout component. - ErrorBoundary -} from 'expo-router' + ErrorBoundary, +} from "expo-router"; export const unstable_settings = { - initialRouteName: 'index', -} + initialRouteName: "index", +}; // Prevent the splash screen from auto-hiding before asset loading is complete. -SplashScreen.preventAutoHideAsync() +SplashScreen.preventAutoHideAsync(); export default function RootLayout() { const [loaded, error] = useFonts({ ...FontAwesome.font, - }) + }); // Expo Router uses Error Boundaries to catch errors in the navigation tree. useEffect(() => { - if (error) throw error - }, [error]) + if (error) throw error; + }, [error]); useEffect(() => { if (loaded) { - SplashScreen.hideAsync() + SplashScreen.hideAsync(); } - }, [loaded]) + }, [loaded]); if (!loaded) { - return null + return null; } - return + return ; } function RootLayoutNav() { - const colorScheme = useColorScheme() + const colorScheme = useColorScheme(); return ( - + - + - ) -} \ No newline at end of file + ); +} diff --git a/app/index.tsx b/app/index.tsx index cc2e464..d55eb49 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -16,89 +16,92 @@ * limitations under the License. */ -import * as React from "react" -import { StyleSheet, Text, View, Keyboard } from "react-native" -import { SafeAreaView } from "react-native-safe-area-context" -import { useTranslation } from "react-i18next" -import { colors, commonStyles } from "../styles" -import { useEffect, useState } from "react" -import { DefaultCustomizer } from "../customize/DefaultCustomizer" -import { useSnackBar } from "../utils/useSnackBar" -import { MaterialTabBar, MaterialTabBarProps, Tabs } from 'react-native-collapsible-tab-view' -import ProgrammablewalletRnSdk, { WalletSdk } from "@circle-fin/w3s-pw-react-native-sdk" -import { SnackBar } from "../components/SnackBar" -import { useRouter } from "expo-router" -import { PinTab } from "../components/PinTab" -import { EmailTab } from "../components/EmailTab" -import { SocialTab } from "../components/SocialTab" - -const TAB_BAR_HEIGHT = 48 +import * as React from "react"; +import { StyleSheet, Text, View, Keyboard } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; +import { useTranslation } from "react-i18next"; +import { colors, commonStyles } from "../styles"; +import { useCallback, useEffect } from "react"; +import { DefaultCustomizer } from "../customize/DefaultCustomizer"; +import { useSnackBar } from "../utils/useSnackBar"; +import { + MaterialTabBar, + MaterialTabBarProps, + Tabs, +} from "react-native-collapsible-tab-view"; +import ProgrammablewalletRnSdk, { + WalletSdk, +} from "@circle-fin/w3s-pw-react-native-sdk"; +import { SnackBar } from "../components/SnackBar"; +import { PinTab } from "../components/PinTab"; +import { EmailTab } from "../components/EmailTab"; +import { SocialTab } from "../components/SocialTab"; export default function IndexScreen() { - const { t } = useTranslation() - const { visible, message, isSuccess, showSnackBar, hideSnackBar } = useSnackBar() - const router = useRouter() - - // Initialize headerHeight state with the default value, will be updated dynamically - const [headerHeight, setHeaderHeight] = useState(TAB_BAR_HEIGHT) + const { t } = useTranslation(); + const { visible, message, isSuccess, showSnackBar, hideSnackBar } = + useSnackBar(); useEffect(() => { - DefaultCustomizer.setup() + DefaultCustomizer.setup(); try { const eventListener = ProgrammablewalletRnSdk.addListener( - 'CirclePwOnEvent', + "CirclePwOnEvent", (event) => { - console.log(event) + console.log(event); // Bring application to foreground - WalletSdk.moveRnTaskToFront() + WalletSdk.moveRnTaskToFront(); // Dismiss keyboard to prevent it from blocking the snackbar - Keyboard.dismiss() + Keyboard.dismiss(); // Format event content for display - const eventMessage = `CirclePwOnEvent: ${JSON.stringify(event)}` - showSnackBar(eventMessage, true) - } - ) - + const eventMessage = `CirclePwOnEvent: ${JSON.stringify(event)}`; + showSnackBar(eventMessage, true); + }, + ); + // Cleanup function return () => { // Remove the specific listener - eventListener.remove() - } + eventListener.remove(); + }; } catch (error) { - console.log("Error setting up SDK listeners:", error) + console.error("Error setting up SDK listeners:", error); + showSnackBar( + "Failed to initialize wallet SDK event listener. Please restart the app.", + false, + ); } - }, [showSnackBar, t]) + }, [showSnackBar]); - const header = () => { - return ( - ( + { - // Dynamically measure the height of the header content - const { height } = event.nativeEvent.layout; - setHeaderHeight(height); + alignItems: "center", + justifyContent: "center", + width: "100%", }} > {t("main_title")} {t("main_subtitle")} - ) - } + ), + [t], + ); - const tabBar = (props: MaterialTabBarProps) => ( - name} - labelStyle={commonStyles.tabText} - indicatorStyle={{ backgroundColor: colors.tab_focused }} - /> - ) + const tabBar = useCallback( + (props: MaterialTabBarProps) => ( + name} + labelStyle={commonStyles.tabText} + indicatorStyle={{ backgroundColor: colors.tab_focused }} + /> + ), + [], + ); const tabView = () => { return ( @@ -106,27 +109,27 @@ export default function IndexScreen() { headerContainerStyle={styles.tab} renderHeader={header} renderTabBar={tabBar} - headerHeight={headerHeight} - initialTabName={t('tab_pin')} + initialTabName={t("tab_pin")} + allowHeaderOverscroll > - + - + - + - + - + - + - ) - } + ); + }; return ( @@ -138,7 +141,7 @@ export default function IndexScreen() { onDismiss={hideSnackBar} /> - ) + ); } const styles = StyleSheet.create({ @@ -146,16 +149,16 @@ const styles = StyleSheet.create({ flex: 1, }, header: { - width: '100%', - alignItems: 'center', - justifyContent: 'center', - position: 'absolute', + width: "100%", + alignItems: "center", + justifyContent: "center", + position: "absolute", }, tab: { elevation: 0, backgroundColor: colors.background, borderBottomWidth: 1, - borderColor: colors.tab_underline + borderColor: colors.tab_underline, }, indicator: { backgroundColor: colors.tab_focused }, -}) \ No newline at end of file +}); diff --git a/components/BiometricsSwitch.tsx b/components/BiometricsSwitch.tsx index acac8cb..2b6d0d8 100644 --- a/components/BiometricsSwitch.tsx +++ b/components/BiometricsSwitch.tsx @@ -16,25 +16,31 @@ * limitations under the License. */ -import { Image, StyleSheet, Switch, SwitchProps, Text, View } from "react-native" -import { colors } from "../styles" -import * as React from "react" -import { t } from "../utils/i18n" +import { + Image, + StyleSheet, + Switch, + SwitchProps, + Text, + View, +} from "react-native"; +import { colors } from "../styles"; +import * as React from "react"; +import { t } from "../utils/i18n"; export const BiometricsSwitchButton = (props: BiometricsSwitchProps) => { return ( - - + + - - {t('biometrics_switch_title')} - - - {t('biometrics_switch_desc')} - + {t("biometrics_switch_title")} + {t("biometrics_switch_desc")} - + { value={props.value} /> - ) -} + ); +}; interface BiometricsSwitchProps extends SwitchProps { accessibilityLabel: string; @@ -55,8 +61,8 @@ interface BiometricsSwitchProps extends SwitchProps { const styles = StyleSheet.create({ normalBg: { marginTop: 16, - flexDirection: 'row', - justifyContent: 'space-between' + flexDirection: "row", + justifyContent: "space-between", }, mainText: { color: colors.social_login_text, @@ -68,4 +74,4 @@ const styles = StyleSheet.create({ fontWeight: "400", fontSize: 14, }, -}) +}); diff --git a/components/CommonInputText.tsx b/components/CommonInputText.tsx index f97f6eb..b69ae24 100644 --- a/components/CommonInputText.tsx +++ b/components/CommonInputText.tsx @@ -16,9 +16,9 @@ * limitations under the License. */ -import { TextInput, TextInputProps } from "react-native" -import { colors, commonStyles } from "../styles" -import React from "react" +import { TextInput, TextInputProps } from "react-native"; +import { colors, commonStyles } from "../styles"; +import React from "react"; export default function CommonInputText(props: TextInputProps) { return ( @@ -29,5 +29,5 @@ export default function CommonInputText(props: TextInputProps) { placeholderTextColor={colors.placeholder} style={[commonStyles.normalInputField, props.style]} /> - ) + ); } diff --git a/components/CopyableText.tsx b/components/CopyableText.tsx index 28eae45..f87b7db 100644 --- a/components/CopyableText.tsx +++ b/components/CopyableText.tsx @@ -16,35 +16,43 @@ * limitations under the License. */ -import { Image, Text, TouchableOpacity } from "react-native" -import { commonStyles } from "../styles" -import React from "react" -import * as Clipboard from 'expo-clipboard' +import { Image, Text, TouchableOpacity } from "react-native"; +import { commonStyles } from "../styles"; +import React from "react"; +import * as Clipboard from "expo-clipboard"; export type CopyableTextProps = { accessibilityLabel: string; value: string | undefined | null; }; -export default function CopyableText({ accessibilityLabel, value }: CopyableTextProps) { +export default function CopyableText({ + accessibilityLabel, + value, +}: CopyableTextProps) { return ( { if (value) { if (__DEV__) { - console.log(value) + console.log(value); } - await Clipboard.setStringAsync(value) + await Clipboard.setStringAsync(value); } - }}> + }} + > + style={[commonStyles.normalValueText]} + > {value} - + - ) + ); } diff --git a/components/EmailTab.tsx b/components/EmailTab.tsx index 911ab0e..877e163 100644 --- a/components/EmailTab.tsx +++ b/components/EmailTab.tsx @@ -16,87 +16,87 @@ * limitations under the License. */ -import * as React from "react" -import { Text, View } from "react-native" -import { MainButton } from "./MainButton" -import { useTranslation } from 'react-i18next' -import { commonStyles } from "../styles" -import CommonInputText from "./CommonInputText" -import useWalletSdk from "../utils/useWalletSdk" -import { AuthMode } from "../redux/types" -import { ShowSnackBarFn } from "../utils/useSnackBar" -import CopyableText from "./CopyableText" -import { WalletSdk } from "@circle-fin/w3s-pw-react-native-sdk" -import RequiredMarkText from "./RequiredMarkText" -import { GoExecuteButton } from "./GoExecuteButton" -import { useRouter } from "expo-router" +import * as React from "react"; +import { Text, View } from "react-native"; +import { MainButton } from "./MainButton"; +import { useTranslation } from "react-i18next"; +import { commonStyles } from "../styles"; +import CommonInputText from "./CommonInputText"; +import useWalletSdk from "../utils/useWalletSdk"; +import { AuthMode } from "../redux/types"; +import { ShowSnackBarFn } from "../utils/useSnackBar"; +import CopyableText from "./CopyableText"; +import { WalletSdk } from "@circle-fin/w3s-pw-react-native-sdk"; +import RequiredMarkText from "./RequiredMarkText"; +import { GoExecuteButton } from "./GoExecuteButton"; +import { useRouter } from "expo-router"; interface EmailTabProps { showSnackBar: ShowSnackBarFn; } export const EmailTab: React.FC = ({ showSnackBar }) => { - const router = useRouter() - const { t } = useTranslation() - + const router = useRouter(); + const { t } = useTranslation(); + const { - appId, - dispatchAppId, - verifyOtp, + appId, + dispatchAppId, + verifyOtp, dispatchLoginResult, - // Get email auth data directly from the hook + // Get email auth data directly from the hook emailUserToken, - emailEncryptionKey - } = useWalletSdk(showSnackBar) - - const [deviceToken, setDeviceToken] = React.useState("") - const [deviceEncryptionKey, setDeviceEncryptionKey] = React.useState("") - const [otpToken, setOtpToken] = React.useState("") + emailEncryptionKey, + } = useWalletSdk(showSnackBar); + + const [deviceToken, setDeviceToken] = React.useState(""); + const [deviceEncryptionKey, setDeviceEncryptionKey] = React.useState(""); + const [otpToken, setOtpToken] = React.useState(""); const isEmailLoginDisabled = () => { - return ( - !appId || - !deviceToken || - !deviceEncryptionKey || - !otpToken - ) - } + return !appId || !deviceToken || !deviceEncryptionKey || !otpToken; + }; return ( - {t("device_id_title")} - - - + + {t("device_id_title")} + + + + { - dispatchAppId(value) + dispatchAppId(value); }} value={appId} /> - - + + - - + + - - + + - + = ({ showSnackBar }) => { text={t("email_login_bt")} onPress={() => { verifyOtp( - otpToken, - deviceToken, + otpToken, + deviceToken, deviceEncryptionKey, - loginResult => { - dispatchLoginResult(AuthMode.email, loginResult) - showSnackBar(t('login_success'), true) + (loginResult) => { + dispatchLoginResult(AuthMode.email, loginResult); + showSnackBar(t("login_success"), true); }, - e => { - showSnackBar(e.message, false) - } - ) + (e) => { + showSnackBar(e?.message || "Unknown error", false); + }, + ); }} /> - + {emailUserToken != null && emailEncryptionKey != null && ( { router.push({ - pathname: '/ExecuteScreen', - params: { + pathname: "/ExecuteScreen", + params: { authMode: AuthMode.email, userToken: emailUserToken, - encryptionKey: emailEncryptionKey - } - }) + encryptionKey: emailEncryptionKey, + }, + }); }} /> )} - ) -} \ No newline at end of file + ); +}; diff --git a/components/GoExecuteButton.tsx b/components/GoExecuteButton.tsx index dbddc7e..2d693c7 100644 --- a/components/GoExecuteButton.tsx +++ b/components/GoExecuteButton.tsx @@ -16,35 +16,39 @@ * limitations under the License. */ -import { Image, StyleSheet, Text, TouchableOpacity, TouchableOpacityProps, View } from "react-native" -import { colors } from "../styles" -import * as React from "react" -import { useTranslation } from 'react-i18next' +import { + Image, + StyleSheet, + Text, + TouchableOpacity, + TouchableOpacityProps, + View, +} from "react-native"; +import { colors } from "../styles"; +import * as React from "react"; +import { useTranslation } from "react-i18next"; export const GoExecuteButton = (props: GoExecuteButtonProps) => { - const { t } = useTranslation() + const { t } = useTranslation(); return ( - + - + - - {t('go_execute_bt')} - - - {t('go_execute_bt_desc')} - + {t("go_execute_bt")} + {t("go_execute_bt_desc")} - ) -} + ); +}; interface GoExecuteButtonProps extends TouchableOpacityProps { accessibilityLabel: string; @@ -58,7 +62,7 @@ const styles = StyleSheet.create({ borderRadius: 16, borderColor: colors.input_boarder, marginTop: 16, - flexDirection: 'row', + flexDirection: "row", }, mainText: { color: colors.tab_focused, @@ -70,4 +74,4 @@ const styles = StyleSheet.create({ fontWeight: "400", fontSize: 14, }, -}) +}); diff --git a/components/HyperlinkText.tsx b/components/HyperlinkText.tsx index 89cdf9c..87857c0 100644 --- a/components/HyperlinkText.tsx +++ b/components/HyperlinkText.tsx @@ -16,9 +16,9 @@ * limitations under the License. */ -import { Linking, StyleSheet, Text } from "react-native" -import { colors } from "../styles" -import React from "react" +import { Linking, StyleSheet, Text } from "react-native"; +import { colors } from "../styles"; +import React from "react"; export type HyperlinkTextProps = { fullText: string; @@ -26,22 +26,29 @@ export type HyperlinkTextProps = { url: string; }; -export default function HyperlinkText({ fullText, linkText, url }: HyperlinkTextProps) { - const startIndex = linkText ? fullText.indexOf(linkText) : -1 - +export default function HyperlinkText({ + fullText, + linkText, + url, +}: HyperlinkTextProps) { + const startIndex = linkText ? fullText.indexOf(linkText) : -1; + const getPlainArr = () => { switch (startIndex) { case 0: - return ['', fullText.substring(linkText.length)] + return ["", fullText.substring(linkText.length)]; case -1: - return [fullText, ''] + return [fullText, ""]; default: - return [fullText.substring(0, startIndex), fullText.substring(startIndex + linkText.length)] + return [ + fullText.substring(0, startIndex), + fullText.substring(startIndex + linkText.length), + ]; } - } - - const plainArr = getPlainArr() - + }; + + const plainArr = getPlainArr(); + return ( {plainArr[0]} @@ -49,14 +56,15 @@ export default function HyperlinkText({ fullText, linkText, url }: HyperlinkText { - Linking.openURL(url) - }}> + Linking.openURL(url); + }} + > {linkText} )} {plainArr[1]} - ) + ); } const styles = StyleSheet.create({ @@ -71,4 +79,4 @@ const styles = StyleSheet.create({ fontWeight: "600", fontSize: 14, }, -}) +}); diff --git a/components/MainButton.tsx b/components/MainButton.tsx index 840238d..8478d37 100644 --- a/components/MainButton.tsx +++ b/components/MainButton.tsx @@ -16,43 +16,36 @@ * limitations under the License. */ -import type { TouchableOpacityProps } from "react-native" -import { Text, TouchableOpacity } from "react-native" -import { commonStyles } from "../styles" -import * as React from "react" +import type { TouchableOpacityProps } from "react-native"; +import { Text, TouchableOpacity } from "react-native"; +import { commonStyles } from "../styles"; +import * as React from "react"; export const MainButton = (props: MainButtonProps) => { - let normalBg = commonStyles.mainButton - let disabledBg = commonStyles.disabledMainButton - let normalText = commonStyles.mainButtonText - let disabledText = commonStyles.disabledMainButtonText - + let normalBg = commonStyles.mainButton; + let disabledBg = commonStyles.disabledMainButton; + let normalText = commonStyles.mainButtonText; + let disabledText = commonStyles.disabledMainButtonText; + if (props.isSecondary) { - normalBg = commonStyles.secondaryButton - disabledBg = commonStyles.disabledSecondaryButton - normalText = commonStyles.secondaryButtonText - disabledText = commonStyles.disabledSecondaryButtonText + normalBg = commonStyles.secondaryButton; + disabledBg = commonStyles.disabledSecondaryButton; + normalText = commonStyles.secondaryButtonText; + disabledText = commonStyles.disabledSecondaryButtonText; } - + return ( - + style={[normalBg, props.disabled && disabledBg, props.style]} + > + {props.text} - ) -} + ); +}; interface MainButtonProps extends TouchableOpacityProps { disabled: boolean; diff --git a/components/PinTab.tsx b/components/PinTab.tsx index d4bf540..2299279 100644 --- a/components/PinTab.tsx +++ b/components/PinTab.tsx @@ -16,32 +16,37 @@ * limitations under the License. */ -import * as React from "react" -import { Text, View } from "react-native" -import { MainButton } from "./MainButton" -import { useTranslation } from 'react-i18next' -import { commonStyles } from "../styles" -import CommonInputText from "./CommonInputText" -import useWalletSdk from "../utils/useWalletSdk" -import { ShowSnackBarFn } from "../utils/useSnackBar" -import RequiredMarkText from "./RequiredMarkText" -import { BiometricsSwitchButton } from "./BiometricsSwitch" +import * as React from "react"; +import { Text, View } from "react-native"; +import { MainButton } from "./MainButton"; +import { useTranslation } from "react-i18next"; +import { commonStyles } from "../styles"; +import CommonInputText from "./CommonInputText"; +import useWalletSdk from "../utils/useWalletSdk"; +import { ShowSnackBarFn } from "../utils/useSnackBar"; +import RequiredMarkText from "./RequiredMarkText"; +import { BiometricsSwitchButton } from "./BiometricsSwitch"; interface PinTabProps { showSnackBar: ShowSnackBarFn; } export const PinTab: React.FC = ({ showSnackBar }) => { - const { t } = useTranslation() + const { t } = useTranslation(); const { - endpoint, appId, enableBiometricsPin, - dispatchEndpoint, dispatchAppId, dispatchEnableBiometrics, - execute, setBiometricsPin - } = useWalletSdk(showSnackBar) - const [userToken, setUserToken] = React.useState("") - const [encryptionKey, setEncryptionKey] = React.useState("") - const [challengeId, setChallengeId] = React.useState("") - + endpoint, + appId, + enableBiometricsPin, + dispatchEndpoint, + dispatchAppId, + dispatchEnableBiometrics, + execute, + setBiometricsPin, + } = useWalletSdk(showSnackBar); + const [userToken, setUserToken] = React.useState(""); + const [encryptionKey, setEncryptionKey] = React.useState(""); + const [challengeId, setChallengeId] = React.useState(""); + const isSetBiometricsPinDisabled = () => { return ( !enableBiometricsPin || @@ -49,54 +54,48 @@ export const PinTab: React.FC = ({ showSnackBar }) => { !appId || !encryptionKey || !userToken - ) - } - + ); + }; + const isExecuteDisabled = () => { - return ( - !endpoint || - !appId || - !userToken || - !encryptionKey || - !challengeId - ) - } - + return !endpoint || !appId || !userToken || !encryptionKey || !challengeId; + }; + const successCallback = (result: any) => { - showSnackBar(t('execute_success'), true) - console.log(JSON.stringify(result)) - } - - const errorCallback = (e: any) => { - const message = e?.message || 'Unknown error' - showSnackBar(message, false) - } - + showSnackBar(t("execute_success"), true); + console.log(JSON.stringify(result)); + }; + + const errorCallback = (e: any) => { + const message = e?.message || "Unknown error"; + showSnackBar(message, false); + }; + return ( - + { - dispatchEndpoint(value) + dispatchEndpoint(value); }} value={endpoint} /> - + { - dispatchAppId(value) + dispatchAppId(value); }} value={appId} /> - + - + = ({ showSnackBar }) => { accessibilityLabel={"biometricsSwitch"} enableSwitch={enableBiometricsPin} onValueChange={(value) => { - dispatchEnableBiometrics(value) + dispatchEnableBiometrics(value); }} value={enableBiometricsPin} /> @@ -122,7 +121,13 @@ export const PinTab: React.FC = ({ showSnackBar }) => { isSecondary={false} text={t("execute_bt")} onPress={() => { - execute(userToken, encryptionKey, challengeId, successCallback, errorCallback) + execute( + userToken, + encryptionKey, + challengeId, + successCallback, + errorCallback, + ); }} /> = ({ showSnackBar }) => { disabled={isSetBiometricsPinDisabled()} text={t("set_biometrics_pin_bt")} onPress={() => { - setBiometricsPin(userToken, encryptionKey, successCallback, errorCallback) + setBiometricsPin( + userToken, + encryptionKey, + successCallback, + errorCallback, + ); }} /> - ) -} \ No newline at end of file + ); +}; diff --git a/components/RequiredMarkText.tsx b/components/RequiredMarkText.tsx index 331d263..337cf05 100644 --- a/components/RequiredMarkText.tsx +++ b/components/RequiredMarkText.tsx @@ -16,35 +16,35 @@ * limitations under the License. */ -import { Text, View, StyleProp, TextStyle } from "react-native" -import { commonStyles } from "../styles" -import React from "react" -import { useTranslation } from 'react-i18next' +import { Text, View, StyleProp, TextStyle, ViewStyle } from "react-native"; +import { commonStyles } from "../styles"; +import React from "react"; +import { useTranslation } from "react-i18next"; export type RequiredMarkTextProps = { - text: string, - style?: StyleProp | undefined - markStyle?: StyleProp | undefined -} + text: string; + style?: StyleProp | undefined; + markStyle?: StyleProp | undefined; +}; export default function RequiredMarkText(props: RequiredMarkTextProps) { - const { t } = useTranslation() + const { t } = useTranslation(); return ( - - - {props.text} - + + {props.text} - {t('required_mark')} + {t("required_mark")} - ) + ); } diff --git a/components/SnackBar.tsx b/components/SnackBar.tsx index 7275cda..cc389b0 100644 --- a/components/SnackBar.tsx +++ b/components/SnackBar.tsx @@ -16,10 +16,10 @@ * limitations under the License. */ -import React, { useEffect, useRef } from 'react' -import { Animated, StyleSheet, Text } from 'react-native' -import { colors } from '../styles' -import { useSafeAreaInsets } from 'react-native-safe-area-context' +import React, { useEffect, useRef } from "react"; +import { Animated, StyleSheet, Text } from "react-native"; +import { colors } from "../styles"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; interface SnackBarProps { visible: boolean; @@ -36,8 +36,8 @@ export const SnackBar: React.FC = ({ duration = 3000, onDismiss, }) => { - const opacity = useRef(new Animated.Value(0)).current - const insets = useSafeAreaInsets() + const opacity = useRef(new Animated.Value(0)).current; + const insets = useSafeAreaInsets(); useEffect(() => { if (visible) { @@ -52,33 +52,33 @@ export const SnackBar: React.FC = ({ toValue: 0, duration: 300, useNativeDriver: true, - }) + }), ]).start(() => { if (onDismiss) { - onDismiss() + onDismiss(); } - }) + }); } - }, [visible, duration, opacity, onDismiss]) + }, [visible, duration, opacity, onDismiss]); - if (!visible) return null + if (!visible) return null; return ( - - = ({ {message} - ) -} + ); +}; const styles = StyleSheet.create({ container: { - position: 'absolute', + position: "absolute", left: 20, right: 20, padding: 10, borderRadius: 5, - alignItems: 'center', - justifyContent: 'center', + alignItems: "center", + justifyContent: "center", zIndex: 1000, }, successContainer: { @@ -111,9 +111,9 @@ const styles = StyleSheet.create({ fontSize: 16, }, successMessage: { - color: 'white', + color: "white", }, errorMessage: { color: colors.snackbar_error_text, }, -}) \ No newline at end of file +}); diff --git a/components/SocialLoginButton.tsx b/components/SocialLoginButton.tsx index 70a26b7..1999973 100644 --- a/components/SocialLoginButton.tsx +++ b/components/SocialLoginButton.tsx @@ -16,40 +16,50 @@ * limitations under the License. */ -import { Image, StyleSheet, Text, TouchableOpacity, TouchableOpacityProps } from "react-native" -import { colors } from "../styles" -import * as React from "react" -import { useTranslation } from 'react-i18next' -import { SocialProvider } from "@circle-fin/w3s-pw-react-native-sdk" +import { + Image, + StyleSheet, + Text, + TouchableOpacity, + TouchableOpacityProps, +} from "react-native"; +import { colors } from "../styles"; +import * as React from "react"; +import { useTranslation } from "react-i18next"; +import { SocialProvider } from "@circle-fin/w3s-pw-react-native-sdk"; export const SocialLoginButton = (props: SocialLoginButtonProps) => { - const { t } = useTranslation() - const name = props.provider.toLowerCase() - - let icon = require("../assets/image/ic_google.png") + const { t } = useTranslation(); + const name = props.provider.toLowerCase(); + + let icon = require("../assets/image/ic_google.png"); switch (props.provider) { case SocialProvider.Facebook: - icon = require("../assets/image/ic_facebook.png") - break + icon = require("../assets/image/ic_facebook.png"); + break; case SocialProvider.Apple: - icon = require("../assets/image/ic_apple.png") - break + icon = require("../assets/image/ic_apple.png"); + break; } - + return ( + style={[ + props.disabled ? styles.disabledBg : styles.normalBg, + props.style, + ]} + > {t(`${name}_bt`)} - ) -} + ); +}; interface SocialLoginButtonProps extends TouchableOpacityProps { disabled: boolean; @@ -64,18 +74,18 @@ const styles = StyleSheet.create({ borderRadius: 6, borderColor: colors.social_login_bt_boarder, marginTop: 12, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center' + flexDirection: "row", + alignItems: "center", + justifyContent: "center", }, disabledBg: { backgroundColor: colors.social_login_bt_disabled, minHeight: 40, borderRadius: 6, marginTop: 12, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center' + flexDirection: "row", + alignItems: "center", + justifyContent: "center", }, normalText: { color: colors.social_login_text, @@ -87,4 +97,4 @@ const styles = StyleSheet.create({ fontWeight: "500", fontSize: 16, }, -}) +}); diff --git a/components/SocialTab.tsx b/components/SocialTab.tsx index 59615d5..b279901 100644 --- a/components/SocialTab.tsx +++ b/components/SocialTab.tsx @@ -16,105 +16,115 @@ * limitations under the License. */ -import * as React from "react" -import { Text, View } from "react-native" -import { useTranslation } from 'react-i18next' -import { commonStyles } from "../styles" -import CommonInputText from "./CommonInputText" -import CopyableText from "./CopyableText" -import { WalletSdk, SocialProvider } from "@circle-fin/w3s-pw-react-native-sdk" -import useWalletSdk from "../utils/useWalletSdk" -import { AuthMode } from "../redux/types" -import { ShowSnackBarFn } from "../utils/useSnackBar" -import RequiredMarkText from "./RequiredMarkText" -import { SocialLoginButton } from "./SocialLoginButton" -import HyperlinkText from "./HyperlinkText" -import { GoExecuteButton } from "./GoExecuteButton" -import { useRouter } from "expo-router" -import { useAppDispatch, useAppSelector } from "@redux/hooks" -import { setAppId } from "@features/settings/settingsSlice" -import { selectSocialUserToken, selectSocialEncryptionKey } from "../features/result/resultSlice" +import * as React from "react"; +import { Text, View } from "react-native"; +import { useTranslation } from "react-i18next"; +import { commonStyles } from "../styles"; +import CommonInputText from "./CommonInputText"; +import CopyableText from "./CopyableText"; +import { WalletSdk, SocialProvider } from "@circle-fin/w3s-pw-react-native-sdk"; +import useWalletSdk from "../utils/useWalletSdk"; +import { AuthMode } from "../redux/types"; +import { ShowSnackBarFn } from "../utils/useSnackBar"; +import RequiredMarkText from "./RequiredMarkText"; +import { SocialLoginButton } from "./SocialLoginButton"; +import HyperlinkText from "./HyperlinkText"; +import { GoExecuteButton } from "./GoExecuteButton"; +import { useRouter } from "expo-router"; +import { useAppSelector } from "@redux/hooks"; +import { + selectSocialUserToken, + selectSocialEncryptionKey, +} from "../features/result/resultSlice"; interface SocialTabProps { showSnackBar: ShowSnackBarFn; } export const SocialTab: React.FC = ({ showSnackBar }) => { - const DOC_URL = 'https://developers.circle.com/w3s/docs/authentication-methods#create-a-wallet-with-social-logins' - const router = useRouter() - const { t } = useTranslation() - const dispatch = useAppDispatch() - const { appId: reduxAppId } = useAppSelector(state => state.settings) - const socialUserToken = useAppSelector(selectSocialUserToken) - const socialEncryptionKey = useAppSelector(selectSocialEncryptionKey) - - const { - appId, - dispatchAppId, - logoutAndLogin, - dispatchLoginResult - } = useWalletSdk(showSnackBar) - - const [deviceToken, setDeviceToken] = React.useState("") - const [deviceEncryptionKey, setDeviceEncryptionKey] = React.useState("") + const DOC_URL = + "https://developers.circle.com/w3s/docs/authentication-methods#create-a-wallet-with-social-logins"; + const router = useRouter(); + const { t } = useTranslation(); + const { appId: reduxAppId } = useAppSelector((state) => state.settings); + const socialUserToken = useAppSelector(selectSocialUserToken); + const socialEncryptionKey = useAppSelector(selectSocialEncryptionKey); + + const { appId, dispatchAppId, logoutAndLogin, dispatchLoginResult } = + useWalletSdk(showSnackBar); + + const [deviceToken, setDeviceToken] = React.useState(""); + const [deviceEncryptionKey, setDeviceEncryptionKey] = React.useState(""); const LoginButton = (provider: SocialProvider) => { return ( - { logoutAndLogin( - provider, - deviceToken, + provider, + deviceToken, deviceEncryptionKey, - loginResult => { - dispatchLoginResult(AuthMode.social, loginResult) - showSnackBar(t('login_success'), true) - console.log('login_success') + (loginResult) => { + dispatchLoginResult(AuthMode.social, loginResult); + showSnackBar(t("login_success"), true); + console.log("login_success"); + }, + (e) => { + console.log("login_failed:" + (e?.message || "Unknown error")); + showSnackBar(e?.message || "Unknown error", false); }, - e => { - console.log('login_failed:'+e.message) - showSnackBar(e.message, false) - } - ) + ); }} /> - ) - } + ); + }; const SectionText = () => { return ( - + {t("log_in_with_section")} - ) - } + ); + }; return ( - {t("device_id_title")} - - - + + {t("device_id_title")} + + + + { - dispatchAppId(value) + dispatchAppId(value); }} value={appId || reduxAppId} /> - - + + - - + + = ({ showSnackBar }) => { {LoginButton(SocialProvider.Google)} {LoginButton(SocialProvider.Facebook)} {LoginButton(SocialProvider.Apple)} - + {socialUserToken != null && socialEncryptionKey != null && ( { router.push({ - pathname: '/ExecuteScreen', - params: { + pathname: "/ExecuteScreen", + params: { authMode: AuthMode.social, userToken: socialUserToken, - encryptionKey: socialEncryptionKey - } - }) + encryptionKey: socialEncryptionKey, + }, + }); }} /> )} - - - ) -} \ No newline at end of file + ); +}; diff --git a/customize/DefaultCustomizer.ts b/customize/DefaultCustomizer.ts index a89a344..4fd85fb 100644 --- a/customize/DefaultCustomizer.ts +++ b/customize/DefaultCustomizer.ts @@ -28,263 +28,251 @@ import { TextKey, TextsKey, WalletSdk, -} from '@circle-fin/w3s-pw-react-native-sdk' +} from "@circle-fin/w3s-pw-react-native-sdk"; -import { colors } from '../styles' +import { colors } from "../styles"; export const DefaultCustomizer = { setup() { - _setSecurityQuestions() - _setDismissOnCallbackMap() - _setTextConfigsMap() - _setIconTextConfigsMap() - _setTextConfigMap() - _setErrorStringMap() - _setImageMap() - _setDateFormat() - _setDebugging() - _setCustomUserAgent() - } -} + _setSecurityQuestions(); + _setDismissOnCallbackMap(); + _setTextConfigsMap(); + _setIconTextConfigsMap(); + _setTextConfigMap(); + _setErrorStringMap(); + _setImageMap(); + _setDateFormat(); + _setDebugging(); + _setCustomUserAgent(); + }, +}; const _setSecurityQuestions = () => { WalletSdk.setSecurityQuestions([ - new SecurityQuestion('What was your childhood nickname?', InputType.text), - new SecurityQuestion('What is your fatherโ€™s middle name?', InputType.text), - new SecurityQuestion('When is your birthday?', InputType.datePicker), + new SecurityQuestion("What was your childhood nickname?", InputType.text), + new SecurityQuestion("What is your fatherโ€™s middle name?", InputType.text), + new SecurityQuestion("When is your birthday?", InputType.datePicker), new SecurityQuestion( "When is your father's birthday?", InputType.datePicker, ), - new SecurityQuestion('When is your favorite date?', InputType.datePicker), - new SecurityQuestion('What is the name of your first pet?'), - new SecurityQuestion('What is the name of the first street you lived on?'), - new SecurityQuestion('What is your favorite song?'), - new SecurityQuestion('What is your favorite color?'), - new SecurityQuestion('What is your favorite country?') - ]) -} + new SecurityQuestion("When is your favorite date?", InputType.datePicker), + new SecurityQuestion("What is the name of your first pet?"), + new SecurityQuestion("What is the name of the first street you lived on?"), + new SecurityQuestion("What is your favorite song?"), + new SecurityQuestion("What is your favorite color?"), + new SecurityQuestion("What is your favorite country?"), + ]); +}; const _setDismissOnCallbackMap = () => { - const map = new Map() - map.set(ErrorCode.unknown, true) - map.set(ErrorCode.userCanceled, true) - map.set(ErrorCode.networkError, true) - map.set(ErrorCode.biometricsKeyPermanentlyInvalidated, true) - map.set(ErrorCode.biometricsUserSkip, true) - map.set(ErrorCode.biometricsUserDisableForPin, true) - map.set(ErrorCode.biometricsUserLockout, true) - map.set(ErrorCode.biometricsUserLockoutPermanent, true) - map.set(ErrorCode.biometricsUserNotAllowPermission, true) - map.set(ErrorCode.biometricsInternalError, true) - WalletSdk.setDismissOnCallbackMap(map) -} + const map = new Map(); + map.set(ErrorCode.unknown, true); + map.set(ErrorCode.userCanceled, true); + map.set(ErrorCode.networkError, true); + map.set(ErrorCode.biometricsKeyPermanentlyInvalidated, true); + map.set(ErrorCode.biometricsUserSkip, true); + map.set(ErrorCode.biometricsUserDisableForPin, true); + map.set(ErrorCode.biometricsUserLockout, true); + map.set(ErrorCode.biometricsUserLockoutPermanent, true); + map.set(ErrorCode.biometricsUserNotAllowPermission, true); + map.set(ErrorCode.biometricsInternalError, true); + WalletSdk.setDismissOnCallbackMap(map); +}; const _setTextConfigsMap = () => { - const map = new Map() + const map = new Map(); map.set(TextsKey.securityQuestionHeaders, [ new TextConfig("Choose your 1st question"), - new TextConfig("Choose your 2nd question") - ]) + new TextConfig("Choose your 2nd question"), + ]); map.set(TextsKey.securitySummaryQuestionHeaders, [ new TextConfig("1st Question"), - new TextConfig("2nd Question") - ]) + new TextConfig("2nd Question"), + ]); map.set(TextsKey.enterPinCodeHeadline, [ new TextConfig("ENTER your "), - new TextConfig("PIN", colors.primary) - ]) + new TextConfig("PIN", colors.primary), + ]); map.set(TextsKey.securityIntroHeadline, [ new TextConfig("Set up your "), - new TextConfig("Recovery Method", colors.primary) - ]) + new TextConfig("Recovery Method", colors.primary), + ]); map.set(TextsKey.newPinCodeHeadline, [ new TextConfig("ENTER your "), - new TextConfig("PIN", colors.primary) - ]) + new TextConfig("PIN", colors.primary), + ]); map.set(TextsKey.securityIntroLink, [ new TextConfig("Learn more"), - new TextConfig("https://path/terms-policies/privacy-notice/") - ]) + new TextConfig("https://path/terms-policies/privacy-notice/"), + ]); map.set(TextsKey.recoverPinCodeHeadline, [ new TextConfig("Recover your "), - new TextConfig("PIN", colors.primary) - ]) - WalletSdk.setTextConfigsMap(map) -} + new TextConfig("PIN", colors.primary), + ]); + WalletSdk.setTextConfigsMap(map); +}; const _setIconTextConfigsMap = () => { - const map = new Map() + const map = new Map(); map.set(IconTextsKey.securityConfirmationItems, [ new IconTextConfig( - require("../assets/image/ic_intro_item0_icon.png"), - new TextConfig("This is the only way to recover my account access. ") + require("../assets/image/ic_intro_item0_icon.png"), + new TextConfig("This is the only way to recover my account access. "), ), new IconTextConfig( - require("../assets/image/ic_intro_item1_icon.png"), - new TextConfig( - "Circle wonโ€™t store my answers so itโ€™s my responsibility to remember them." - ) + require("../assets/image/ic_intro_item1_icon.png"), + new TextConfig( + "Circle wonโ€™t store my answers so itโ€™s my responsibility to remember them.", + ), ), new IconTextConfig( - require("../assets/image/ic_intro_item2_icon.png"), - new TextConfig( - "I will lose access to my wallet and my digital assets if I forget my answers. " - ) - ) - ]) - WalletSdk.setIconTextConfigsMap(map) -} -const _setTextConfigMap = () => { - const map = new Map() - const font = 'Inter-Semi-Bold' - const onlyFontConfig = new TextConfig(undefined, undefined, font) - map.set( - TextKey.circlepw_recover_pincode_answer_input_placeholder as TextKey, + require("../assets/image/ic_intro_item2_icon.png"), new TextConfig( - "Type your answer here", - colors.placeholder, - 'Inter-Regular', + "I will lose access to my wallet and my digital assets if I forget my answers. ", ), - ) + ), + ]); + WalletSdk.setIconTextConfigsMap(map); +}; +const _setTextConfigMap = () => { + const map = new Map(); + const font = "Inter-Semi-Bold"; + const onlyFontConfig = new TextConfig(undefined, undefined, font); map.set( - TextKey.circlepw_transaction_request_main_currency as TextKey, - new TextConfig( - 'USDC', - undefined, - font, - ), - ) + TextKey.circlepw_recover_pincode_answer_input_placeholder as TextKey, + new TextConfig( + "Type your answer here", + colors.placeholder, + "Inter-Regular", + ), + ); map.set( - TextKey.circlepw_transaction_request_exchange_value as TextKey, - new TextConfig( - 'โ‰ˆ$20 USD', - undefined, - font, - ), - ) + TextKey.circlepw_transaction_request_main_currency as TextKey, + new TextConfig("USDC", undefined, font), + ); map.set( - TextKey.circlepw_transaction_request_from as TextKey, - new TextConfig( - '0x9988770123456789ccii', - undefined, - font, - ), - ) + TextKey.circlepw_transaction_request_exchange_value as TextKey, + new TextConfig("โ‰ˆ$20 USD", undefined, font), + ); + map.set( + TextKey.circlepw_transaction_request_from as TextKey, + new TextConfig("0x9988770123456789ccii", undefined, font), + ); map.set( - TextKey.circlepw_transaction_request_to_config as TextKey, - onlyFontConfig, - ) + TextKey.circlepw_transaction_request_to_config as TextKey, + onlyFontConfig, + ); map.set( - TextKey.circlepw_transaction_request_to_contract_name as TextKey, - new TextConfig('uniswap.org', undefined, font), - ) + TextKey.circlepw_transaction_request_to_contract_name as TextKey, + new TextConfig("uniswap.org", undefined, font), + ); map.set( - TextKey.circlepw_transaction_request_to_contract_url as TextKey, - new TextConfig('https://uniswap.org', undefined, font), - ) + TextKey.circlepw_transaction_request_to_contract_url as TextKey, + new TextConfig("https://uniswap.org", undefined, font), + ); map.set( - TextKey.circlepw_transaction_request_network_fee as TextKey, - new TextConfig('0.1234 ETH', undefined, font), - ) + TextKey.circlepw_transaction_request_network_fee as TextKey, + new TextConfig("0.1234 ETH", undefined, font), + ); map.set( - TextKey.circlepw_transaction_request_exchange_network_fee as TextKey, - new TextConfig('โ‰ˆ$1.1 USD'), - ) + TextKey.circlepw_transaction_request_exchange_network_fee as TextKey, + new TextConfig("โ‰ˆ$1.1 USD"), + ); map.set( - TextKey.circlepw_transaction_request_total_config as TextKey, - onlyFontConfig, - ) + TextKey.circlepw_transaction_request_total_config as TextKey, + onlyFontConfig, + ); map.set( - TextKey.circlepw_contract_interaction_abi_function_config as TextKey, - onlyFontConfig, - ) + TextKey.circlepw_contract_interaction_abi_function_config as TextKey, + onlyFontConfig, + ); map.set( - TextKey.circlepw_contract_interaction_data_details as TextKey, - onlyFontConfig, - ) + TextKey.circlepw_contract_interaction_data_details as TextKey, + onlyFontConfig, + ); map.set( - TextKey.circlepw_transaction_request_exchange_total_value as TextKey, - new TextConfig('โ‰ˆ$21.1 USD'), - ) + TextKey.circlepw_transaction_request_exchange_total_value as TextKey, + new TextConfig("โ‰ˆ$21.1 USD"), + ); map.set( - TextKey.circlepw_signature_request_contract_name as TextKey, - new TextConfig('koalla.org', undefined, font), - ) + TextKey.circlepw_signature_request_contract_name as TextKey, + new TextConfig("koalla.org", undefined, font), + ); map.set( - TextKey.circlepw_signature_request_contract_url as TextKey, - new TextConfig('https://koalla.org', undefined, font), - ) - WalletSdk.setTextConfigMap(map) -} + TextKey.circlepw_signature_request_contract_url as TextKey, + new TextConfig("https://koalla.org", undefined, font), + ); + WalletSdk.setTextConfigMap(map); +}; const _setErrorStringMap = () => { - const map = new Map() - map.set(ErrorCode.incorrectUserPin, "The PIN you entered is incorrect.") + const map = new Map(); + map.set(ErrorCode.incorrectUserPin, "The PIN you entered is incorrect."); map.set( - ErrorCode.insecurePinCode, - "Your PIN canโ€™t have repeating or consecutive numbers." - ) - WalletSdk.setErrorStringMap(map) -} + ErrorCode.insecurePinCode, + "Your PIN canโ€™t have repeating or consecutive numbers.", + ); + WalletSdk.setErrorStringMap(map); +}; const _setImageMap = () => { - const imageMap = new Map() - imageMap.set(ImageKey.naviBack, require("../assets/image/ic_back.png")) - imageMap.set(ImageKey.naviClose, require("../assets/image/ic_close.png")) - imageMap.set(ImageKey.showPin, require("../assets/image/ic_show_pin.png")) - imageMap.set(ImageKey.hidePin, require("../assets/image/ic_hide_pin.png")) + const imageMap = new Map(); + imageMap.set(ImageKey.naviBack, require("../assets/image/ic_back.png")); + imageMap.set(ImageKey.naviClose, require("../assets/image/ic_close.png")); + imageMap.set(ImageKey.showPin, require("../assets/image/ic_show_pin.png")); + imageMap.set(ImageKey.hidePin, require("../assets/image/ic_hide_pin.png")); imageMap.set( - ImageKey.alertWindowIcon, - require("../assets/image/ic_alert_window_icon.png") - ) + ImageKey.alertWindowIcon, + require("../assets/image/ic_alert_window_icon.png"), + ); imageMap.set( - ImageKey.securityIntroMain, - require("../assets/image/ic_intro_main_icon.png") - ) + ImageKey.securityIntroMain, + require("../assets/image/ic_intro_main_icon.png"), + ); imageMap.set( - ImageKey.selectCheckMark, - require("../assets/image/ic_select_checkmark.png") - ) + ImageKey.selectCheckMark, + require("../assets/image/ic_select_checkmark.png"), + ); imageMap.set( - ImageKey.dropdownArrow, - require("../assets/image/ic_dropdown_arrow.png") - ) + ImageKey.dropdownArrow, + require("../assets/image/ic_dropdown_arrow.png"), + ); imageMap.set( - ImageKey.errorInfo, - require("../assets/image/ic_error_info.png") - ) + ImageKey.errorInfo, + require("../assets/image/ic_error_info.png"), + ); imageMap.set( - ImageKey.securityConfirmMain, - require("../assets/image/ic_security_confirm_main.png") - ) + ImageKey.securityConfirmMain, + require("../assets/image/ic_security_confirm_main.png"), + ); imageMap.set( - ImageKey.biometricsAllowMain, - require('../assets/image/ic_biometrics_allow_main.png'), - ) + ImageKey.biometricsAllowMain, + require("../assets/image/ic_biometrics_allow_main.png"), + ); imageMap.set(ImageKey.requestIcon, { - uri: 'https://avatars.githubusercontent.com/u/37784886', - }) + uri: "https://avatars.githubusercontent.com/u/37784886", + }); imageMap.set( - ImageKey.transactionTokenIcon, - require("../assets/image/ic_usdc.png"), - ) + ImageKey.transactionTokenIcon, + require("../assets/image/ic_usdc.png"), + ); imageMap.set( - ImageKey.networkFeeTipIcon, - require("../assets/image/ic_fee_info.png"), - ) + ImageKey.networkFeeTipIcon, + require("../assets/image/ic_fee_info.png"), + ); imageMap.set( - ImageKey.showLessDetailArrow, - require("../assets/image/ic_show_less_arrow.png"), - ) + ImageKey.showLessDetailArrow, + require("../assets/image/ic_show_less_arrow.png"), + ); imageMap.set( - ImageKey.showMoreDetailArrow, - require("../assets/image/ic_show_more_arrow.png"), - ) - WalletSdk.setImageMap(imageMap) -} + ImageKey.showMoreDetailArrow, + require("../assets/image/ic_show_more_arrow.png"), + ); + WalletSdk.setImageMap(imageMap); +}; const _setDateFormat = () => { - WalletSdk.setDateFormat(DateFormat.YYYYMMDD_HYPHEN) -} + WalletSdk.setDateFormat(DateFormat.YYYYMMDD_HYPHEN); +}; const _setDebugging = () => { - WalletSdk.setDebugging(true) -} + WalletSdk.setDebugging(true); +}; const _setCustomUserAgent = () => { - WalletSdk.setCustomUserAgent("Circle-Programmable-Wallet-RN-Sample") -} + WalletSdk.setCustomUserAgent("Circle-Programmable-Wallet-RN-Sample"); +}; diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..4566278 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,9 @@ +const { defineConfig } = require("eslint/config"); +const expoConfig = require("eslint-config-expo/flat"); + +module.exports = defineConfig([ + expoConfig, + { + ignores: ["dist/*"], + }, +]); diff --git a/features/result/resultSlice.ts b/features/result/resultSlice.ts index d89475e..1310e97 100644 --- a/features/result/resultSlice.ts +++ b/features/result/resultSlice.ts @@ -16,22 +16,22 @@ * limitations under the License. */ -import { createSlice, PayloadAction } from '@reduxjs/toolkit' -import type { RootState } from '../../redux/store' -import { AuthMode } from '../../redux/types' -import type { LoginResult } from '@circle-fin/w3s-pw-react-native-sdk' +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import type { RootState } from "../../redux/store"; +import { AuthMode } from "../../redux/types"; +import type { LoginResult } from "@circle-fin/w3s-pw-react-native-sdk"; export interface AuthLoginResult { - authMode: AuthMode, - result: LoginResult + authMode: AuthMode; + result: LoginResult; } // Define a type for the slice state interface ResultState { - emailUserToken?: string | undefined, - emailEncryptionKey?: string | undefined, - socialUserToken?: string | undefined, - socialEncryptionKey?: string | undefined, + emailUserToken?: string | undefined; + emailEncryptionKey?: string | undefined; + socialUserToken?: string | undefined; + socialEncryptionKey?: string | undefined; } // Define the initial state using that type @@ -39,39 +39,41 @@ const initialState: ResultState = { emailEncryptionKey: undefined, emailUserToken: undefined, socialEncryptionKey: undefined, - socialUserToken: undefined -} + socialUserToken: undefined, +}; export const resultSlice = createSlice({ - name: 'result', + name: "result", initialState, reducers: { setLoginResult: (state, action: PayloadAction) => { - switch (action.payload.authMode){ + switch (action.payload.authMode) { case AuthMode.email: - state.emailUserToken = action.payload.result.userToken - state.emailEncryptionKey = action.payload.result.encryptionKey - break + state.emailUserToken = action.payload.result.userToken; + state.emailEncryptionKey = action.payload.result.encryptionKey; + break; case AuthMode.social: - state.socialUserToken = action.payload.result.userToken - state.socialEncryptionKey = action.payload.result.encryptionKey - break + state.socialUserToken = action.payload.result.userToken; + state.socialEncryptionKey = action.payload.result.encryptionKey; + break; default: // Other auth modes (like PIN) don't store tokens/keys - break + break; } - } + }, }, -}) +}); -export const { - setLoginResult -} = resultSlice.actions +export const { setLoginResult } = resultSlice.actions; // Other code such as selectors can use the imported `RootState` type -export const selectEmailUserToken = (state: RootState) => state.result.emailUserToken -export const selectEmailEncryptionKey = (state: RootState) => state.result.emailEncryptionKey -export const selectSocialUserToken = (state: RootState) => state.result.socialUserToken -export const selectSocialEncryptionKey = (state: RootState) => state.result.socialEncryptionKey +export const selectEmailUserToken = (state: RootState) => + state.result.emailUserToken; +export const selectEmailEncryptionKey = (state: RootState) => + state.result.emailEncryptionKey; +export const selectSocialUserToken = (state: RootState) => + state.result.socialUserToken; +export const selectSocialEncryptionKey = (state: RootState) => + state.result.socialEncryptionKey; -export default resultSlice.reducer +export default resultSlice.reducer; diff --git a/features/settings/settingsSlice.ts b/features/settings/settingsSlice.ts index 997f69c..7545362 100644 --- a/features/settings/settingsSlice.ts +++ b/features/settings/settingsSlice.ts @@ -16,43 +16,41 @@ * limitations under the License. */ -import { createSlice, PayloadAction } from '@reduxjs/toolkit' -import { pw_app_id, pw_endpoint } from '../../src/config.json' -import type { RootState } from '@redux/store' -import type { SettingsState } from '@redux/types' +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { pw_app_id, pw_endpoint } from "../../src/config.json"; +import type { RootState } from "@redux/store"; +import type { SettingsState } from "@redux/types"; // Define the initial state using that type const initialState: SettingsState = { endpoint: pw_endpoint, appId: pw_app_id, - enableBiometrics: false -} + enableBiometrics: false, +}; export const settingsSlice = createSlice({ - name: 'settings', + name: "settings", initialState, reducers: { setEndpoint: (state, action: PayloadAction) => { - state.endpoint = action.payload + state.endpoint = action.payload; }, setAppId: (state, action: PayloadAction) => { - state.appId = action.payload + state.appId = action.payload; }, setEnableBiometrics: (state, action: PayloadAction) => { - state.enableBiometrics = action.payload + state.enableBiometrics = action.payload; }, }, -}) +}); -export const { - setEndpoint, - setAppId, - setEnableBiometrics -} = settingsSlice.actions +export const { setEndpoint, setAppId, setEnableBiometrics } = + settingsSlice.actions; // Other code such as selectors can use the imported `RootState` type -export const selectEndpoint = (state: RootState) => state.settings.endpoint -export const selectAppId = (state: RootState) => state.settings.appId -export const selectEnableBiometrics = (state: RootState) => state.settings.enableBiometrics +export const selectEndpoint = (state: RootState) => state.settings.endpoint; +export const selectAppId = (state: RootState) => state.settings.appId; +export const selectEnableBiometrics = (state: RootState) => + state.settings.enableBiometrics; -export default settingsSlice.reducer \ No newline at end of file +export default settingsSlice.reducer; diff --git a/metro.config.js b/metro.config.js index ca0f719..a290de0 100644 --- a/metro.config.js +++ b/metro.config.js @@ -17,17 +17,17 @@ */ // Learn more https://docs.expo.io/guides/customizing-metro -const { getDefaultConfig } = require('expo/metro-config') -const path = require('path') +const { getDefaultConfig } = require("expo/metro-config"); +const path = require("path"); -const config = getDefaultConfig(__dirname) +const config = getDefaultConfig(__dirname); config.resolver = { ...config.resolver, extraNodeModules: { - '@features': path.resolve(__dirname, 'features'), - '@redux': path.resolve(__dirname, 'redux') - } -} + "@features": path.resolve(__dirname, "features"), + "@redux": path.resolve(__dirname, "redux"), + }, +}; -module.exports = config +module.exports = config; diff --git a/package-lock.json b/package-lock.json index 6e22b35..5ee1413 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,14 @@ { "name": "w3ssamplewallet", - "version": "1.0.0", + "version": "2.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "w3ssamplewallet", - "version": "1.0.0", + "version": "2.1.0", "dependencies": { - "@circle-fin/w3s-pw-react-native-sdk": "^2.0.0", + "@circle-fin/w3s-pw-react-native-sdk": "^2.2.1", "@expo/vector-icons": "^15.0.2", "@react-navigation/bottom-tabs": "^7.2.1", "@react-navigation/elements": "^2.8.1", @@ -1543,9 +1543,9 @@ } }, "node_modules/@circle-fin/w3s-pw-react-native-sdk": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@circle-fin/w3s-pw-react-native-sdk/-/w3s-pw-react-native-sdk-2.0.2.tgz", - "integrity": "sha512-ieG3quNu+/LpYm58ioK1lxh9PD5wWjbTPbXtWvAtd5+jLppy5LOIbeG62PTfQUdfpY4lECTr/HiCkSUiGA5Bxg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@circle-fin/w3s-pw-react-native-sdk/-/w3s-pw-react-native-sdk-2.2.1.tgz", + "integrity": "sha512-+weBFrEzr5w7n1lshfhRHGDtAVtbXlXL2eqVO/5okRTrFRs7ol4r2HSDODnEdlWRnvDxG4x8eUJmHvYGYtPCNA==", "license": "Apache-2.0", "peerDependencies": { "expo": "*", @@ -1929,22 +1929,13 @@ "fingerprint": "bin/cli.js" } }, - "node_modules/@expo/fingerprint/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/@expo/fingerprint/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -2111,15 +2102,6 @@ } } }, - "node_modules/@expo/metro-config/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/@expo/metro-config/node_modules/dotenv": { "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", @@ -2133,12 +2115,12 @@ } }, "node_modules/@expo/metro-config/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -3630,24 +3612,14 @@ "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -4700,13 +4672,12 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "2.0.3", + "resolved": "https://npm.cloudsmith.io/circle/npm-public/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -5163,12 +5134,6 @@ "node": ">= 0.6" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, "node_modules/connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", @@ -6951,15 +6916,6 @@ "async-limiter": "~1.0.0" } }, - "node_modules/expo/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/expo/node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -6982,12 +6938,12 @@ "license": "MIT" }, "node_modules/expo/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -6997,9 +6953,9 @@ } }, "node_modules/expo/node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.2.tgz", + "integrity": "sha512-cfDHL6LStTEKlNilboNtobT/kEa30PtAf2Q1OgszfrG/rpVl1xaFWT9ktfkS306GmHgmnad1Sw4wabhlvFtsTw==", "license": "MIT", "engines": { "node": ">=10" @@ -7302,9 +7258,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -7597,22 +7553,13 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -9766,9 +9713,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -9915,9 +9862,9 @@ } }, "node_modules/node-forge": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz", - "integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", + "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" @@ -10506,9 +10453,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -12494,9 +12441,9 @@ } }, "node_modules/tar": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", - "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz", + "integrity": "sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -12644,9 +12591,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -12923,9 +12870,9 @@ } }, "node_modules/undici": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", - "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", + "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", "license": "MIT", "engines": { "node": ">=18.17" diff --git a/package.json b/package.json index 459f396..1499e59 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "w3ssamplewallet", "main": "expo-router/entry", "packageManager": "npm@10.8.2", - "version": "1.0.0", + "version": "2.1.0", "scripts": { "start": "expo start", "android": "expo run:android", @@ -12,7 +12,7 @@ "format": "npx --yes prettier --write ." }, "dependencies": { - "@circle-fin/w3s-pw-react-native-sdk": "^2.0.0", + "@circle-fin/w3s-pw-react-native-sdk": "^2.2.1", "@expo/vector-icons": "^15.0.2", "@react-navigation/bottom-tabs": "^7.2.1", "@react-navigation/elements": "^2.8.1", @@ -54,11 +54,10 @@ "typescript": "~5.9.2" }, "overrides": { - "node-forge": "1.3.2", "glob": "10.5.0", - "undici": "6.23.0", "js-yaml": "4.1.1", - "tar": "7.5.7" + "tar": "7.5.11", + "brace-expansion": "2.0.3" }, "private": true } diff --git a/redux/hooks.ts b/redux/hooks.ts index 3358dfe..2727933 100644 --- a/redux/hooks.ts +++ b/redux/hooks.ts @@ -16,9 +16,9 @@ * limitations under the License. */ -import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux' -import type { RootState, AppDispatch } from './store' +import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; +import type { RootState, AppDispatch } from "./store"; // Use throughout your app instead of plain `useDispatch` and `useSelector` -export const useAppDispatch = () => useDispatch() -export const useAppSelector: TypedUseSelectorHook = useSelector +export const useAppDispatch = () => useDispatch(); +export const useAppSelector: TypedUseSelectorHook = useSelector; diff --git a/redux/store.ts b/redux/store.ts index 0ba6072..e39fa25 100644 --- a/redux/store.ts +++ b/redux/store.ts @@ -16,16 +16,16 @@ * limitations under the License. */ -import { configureStore } from '@reduxjs/toolkit' -import settingsReducer from '@features/settings/settingsSlice' -import resultReducer from '@features/result/resultSlice' +import { configureStore } from "@reduxjs/toolkit"; +import settingsReducer from "@features/settings/settingsSlice"; +import resultReducer from "@features/result/resultSlice"; export const store = configureStore({ reducer: { settings: settingsReducer, result: resultReducer, }, -}) +}); // Infer the `RootState` and `AppDispatch` types from the store itself export type RootState = ReturnType; diff --git a/redux/types.ts b/redux/types.ts index 053498e..ecead54 100644 --- a/redux/types.ts +++ b/redux/types.ts @@ -17,9 +17,9 @@ */ export enum AuthMode { - social = 'social', - email = 'email', - pin = 'pin', + social = "social", + email = "email", + pin = "pin", } export interface SettingsState { diff --git a/styles.ts b/styles.ts index 8f08ce4..056a34e 100644 --- a/styles.ts +++ b/styles.ts @@ -16,7 +16,7 @@ * limitations under the License. */ -import { StyleSheet } from "react-native" +import { StyleSheet } from "react-native"; export const colors = { text_main: "#1A1A1A", @@ -53,14 +53,14 @@ export const colors = { snackbar_success_bg: "#008339", snackbar_error_bg: "#FFEAEF", snackbar_error_text: "#BC0016", -} +}; -export const pagePaddingH = 16 +export const pagePaddingH = 16; export const commonStyles = StyleSheet.create({ container: { flex: 1, - backgroundColor: colors.background + backgroundColor: colors.background, }, containerWithPadding: { flex: 1, @@ -75,7 +75,7 @@ export const commonStyles = StyleSheet.create({ borderRadius: 56, minHeight: 56, paddingVertical: 8, - justifyContent: "center" + justifyContent: "center", }, secondaryButton: { marginTop: 24, @@ -86,46 +86,46 @@ export const commonStyles = StyleSheet.create({ borderRadius: 56, minHeight: 56, paddingVertical: 8, - justifyContent: "center" + justifyContent: "center", }, disabledMainButton: { - backgroundColor: colors.disabled_main_bt_background + backgroundColor: colors.disabled_main_bt_background, }, disabledSecondaryButton: { backgroundColor: colors.disabled_secondary_bt_background, - opacity: 0.5 + opacity: 0.5, }, disabledMainButtonText: { - color: colors.disabled_main_bt_text + color: colors.disabled_main_bt_text, }, disabledSecondaryButtonText: { - color: colors.disabled_secondary_bt_text + color: colors.disabled_secondary_bt_text, }, mainButtonText: { color: colors.main_bt_text, fontWeight: "500", - fontSize: 16 + fontSize: 16, }, secondaryButtonText: { color: colors.primary, fontWeight: "500", - fontSize: 16 + fontSize: 16, }, sectionLine: { height: 1, backgroundColor: colors.section_line, flexShrink: 1, - alignSelf: 'center', - flex: 1 + alignSelf: "center", + flex: 1, }, sectionText: { color: colors.section_text, - textAlign: 'center', + textAlign: "center", fontWeight: "400", fontSize: 14, marginHorizontal: 16, - alignSelf: 'center', - justifyContent: 'center', + alignSelf: "center", + justifyContent: "center", }, inputTitle: { color: colors.text_auxiliary, @@ -139,28 +139,28 @@ export const commonStyles = StyleSheet.create({ color: colors.text_main, fontWeight: "400", fontSize: 16, - marginTop: 0 + marginTop: 0, }, tabText: { color: colors.tab_focused, fontSize: 14, - fontWeight: "600" + fontWeight: "600", }, heading: { color: colors.text_main, fontSize: 22, fontWeight: "600", marginTop: 24, - textAlign: 'center', - paddingHorizontal: pagePaddingH + textAlign: "center", + paddingHorizontal: pagePaddingH, }, desc: { color: colors.text_desc, fontSize: 14, marginTop: 16, marginBottom: 24, - textAlign: 'center', - paddingHorizontal: pagePaddingH + textAlign: "center", + paddingHorizontal: pagePaddingH, }, normalInputField: { marginTop: 8, @@ -172,7 +172,7 @@ export const commonStyles = StyleSheet.create({ paddingVertical: 8, color: colors.text_main, fontWeight: "400", - fontSize: 16 + fontSize: 16, }, normalValueText: { color: colors.text_main, @@ -181,21 +181,21 @@ export const commonStyles = StyleSheet.create({ marginTop: 8, marginBottom: 4, minHeight: 24, - flexShrink: 1 + flexShrink: 1, }, disabledInputField: { borderColor: colors.input_boarder, - backgroundColor: colors.disabled_input_background + backgroundColor: colors.disabled_input_background, }, focusedInputField: { - borderColor: colors.input_boarder_focused + borderColor: colors.input_boarder_focused, }, requiredMark: { color: colors.required_red, - borderColor: 'red', + borderColor: "red", fontWeight: "400", fontSize: 12, marginLeft: 4, - flexWrap: 'wrap' + flexWrap: "wrap", }, -}) +}); diff --git a/tsconfig.json b/tsconfig.json index 3eade30..2e54598 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,21 +4,10 @@ "strict": true, "jsx": "react-native", "paths": { - "@/*": [ - "./*" - ], - "@features/*": [ - "./features/*" - ], - "@redux/*": [ - "./redux/*" - ] + "@/*": ["./*"], + "@features/*": ["./features/*"], + "@redux/*": ["./redux/*"] } }, - "include": [ - "**/*.ts", - "**/*.tsx", - ".expo/types/**/*.ts", - "expo-env.d.ts" - ] + "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"] } diff --git a/utils/i18n.ts b/utils/i18n.ts index 4ed09d2..cd9f51a 100644 --- a/utils/i18n.ts +++ b/utils/i18n.ts @@ -16,81 +16,81 @@ * limitations under the License. */ -import { default as i18next } from 'i18next' -import { initReactI18next } from 'react-i18next' +import { default as i18next } from "i18next"; +import { initReactI18next } from "react-i18next"; const resources = { en: { translation: { - tab_social: 'Social', - tab_email: 'Email', - tab_pin: 'PIN', - login: 'Login', - register: 'Register', - email: 'Email', - password: 'Password', - pin: 'PIN', - confirm_pin: 'Confirm PIN', - submit: 'Submit', - back: 'Back', - social_login_title: 'Social Login', - email_login_title: 'Email Login', - pin_login_title: 'PIN Login', - sdk_version: 'SDK Version', - required_mark: '*', - endpoint_title: 'Endpoint', - appid_title: 'App ID', - user_token_title: 'User Token', - encryption_title: 'Encryption Key', - challenge_id_title: 'Challenge ID', - execute_bt: 'Execute', - set_biometrics_pin_bt: 'Set Biometrics PIN', - biometrics_switch_title: 'Enable Biometrics', - biometrics_switch_desc: 'Use biometrics for authentication', - execute_success: 'Execute Success', - execute_screen_title: 'Execute Screen', - received_pin: 'Received PIN', - go_back: 'Go Back', - device_id_title: 'Device ID', - device_token_title: 'Device Token', - device_encryption_title: 'Device Encryption Key', - log_in_with_section: 'Log in with', - login_success: 'Login Success', - info_full_text: 'Learn more about social login here', - info_link_text: 'here', - go_execute_bt: 'Go to Execute', - go_execute_bt_desc: 'Execute challenges with your user token', - otp_token_title: 'OTP Token', - email_login_bt: 'Email Login', + tab_social: "Social", + tab_email: "Email", + tab_pin: "PIN", + login: "Login", + register: "Register", + email: "Email", + password: "Password", + pin: "PIN", + confirm_pin: "Confirm PIN", + submit: "Submit", + back: "Back", + social_login_title: "Social Login", + email_login_title: "Email Login", + pin_login_title: "PIN Login", + sdk_version: "SDK Version", + required_mark: "*", + endpoint_title: "Endpoint", + appid_title: "App ID", + user_token_title: "User Token", + encryption_title: "Encryption Key", + challenge_id_title: "Challenge ID", + execute_bt: "Execute", + set_biometrics_pin_bt: "Set Biometrics PIN", + biometrics_switch_title: "Enable Biometrics", + biometrics_switch_desc: "Use biometrics for authentication", + execute_success: "Execute Success", + execute_screen_title: "Execute Screen", + received_pin: "Received PIN", + go_back: "Go Back", + device_id_title: "Device ID", + device_token_title: "Device Token", + device_encryption_title: "Device Encryption Key", + log_in_with_section: "Log in with", + login_success: "Login Success", + info_full_text: "Learn more about social login here", + info_link_text: "here", + go_execute_bt: "Go to Execute", + go_execute_bt_desc: "Execute challenges with your user token", + otp_token_title: "OTP Token", + email_login_bt: "Email Login", main_title: "User Controlled Wallet \nSample App", main_subtitle: "Choose one Auth Method to start", - implement_callback_instruction: 'Callback received', - google_bt: 'Continue with Google', - facebook_bt: 'Continue with Facebook', - apple_bt: 'Continue with Apple', - } - } -} + implement_callback_instruction: "Callback received", + google_bt: "Continue with Google", + facebook_bt: "Continue with Facebook", + apple_bt: "Continue with Apple", + }, + }, +}; i18next .use(initReactI18next) .init({ resources, - lng: 'en', - fallbackLng: 'en', + lng: "en", + fallbackLng: "en", interpolation: { - escapeValue: false - } + escapeValue: false, + }, }) .then(() => { - console.log('i18next initialized successfully') - }) - .catch(error => { - console.error('Error initializing i18next:', error) + console.log("i18next initialized successfully"); }) + .catch((error) => { + console.error("Error initializing i18next:", error); + }); export const t = (key: string): string => { - return i18next.t(key) -} + return i18next.t(key); +}; -export default i18next \ No newline at end of file +export default i18next; diff --git a/utils/useSnackBar.tsx b/utils/useSnackBar.tsx index 2493e09..d201fe7 100644 --- a/utils/useSnackBar.tsx +++ b/utils/useSnackBar.tsx @@ -16,24 +16,24 @@ * limitations under the License. */ -import { useState } from 'react' +import { useState } from "react"; export type ShowSnackBarFn = (msg: string, isSuccess?: boolean) => void; export const useSnackBar = () => { - const [visible, setVisible] = useState(false) - const [message, setMessage] = useState('') - const [isSuccess, setIsSuccess] = useState(true) + const [visible, setVisible] = useState(false); + const [message, setMessage] = useState(""); + const [isSuccess, setIsSuccess] = useState(true); const showSnackBar = (msg: string, success: boolean = true) => { - setMessage(msg) - setIsSuccess(success) - setVisible(true) - } + setMessage(msg); + setIsSuccess(success); + setVisible(true); + }; const hideSnackBar = () => { - setVisible(false) - } + setVisible(false); + }; return { visible, @@ -41,5 +41,5 @@ export const useSnackBar = () => { isSuccess, showSnackBar, hideSnackBar, - } -} + }; +}; diff --git a/utils/useWalletSdk.ts b/utils/useWalletSdk.ts index 777ed62..bf47ba8 100644 --- a/utils/useWalletSdk.ts +++ b/utils/useWalletSdk.ts @@ -22,105 +22,160 @@ import { SuccessCallback, LoginSuccessCallback, SocialProvider, - LoginResult -} from "@circle-fin/w3s-pw-react-native-sdk" -import { ShowSnackBarFn } from "./useSnackBar" -import { useAppSelector, useAppDispatch } from "../redux/hooks" + LoginResult, +} from "@circle-fin/w3s-pw-react-native-sdk"; +import { ShowSnackBarFn } from "./useSnackBar"; +import { useAppSelector, useAppDispatch } from "../redux/hooks"; import { setEndpoint as setReduxEndpoint, setAppId as setReduxAppId, - setEnableBiometrics as setReduxEnableBiometrics -} from "../features/settings/settingsSlice" -import { setLoginResult } from "../features/result/resultSlice" -import { AuthMode } from "../redux/types" + setEnableBiometrics as setReduxEnableBiometrics, +} from "../features/settings/settingsSlice"; +import { setLoginResult } from "../features/result/resultSlice"; +import { AuthMode } from "../redux/types"; export default function useWalletSdk(showSnackBar: ShowSnackBarFn) { - const dispatch = useAppDispatch() - const endpoint = useAppSelector(state => state.settings.endpoint) - const appId = useAppSelector(state => state.settings.appId) - const enableBiometricsPin = useAppSelector(state => state.settings.enableBiometrics) + const dispatch = useAppDispatch(); + const endpoint = useAppSelector((state) => state.settings.endpoint); + const appId = useAppSelector((state) => state.settings.appId); + const enableBiometricsPin = useAppSelector( + (state) => state.settings.enableBiometrics, + ); const dispatchEndpoint = (value: string) => { - dispatch(setReduxEndpoint(value)) - } + dispatch(setReduxEndpoint(value)); + }; const dispatchAppId = (value: string) => { - dispatch(setReduxAppId(value)) - } + dispatch(setReduxAppId(value)); + }; const dispatchEnableBiometrics = (value: boolean) => { - dispatch(setReduxEnableBiometrics(value)) - } + dispatch(setReduxEnableBiometrics(value)); + }; const initThen = (action: () => void) => { WalletSdk.init({ endpoint, appId, - settingsManagement: { enableBiometricsPin: enableBiometricsPin } - }).then(() => { - action() - }).catch((e) => { - showSnackBar(e.message, false) + settingsManagement: { enableBiometricsPin: enableBiometricsPin }, }) - } + .then(() => { + action(); + }) + .catch((e) => { + showSnackBar(e?.message || "Unknown error", false); + }); + }; - const verifyOtp = (otpToken: string, deviceToken: string, deviceEncryptionKey: string, + const verifyOtp = ( + otpToken: string, + deviceToken: string, + deviceEncryptionKey: string, successCallback: LoginSuccessCallback, - errorCallback: ErrorCallback) => { + errorCallback: ErrorCallback, + ) => { initThen(() => { - WalletSdk.verifyOTP(otpToken, deviceToken, deviceEncryptionKey, successCallback, errorCallback) - }) - } + WalletSdk.verifyOTP( + otpToken, + deviceToken, + deviceEncryptionKey, + successCallback, + errorCallback, + ); + }); + }; - const logoutAndLogin = (provider: SocialProvider, deviceToken: string, deviceEncryptionKey: string, + const logoutAndLogin = ( + provider: SocialProvider, + deviceToken: string, + deviceEncryptionKey: string, successCallback: LoginSuccessCallback, - errorCallback: ErrorCallback) => { + errorCallback: ErrorCallback, + ) => { initThen(() => { - WalletSdk.performLogin(provider, deviceToken, deviceEncryptionKey, successCallback, errorCallback) - }) - } + WalletSdk.performLogin( + provider, + deviceToken, + deviceEncryptionKey, + successCallback, + errorCallback, + ); + }); + }; - const execute = (userToken: string, encryptionKey: string, challengeId: string, + const execute = ( + userToken: string, + encryptionKey: string, + challengeId: string, successCallback: SuccessCallback, - errorCallback: ErrorCallback) => { + errorCallback: ErrorCallback, + ) => { initThen(() => { - WalletSdk.execute(userToken, encryptionKey, [challengeId], successCallback, errorCallback) - }) - } + WalletSdk.execute( + userToken, + encryptionKey, + [challengeId], + successCallback, + errorCallback, + ); + }); + }; - const setBiometricsPin = (userToken: string, encryptionKey: string, + const setBiometricsPin = ( + userToken: string, + encryptionKey: string, successCallback: SuccessCallback, - errorCallback: ErrorCallback) => { + errorCallback: ErrorCallback, + ) => { initThen(() => { - WalletSdk.setBiometricsPin(userToken, encryptionKey, successCallback, errorCallback) - }) - } + WalletSdk.setBiometricsPin( + userToken, + encryptionKey, + successCallback, + errorCallback, + ); + }); + }; const dispatchLoginResult = (authMode: AuthMode, result: LoginResult) => { - dispatch(setLoginResult({ authMode, result })) - } - + dispatch(setLoginResult({ authMode, result })); + }; // Get authentication data from Redux state - const emailUserToken = useAppSelector(state => state.result.emailUserToken) - const emailEncryptionKey = useAppSelector(state => state.result.emailEncryptionKey) - const socialUserToken = useAppSelector(state => state.result.socialUserToken) - const socialEncryptionKey = useAppSelector(state => state.result.socialEncryptionKey) - + const emailUserToken = useAppSelector((state) => state.result.emailUserToken); + const emailEncryptionKey = useAppSelector( + (state) => state.result.emailEncryptionKey, + ); + const socialUserToken = useAppSelector( + (state) => state.result.socialUserToken, + ); + const socialEncryptionKey = useAppSelector( + (state) => state.result.socialEncryptionKey, + ); + return { // Configuration - endpoint, appId, enableBiometricsPin, - + endpoint, + appId, + enableBiometricsPin, + // Redux action dispatchers - dispatchEndpoint, dispatchAppId, dispatchEnableBiometrics, - + dispatchEndpoint, + dispatchAppId, + dispatchEnableBiometrics, + // SDK methods - execute, setBiometricsPin, logoutAndLogin, dispatchLoginResult, verifyOtp, - + execute, + setBiometricsPin, + logoutAndLogin, + dispatchLoginResult, + verifyOtp, + // Auth data - emailUserToken, emailEncryptionKey, - socialUserToken, socialEncryptionKey - } + emailUserToken, + emailEncryptionKey, + socialUserToken, + socialEncryptionKey, + }; } - -