From 6bdc4ae1d2ba23303125a1c96e06166859fb00ec Mon Sep 17 00:00:00 2001 From: Aaron DeRuvo Date: Sat, 11 Oct 2025 01:52:41 +0200 Subject: [PATCH 1/6] Move components to sdk for future use (#1243) * move components to sdk and refactor a way from tamagui * linting * reverting since this is just a local issue for me * lint * simple * yarn * yolo * run yarn nice * fix pipelines * fix tests * fix mock * add fonts to mobile sdk * more feedback --------- Co-authored-by: Justin Hernandez --- app/index.js | 6 +- app/jest.config.cjs | 6 +- app/jest.setup.js | 44 +++++++++ app/package.json | 1 - app/src/components/Disclosures.tsx | 4 +- app/src/components/FeedbackModal.tsx | 3 +- app/src/components/FeedbackModalScreen.tsx | 15 ++- app/src/components/NavBar/AadhaarNavBar.tsx | 10 +- app/src/components/NavBar/BaseNavBar.tsx | 5 +- .../components/NavBar/DocumentFlowNavBar.tsx | 2 +- app/src/components/NavBar/HomeNavBar.tsx | 2 +- app/src/components/NavBar/IdDetailsNavBar.tsx | 2 +- app/src/components/Tips.tsx | 5 +- .../buttons/PrimaryButtonLongHold.web.tsx | 92 ----------------- app/src/components/homeScreen/idCard.tsx | 2 +- app/src/components/typography/BodyText.ts | 11 --- app/src/components/typography/Caption.tsx | 23 ----- app/src/components/typography/Description.tsx | 37 ------- app/src/components/typography/SubHeader.tsx | 17 ---- app/src/components/typography/Title.tsx | 29 ------ app/src/layouts/SimpleScrolledTitleLayout.tsx | 14 ++- .../recovery/AccountRecoveryChoiceScreen.tsx | 12 ++- .../recovery/AccountRecoveryScreen.tsx | 10 +- .../recovery/DocumentDataNotFoundScreen.tsx | 18 ++-- .../recovery/RecoverWithPhraseScreen.tsx | 8 +- .../account/settings/CloudBackupScreen.tsx | 12 ++- .../account/settings/SettingsScreen.tsx | 12 ++- .../settings/ShowRecoveryPhraseScreen.tsx | 3 +- .../screens/app/DeferredLinkingInfoScreen.tsx | 9 +- app/src/screens/app/LaunchScreen.tsx | 20 ++-- app/src/screens/app/ModalScreen.tsx | 17 ++-- app/src/screens/dev/CreateMockScreen.tsx | 5 +- .../screens/dev/CreateMockScreenDeepLink.tsx | 12 ++- .../aadhaar/AadhaarUploadErrorScreen.tsx | 20 ++-- .../documents/aadhaar/AadhaarUploadScreen.tsx | 21 ++-- .../aadhaar/AadhaarUploadedSuccessScreen.tsx | 15 +-- .../management/DocumentDataInfoScreen.tsx | 8 +- .../management/ManageDocumentsScreen.tsx | 6 +- .../scanning/DocumentCameraScreen.tsx | 10 +- .../scanning/DocumentCameraTroubleScreen.tsx | 7 +- .../DocumentNFCMethodSelectionScreen.tsx | 16 +-- .../scanning/DocumentNFCScanScreen.tsx | 22 ++--- .../scanning/DocumentNFCScanScreen.web.tsx | 10 +- .../scanning/DocumentNFCTroubleScreen.tsx | 10 +- .../selection/ConfirmBelongingScreen.tsx | 12 ++- .../selection/CountryPickerScreen.tsx | 33 ++++--- .../selection/DocumentOnboardingScreen.tsx | 12 ++- .../documents/selection/IDPickerScreen.tsx | 35 ++++--- app/src/screens/home/ProofHistoryList.tsx | 17 ++-- app/src/screens/home/ProofHistoryScreen.tsx | 9 +- .../AccountVerifiedSuccessScreen.tsx | 8 +- .../screens/onboarding/DisclaimerScreen.tsx | 8 +- .../onboarding/SaveRecoveryPhraseScreen.tsx | 19 ++-- app/src/screens/shared/ComingSoonScreen.tsx | 46 +++++---- .../verification/ProofRequestStatusScreen.tsx | 12 ++- app/src/screens/verification/ProveScreen.tsx | 83 +++++++++------- .../verification/QRCodeTroubleScreen.tsx | 5 +- .../verification/QRCodeViewFinderScreen.tsx | 8 +- packages/mobile-sdk-alpha/README.md | 65 +++++++++++- .../assets/fonts/Advercase-Regular.otf | Bin 0 -> 69692 bytes .../assets/fonts/DINOT-Medium.otf | Bin 0 -> 44944 bytes .../assets/fonts/IBMPlexMono-Regular.otf | Bin 0 -> 82328 bytes packages/mobile-sdk-alpha/package.json | 12 +-- .../mobile-sdk-alpha/react-native.config.cjs | 1 + .../src/components/buttons/AbstractButton.tsx | 36 +++---- .../buttons/HeldPrimaryButtonProveScreen.tsx | 17 ++-- .../src/components/buttons/PrimaryButton.tsx | 10 +- .../buttons/PrimaryButtonLongHold.shared.ts | 2 +- .../buttons/PrimaryButtonLongHold.tsx | 17 +--- .../buttons/PrimaryButtonLongHold.web.tsx | 93 ++++++++++++++++++ .../components/buttons/SecondaryButton.tsx | 10 +- .../src/components/buttons/pressedStyle.tsx | 0 .../src/components/flag/RoundFlag.tsx | 14 +-- .../mobile-sdk-alpha/src/components/index.ts | 33 +++++++ .../components/screens/NFCScannerScreen.tsx | 3 +- .../screens/PassportCameraScreen.tsx | 3 +- .../src/components/screens/QRCodeScreen.tsx | 3 +- .../src/components/typography/Additional.tsx | 5 +- .../src/components/typography/BodyText.tsx | 13 +++ .../src/components/typography/Caption.tsx | 19 ++++ .../src/components/typography/Caution.tsx | 5 +- .../src/components/typography/Description.tsx | 33 +++++++ .../typography/DescriptionTitle.tsx | 23 +++++ .../src/components/typography/SubHeader.tsx | 27 +++++ .../src/components/typography/Title.tsx | 36 +++++++ .../src/components/typography/styles.ts | 2 +- packages/mobile-sdk-alpha/src/utils/fonts.ts | 9 ++ .../mobile-sdk-alpha/src/utils/styleUtils.ts | 20 ++++ packages/mobile-sdk-alpha/tsup.config.ts | 3 + yarn.lock | 12 +-- 90 files changed, 874 insertions(+), 572 deletions(-) delete mode 100644 app/src/components/buttons/PrimaryButtonLongHold.web.tsx delete mode 100644 app/src/components/typography/BodyText.ts delete mode 100644 app/src/components/typography/Caption.tsx delete mode 100644 app/src/components/typography/Description.tsx delete mode 100644 app/src/components/typography/SubHeader.tsx delete mode 100644 app/src/components/typography/Title.tsx create mode 100644 packages/mobile-sdk-alpha/assets/fonts/Advercase-Regular.otf create mode 100755 packages/mobile-sdk-alpha/assets/fonts/DINOT-Medium.otf create mode 100644 packages/mobile-sdk-alpha/assets/fonts/IBMPlexMono-Regular.otf rename {app => packages/mobile-sdk-alpha}/src/components/buttons/AbstractButton.tsx (74%) rename {app => packages/mobile-sdk-alpha}/src/components/buttons/HeldPrimaryButtonProveScreen.tsx (94%) rename {app => packages/mobile-sdk-alpha}/src/components/buttons/PrimaryButton.tsx (72%) rename {app => packages/mobile-sdk-alpha}/src/components/buttons/PrimaryButtonLongHold.shared.ts (86%) rename {app => packages/mobile-sdk-alpha}/src/components/buttons/PrimaryButtonLongHold.tsx (85%) create mode 100644 packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.web.tsx rename {app => packages/mobile-sdk-alpha}/src/components/buttons/SecondaryButton.tsx (71%) rename {app => packages/mobile-sdk-alpha}/src/components/buttons/pressedStyle.tsx (100%) rename {app => packages/mobile-sdk-alpha}/src/components/flag/RoundFlag.tsx (79%) rename {app => packages/mobile-sdk-alpha}/src/components/typography/Additional.tsx (86%) create mode 100644 packages/mobile-sdk-alpha/src/components/typography/BodyText.tsx create mode 100644 packages/mobile-sdk-alpha/src/components/typography/Caption.tsx rename {app => packages/mobile-sdk-alpha}/src/components/typography/Caution.tsx (85%) create mode 100644 packages/mobile-sdk-alpha/src/components/typography/Description.tsx create mode 100644 packages/mobile-sdk-alpha/src/components/typography/DescriptionTitle.tsx create mode 100644 packages/mobile-sdk-alpha/src/components/typography/SubHeader.tsx create mode 100644 packages/mobile-sdk-alpha/src/components/typography/Title.tsx rename {app => packages/mobile-sdk-alpha}/src/components/typography/styles.ts (86%) create mode 100644 packages/mobile-sdk-alpha/src/utils/fonts.ts create mode 100644 packages/mobile-sdk-alpha/src/utils/styleUtils.ts diff --git a/app/index.js b/app/index.js index a58bc3754..f34e341e7 100644 --- a/app/index.js +++ b/app/index.js @@ -13,20 +13,18 @@ import 'react-native-get-random-values'; import { Buffer } from 'buffer'; import React from 'react'; import { AppRegistry, LogBox } from 'react-native'; -import { createTamagui, TamaguiProvider } from 'tamagui'; +import { TamaguiProvider } from 'tamagui'; import App from './App'; import { name as appName } from './app.json'; +import tamaguiConfig from './tamagui.config'; import './src/utils/ethers'; import 'react-native-gesture-handler'; -import { config } from '@tamagui/config/v2-native'; // Set global Buffer before any other imports global.Buffer = Buffer; -const tamaguiConfig = createTamagui(config); - LogBox.ignoreLogs([ /bad setState/, 'Warning, duplicate ID for input', diff --git a/app/jest.config.cjs b/app/jest.config.cjs index 7d552df0d..b97823593 100644 --- a/app/jest.config.cjs +++ b/app/jest.config.cjs @@ -6,7 +6,7 @@ module.exports = { preset: 'react-native', moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], transformIgnorePatterns: [ - 'node_modules/(?!(react-native|@react-native|@react-navigation|@react-native-community|@segment/analytics-react-native|@openpassport|react-native-keychain|react-native-check-version|react-native-nfc-manager|react-native-passport-reader|react-native-gesture-handler|uuid|@stablelib|@react-native-google-signin|react-native-cloud-storage|@react-native-clipboard|@react-native-firebase|@selfxyz|@sentry|@anon-aadhaar)/)', + 'node_modules/(?!(react-native|@react-native|@react-navigation|@react-native-community|@segment/analytics-react-native|@openpassport|react-native-keychain|react-native-check-version|react-native-nfc-manager|react-native-passport-reader|react-native-gesture-handler|uuid|@stablelib|@react-native-google-signin|react-native-cloud-storage|@react-native-clipboard|@react-native-firebase|@selfxyz|@sentry|@anon-aadhaar|react-native-svg|react-native-svg-circle-country-flags)/)', ], setupFiles: ['/jest.setup.js'], testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$', @@ -17,8 +17,12 @@ module.exports = { '^@$': '/src', '^@tests/(.*)$': '/tests/src/$1', '^@tests$': '/tests/src', + // Map react-native-svg to app's node_modules for all packages + '^react-native-svg$': '/node_modules/react-native-svg', '^@selfxyz/mobile-sdk-alpha$': '/../packages/mobile-sdk-alpha/dist/cjs/index.cjs', + '^@selfxyz/mobile-sdk-alpha/components$': + '/../packages/mobile-sdk-alpha/dist/cjs/components/index.cjs', '^@selfxyz/mobile-sdk-alpha/onboarding/(.*)$': '/../packages/mobile-sdk-alpha/dist/cjs/flows/onboarding/$1.cjs', '^@selfxyz/mobile-sdk-alpha/disclosing/(.*)$': diff --git a/app/jest.setup.js b/app/jest.setup.js index 36d1c068b..2a79522ae 100644 --- a/app/jest.setup.js +++ b/app/jest.setup.js @@ -682,6 +682,50 @@ jest.mock('./src/utils/notifications/notificationService', () => require('./tests/__setup__/notificationServiceMock.js'), ); +// Mock react-native-svg +jest.mock('react-native-svg', () => { + const React = require('react'); + + // Mock SvgXml component that handles XML strings + const SvgXml = React.forwardRef( + ({ xml, width, height, style, ...props }, ref) => { + return React.createElement('div', { + ref, + style: { + width: width || 'auto', + height: height || 'auto', + display: 'inline-block', + ...style, + }, + dangerouslySetInnerHTML: { __html: xml }, + ...props, + }); + }, + ); + SvgXml.displayName = 'SvgXml'; + + return { + __esModule: true, + default: SvgXml, + SvgXml, + Svg: props => React.createElement('Svg', props, props.children), + Circle: props => React.createElement('Circle', props, props.children), + Path: props => React.createElement('Path', props, props.children), + G: props => React.createElement('G', props, props.children), + Rect: props => React.createElement('Rect', props, props.children), + Defs: props => React.createElement('Defs', props, props.children), + LinearGradient: props => + React.createElement('LinearGradient', props, props.children), + Stop: props => React.createElement('Stop', props, props.children), + ClipPath: props => React.createElement('ClipPath', props, props.children), + Polygon: props => React.createElement('Polygon', props, props.children), + Polyline: props => React.createElement('Polyline', props, props.children), + Line: props => React.createElement('Line', props, props.children), + Text: props => React.createElement('Text', props, props.children), + TSpan: props => React.createElement('TSpan', props, props.children), + }; +}); + // Mock React Navigation jest.mock('@react-navigation/native', () => { const actualNav = jest.requireActual('@react-navigation/native'); diff --git a/app/package.json b/app/package.json index 299b4ad0f..70283679e 100644 --- a/app/package.json +++ b/app/package.json @@ -143,7 +143,6 @@ "react-native-screens": "4.15.3", "react-native-sqlite-storage": "^6.0.1", "react-native-svg": "15.12.1", - "react-native-svg-circle-country-flags": "^0.2.2", "react-native-svg-web": "^1.0.9", "react-native-web": "^0.19.0", "react-qr-barcode-scanner": "^2.1.8", diff --git a/app/src/components/Disclosures.tsx b/app/src/components/Disclosures.tsx index a55702119..58809af5c 100644 --- a/app/src/components/Disclosures.tsx +++ b/app/src/components/Disclosures.tsx @@ -8,8 +8,8 @@ import { XStack, YStack } from 'tamagui'; import type { Country3LetterCode } from '@selfxyz/common/constants'; import { countryCodes } from '@selfxyz/common/constants'; import type { SelfAppDisclosureConfig } from '@selfxyz/common/utils'; +import { BodyText } from '@selfxyz/mobile-sdk-alpha/components'; -import { BodyText } from '@/components/typography/BodyText'; import CheckMark from '@/images/icons/checkmark.svg'; import { slate200, slate500 } from '@/utils/colors'; @@ -115,7 +115,7 @@ const DisclosureItem: React.FC = ({ paddingHorizontal={10} > - + {text} diff --git a/app/src/components/FeedbackModal.tsx b/app/src/components/FeedbackModal.tsx index de4c20e77..651050151 100644 --- a/app/src/components/FeedbackModal.tsx +++ b/app/src/components/FeedbackModal.tsx @@ -6,7 +6,8 @@ import React, { useState } from 'react'; import { Alert, Modal, StyleSheet, Text, TextInput, View } from 'react-native'; import { Button, XStack, YStack } from 'tamagui'; -import { Caption } from '@/components/typography/Caption'; +import { Caption } from '@selfxyz/mobile-sdk-alpha/components'; + import { black, slate400, white, zinc800, zinc900 } from '@/utils/colors'; import { advercase, dinot } from '@/utils/fonts'; diff --git a/app/src/components/FeedbackModalScreen.tsx b/app/src/components/FeedbackModalScreen.tsx index 1554ea8e9..1b1c793e8 100644 --- a/app/src/components/FeedbackModalScreen.tsx +++ b/app/src/components/FeedbackModalScreen.tsx @@ -6,10 +6,13 @@ import React, { useCallback } from 'react'; import { Modal, StyleSheet } from 'react-native'; import { styled, View, XStack, YStack } from 'tamagui'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; +import { + Description, + PrimaryButton, + SecondaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; + import ModalClose from '@/images/icons/modal_close.svg'; import LogoInversed from '@/images/logo_inversed.svg'; import { white } from '@/utils/colors'; @@ -112,7 +115,9 @@ const FeedbackModalScreen: React.FC = ({ )} - {modalParams.titleText} + + {modalParams.titleText} + {modalParams.bodyText} diff --git a/app/src/components/NavBar/AadhaarNavBar.tsx b/app/src/components/NavBar/AadhaarNavBar.tsx index 39ea21f60..080b25a2f 100644 --- a/app/src/components/NavBar/AadhaarNavBar.tsx +++ b/app/src/components/NavBar/AadhaarNavBar.tsx @@ -61,10 +61,12 @@ export const AadhaarNavBar = (props: NativeStackHeaderProps) => { /> AADHAAR REGISTRATION diff --git a/app/src/components/NavBar/BaseNavBar.tsx b/app/src/components/NavBar/BaseNavBar.tsx index d345c8ea3..2c3ff10a5 100644 --- a/app/src/components/NavBar/BaseNavBar.tsx +++ b/app/src/components/NavBar/BaseNavBar.tsx @@ -3,13 +3,14 @@ // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. import React, { useMemo } from 'react'; +import type { TextProps } from 'react-native'; import type { SystemBarStyle } from 'react-native-edge-to-edge'; import { SystemBars } from 'react-native-edge-to-edge'; -import type { TextProps, ViewProps, XStackProps } from 'tamagui'; +import type { ViewProps, XStackProps } from 'tamagui'; import { Button, View, XStack } from 'tamagui'; import { ChevronLeft, X } from '@tamagui/lucide-icons'; -import { Title } from '@/components/typography/Title'; +import { Title } from '@selfxyz/mobile-sdk-alpha/components'; interface NavBarProps extends XStackProps { children: React.ReactNode; diff --git a/app/src/components/NavBar/DocumentFlowNavBar.tsx b/app/src/components/NavBar/DocumentFlowNavBar.tsx index d790902c2..79c8d0633 100644 --- a/app/src/components/NavBar/DocumentFlowNavBar.tsx +++ b/app/src/components/NavBar/DocumentFlowNavBar.tsx @@ -32,7 +32,7 @@ export const DocumentFlowNavBar = ({ justifyContent="space-between" > navigation.goBack()} /> - + {title} { } /> - + {props.options.title} { } /> - + {props.options.title} )} - + {title} {': '} diff --git a/app/src/components/buttons/PrimaryButtonLongHold.web.tsx b/app/src/components/buttons/PrimaryButtonLongHold.web.tsx deleted file mode 100644 index c98138528..000000000 --- a/app/src/components/buttons/PrimaryButtonLongHold.web.tsx +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import React, { useEffect, useState } from 'react'; -import type { LayoutChangeEvent } from 'react-native'; -// Tamagui imports for web -import { AnimatePresence, YStack } from 'tamagui'; - -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import type { HeldPrimaryButtonProps } from '@/components/buttons/PrimaryButtonLongHold.shared'; -import { - ACTION_TIMER, - COLORS, -} from '@/components/buttons/PrimaryButtonLongHold.shared'; - -export function HeldPrimaryButton({ - children, - onLongPress, - ...props -}: HeldPrimaryButtonProps) { - const [hasTriggered, setHasTriggered] = useState(false); - const [size, setSize] = useState({ width: 0, height: 0 }); - const [isPressed, setIsPressed] = useState(false); - - const onPressIn = () => { - setHasTriggered(false); - setIsPressed(true); - }; - - const onPressOut = () => { - setIsPressed(false); - }; - - const getButtonSize = (e: LayoutChangeEvent) => { - const width = e.nativeEvent.layout.width - 1; - const height = e.nativeEvent.layout.height - 1; - setSize({ width, height }); - }; - - useEffect(() => { - // Web: Use setTimeout to trigger onLongPress - let timeoutId: NodeJS.Timeout; - if (isPressed && !hasTriggered) { - timeoutId = setTimeout(() => { - if (isPressed && !hasTriggered) { - setHasTriggered(true); - onLongPress(); - } - }, ACTION_TIMER); - } - return () => { - if (timeoutId) clearTimeout(timeoutId); - }; - }, [hasTriggered, onLongPress, isPressed]); - - const renderAnimatedComponent = () => { - // Web: Use Tamagui AnimatePresence with CSS transitions - return ( - - {isPressed && ( - - )} - - ); - }; - - return ( - - {children} - - ); -} diff --git a/app/src/components/homeScreen/idCard.tsx b/app/src/components/homeScreen/idCard.tsx index 53b5b9aa2..ff5b27a96 100644 --- a/app/src/components/homeScreen/idCard.tsx +++ b/app/src/components/homeScreen/idCard.tsx @@ -142,7 +142,7 @@ const IdCardLayout: FC = ({ Verified{' '} diff --git a/app/src/components/typography/BodyText.ts b/app/src/components/typography/BodyText.ts deleted file mode 100644 index 0cbc9274e..000000000 --- a/app/src/components/typography/BodyText.ts +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import { styled, Text } from 'tamagui'; - -import { dinot } from '@/utils/fonts'; - -export const BodyText = styled(Text, { - fontFamily: dinot, -}); diff --git a/app/src/components/typography/Caption.tsx b/app/src/components/typography/Caption.tsx deleted file mode 100644 index 5d3b7149c..000000000 --- a/app/src/components/typography/Caption.tsx +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import { styled } from 'tamagui'; - -import { BodyText } from '@/components/typography/BodyText'; -import { slate400 } from '@/utils/colors'; - -export const Caption = styled(BodyText, { - fontSize: 15, - color: slate400, - variants: { - size: { - small: { - fontSize: 14, - }, - large: { - fontSize: 16, - }, - }, - }, -}); diff --git a/app/src/components/typography/Description.tsx b/app/src/components/typography/Description.tsx deleted file mode 100644 index 924cfe327..000000000 --- a/app/src/components/typography/Description.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import React from 'react'; -import { StyleSheet } from 'react-native'; -import type { TextProps } from 'tamagui'; -import { Text } from 'tamagui'; - -import { slate500 } from '@/utils/colors'; -import { dinot } from '@/utils/fonts'; - -type DescriptionProps = TextProps; - -const Description = ({ children, style, ...props }: DescriptionProps) => { - return ( - - {children} - - ); -}; - -export default Description; - -const styles = StyleSheet.create({ - description: { - color: slate500, - fontSize: 18, - lineHeight: 23, - textAlign: 'center', - fontFamily: dinot, - }, -}); diff --git a/app/src/components/typography/SubHeader.tsx b/app/src/components/typography/SubHeader.tsx deleted file mode 100644 index 1d173696d..000000000 --- a/app/src/components/typography/SubHeader.tsx +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import { styled, Text } from 'tamagui'; - -import { dinot } from '@/utils/fonts'; - -export const SubHeader = styled(Text, { - fontFamily: dinot, - lineHeight: 18, - fontSize: 15, - fontWeight: '500', - letterSpacing: 0.6, - textTransform: 'uppercase', - textAlign: 'center', -}); diff --git a/app/src/components/typography/Title.tsx b/app/src/components/typography/Title.tsx deleted file mode 100644 index e77b5d234..000000000 --- a/app/src/components/typography/Title.tsx +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import type { StyleProp, TextStyle } from 'react-native'; -import { styled, Text } from 'tamagui'; - -import { advercase } from '@/utils/fonts'; - -export const Title = styled( - Text, - { - fontSize: 28, - lineHeight: 35, - fontFamily: advercase, - variants: { - size: { - large: { - fontSize: 38, - lineHeight: 47, - }, - }, - }, - }, - { - acceptsClassName: true, - style: (props: { style?: StyleProp }) => props.style, - }, -); diff --git a/app/src/layouts/SimpleScrolledTitleLayout.tsx b/app/src/layouts/SimpleScrolledTitleLayout.tsx index ca85f8dbe..2df632d32 100644 --- a/app/src/layouts/SimpleScrolledTitleLayout.tsx +++ b/app/src/layouts/SimpleScrolledTitleLayout.tsx @@ -7,9 +7,12 @@ import React from 'react'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { ScrollView, YStack } from 'tamagui'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; -import { Title } from '@/components/typography/Title'; +import { + PrimaryButton, + SecondaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; + import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import { white } from '@/utils/colors'; @@ -50,7 +53,10 @@ export default function SimpleScrolledTitleLayout({ )} {secondaryButtonText && onSecondaryButtonPress && ( - + {secondaryButtonText} )} diff --git a/app/src/screens/account/recovery/AccountRecoveryChoiceScreen.tsx b/app/src/screens/account/recovery/AccountRecoveryChoiceScreen.tsx index d39a33253..48de3df54 100644 --- a/app/src/screens/account/recovery/AccountRecoveryChoiceScreen.tsx +++ b/app/src/screens/account/recovery/AccountRecoveryChoiceScreen.tsx @@ -9,13 +9,15 @@ import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { isUserRegisteredWithAlternativeCSCA } from '@selfxyz/common/utils/passports/validate'; import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { + Caption, + Description, + PrimaryButton, + SecondaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; import { BackupEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; -import { Caption } from '@/components/typography/Caption'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import Keyboard from '@/images/icons/keyboard.svg'; import RestoreAccountSvg from '@/images/icons/restore_account.svg'; diff --git a/app/src/screens/account/recovery/AccountRecoveryScreen.tsx b/app/src/screens/account/recovery/AccountRecoveryScreen.tsx index b151056f2..2e2c31e41 100644 --- a/app/src/screens/account/recovery/AccountRecoveryScreen.tsx +++ b/app/src/screens/account/recovery/AccountRecoveryScreen.tsx @@ -5,12 +5,14 @@ import React from 'react'; import { View, YStack } from 'tamagui'; +import { + Description, + PrimaryButton, + SecondaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; import { BackupEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import RestoreAccountSvg from '@/images/icons/restore_account.svg'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; diff --git a/app/src/screens/account/recovery/DocumentDataNotFoundScreen.tsx b/app/src/screens/account/recovery/DocumentDataNotFoundScreen.tsx index fff7ad5b0..f1fbdf8c4 100644 --- a/app/src/screens/account/recovery/DocumentDataNotFoundScreen.tsx +++ b/app/src/screens/account/recovery/DocumentDataNotFoundScreen.tsx @@ -8,10 +8,12 @@ import { hasAnyValidRegisteredDocument, useSelfClient, } from '@selfxyz/mobile-sdk-alpha'; +import { + Description, + PrimaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import analytics from '@/utils/analytics'; @@ -41,13 +43,15 @@ const DocumentDataNotFoundScreen: React.FC = () => { return ( - + <Title style={{ textAlign: 'center', color: white }}> ✨ Are you new here? It seems like you need to go through the registration flow first. diff --git a/app/src/screens/account/recovery/RecoverWithPhraseScreen.tsx b/app/src/screens/account/recovery/RecoverWithPhraseScreen.tsx index de49cc0e5..a7613bfcf 100644 --- a/app/src/screens/account/recovery/RecoverWithPhraseScreen.tsx +++ b/app/src/screens/account/recovery/RecoverWithPhraseScreen.tsx @@ -12,10 +12,12 @@ import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { isUserRegisteredWithAlternativeCSCA } from '@selfxyz/common/utils/passports/validate'; import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { + Description, + SecondaryButton, +} from '@selfxyz/mobile-sdk-alpha/components'; import { BackupEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; -import Description from '@/components/typography/Description'; import Paste from '@/images/icons/paste.svg'; import type { RootStackParamList } from '@/navigation'; import { useAuth } from '@/providers/authProvider'; @@ -115,7 +117,7 @@ const RecoverWithPhraseScreen: React.FC = () => { paddingBottom="$2.5" style={styles.layout} > - + Your recovery phrase has 24 words. Enter the words in the correct order, separated by spaces. diff --git a/app/src/screens/account/settings/CloudBackupScreen.tsx b/app/src/screens/account/settings/CloudBackupScreen.tsx index d1afbe930..469477ce7 100644 --- a/app/src/screens/account/settings/CloudBackupScreen.tsx +++ b/app/src/screens/account/settings/CloudBackupScreen.tsx @@ -9,14 +9,16 @@ import { useNavigation } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { + Caption, + Description, + PrimaryButton, + SecondaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; import { BackupEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; import BackupDocumentationLink from '@/components/BackupDocumentationLink'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; -import { Caption } from '@/components/typography/Caption'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; import { useModal } from '@/hooks/useModal'; import Cloud from '@/images/icons/logo_cloud_backup.svg'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; diff --git a/app/src/screens/account/settings/SettingsScreen.tsx b/app/src/screens/account/settings/SettingsScreen.tsx index 3d1b476d1..5e95d56bd 100644 --- a/app/src/screens/account/settings/SettingsScreen.tsx +++ b/app/src/screens/account/settings/SettingsScreen.tsx @@ -13,8 +13,8 @@ import { useNavigation } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { Bug, FileText } from '@tamagui/lucide-icons'; -import { pressedStyle } from '@/components/buttons/pressedStyle'; -import { BodyText } from '@/components/typography/BodyText'; +import { BodyText, pressedStyle } from '@selfxyz/mobile-sdk-alpha/components'; + import { appStoreUrl, gitHubUrl, @@ -119,7 +119,7 @@ const MenuButton: React.FC = ({ children, Icon, onPress }) => ( hitSlop={4} > - + {children} @@ -262,14 +262,16 @@ ${deviceInfo.map(([k, v]) => `${k}=${v}`).join('; ')} pressStyle={pressedStyle} onPress={goToStore} > - Leave an app store review + + Leave an app store review + {social.map(([Icon, href], i) => ( ))} - + SELF {/* Dont remove if not viewing on ios */} diff --git a/app/src/screens/account/settings/ShowRecoveryPhraseScreen.tsx b/app/src/screens/account/settings/ShowRecoveryPhraseScreen.tsx index 5017709b9..dcfec766d 100644 --- a/app/src/screens/account/settings/ShowRecoveryPhraseScreen.tsx +++ b/app/src/screens/account/settings/ShowRecoveryPhraseScreen.tsx @@ -4,8 +4,9 @@ import React, { useCallback } from 'react'; +import { Description } from '@selfxyz/mobile-sdk-alpha/components'; + import Mnemonic from '@/components/Mnemonic'; -import Description from '@/components/typography/Description'; import useMnemonic from '@/hooks/useMnemonic'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; diff --git a/app/src/screens/app/DeferredLinkingInfoScreen.tsx b/app/src/screens/app/DeferredLinkingInfoScreen.tsx index e1db56959..7a2d17f72 100644 --- a/app/src/screens/app/DeferredLinkingInfoScreen.tsx +++ b/app/src/screens/app/DeferredLinkingInfoScreen.tsx @@ -6,9 +6,12 @@ import React from 'react'; import { YStack } from 'tamagui'; import { useNavigation } from '@react-navigation/native'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; +import { + Description, + PrimaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; + import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import { black, white } from '@/utils/colors'; import { confirmTap } from '@/utils/haptic'; diff --git a/app/src/screens/app/LaunchScreen.tsx b/app/src/screens/app/LaunchScreen.tsx index bbdf72ffe..f2e42ef7f 100644 --- a/app/src/screens/app/LaunchScreen.tsx +++ b/app/src/screens/app/LaunchScreen.tsx @@ -8,11 +8,13 @@ import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Anchor, Text, YStack } from 'tamagui'; +import { + AbstractButton, + BodyText, + Caption, +} from '@selfxyz/mobile-sdk-alpha/components'; import { AppEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import AbstractButton from '@/components/buttons/AbstractButton'; -import { BodyText } from '@/components/typography/BodyText'; -import { Caption } from '@/components/typography/Caption'; import { privacyUrl, termsUrl } from '@/consts/links'; import useConnectionModal from '@/hooks/useConnectionModal'; import useHapticNavigation from '@/hooks/useHapticNavigation'; @@ -64,11 +66,13 @@ const LaunchScreen: React.FC = () => { Take control of your digital identity Self is the easiest way to verify your identity safely wherever you are. diff --git a/app/src/screens/app/ModalScreen.tsx b/app/src/screens/app/ModalScreen.tsx index 2c3a9e7ac..aa2e42f2b 100644 --- a/app/src/screens/app/ModalScreen.tsx +++ b/app/src/screens/app/ModalScreen.tsx @@ -7,10 +7,13 @@ import { styled, View, XStack, YStack } from 'tamagui'; import type { StaticScreenProps } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; +import { + Description, + PrimaryButton, + SecondaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; + import ModalClose from '@/images/icons/modal_close.svg'; import LogoInversed from '@/images/logo_inversed.svg'; import { white } from '@/utils/colors'; @@ -112,9 +115,11 @@ const ModalScreen: React.FC = ({ route: { params } }) => { {params?.preventDismiss ? null : } - {params?.titleText} + + {params?.titleText as React.ReactNode} + - {params?.bodyText} + {params?.bodyText as React.ReactNode} diff --git a/app/src/screens/dev/CreateMockScreen.tsx b/app/src/screens/dev/CreateMockScreen.tsx index a645a3363..2beb2dbce 100644 --- a/app/src/screens/dev/CreateMockScreen.tsx +++ b/app/src/screens/dev/CreateMockScreen.tsx @@ -29,11 +29,10 @@ import { signatureAlgorithmToStrictSignatureAlgorithm, useSelfClient, } from '@selfxyz/mobile-sdk-alpha'; +import { Caption, PrimaryButton } from '@selfxyz/mobile-sdk-alpha/components'; import { MockDataEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; import ButtonsContainer from '@/components/ButtonsContainer'; -import { Caption } from '@/components/typography/Caption'; import { useMockDataForm } from '@/hooks/useMockDataForm'; import SelfDevCard from '@/images/card-dev.svg'; import IdIcon from '@/images/icons/id_icon.svg'; @@ -88,7 +87,7 @@ const MockDocumentTitleCard = () => { Generate mock document data - + Configure data parameters to generate a mock document for testing purposes on the Self Protocol. diff --git a/app/src/screens/dev/CreateMockScreenDeepLink.tsx b/app/src/screens/dev/CreateMockScreenDeepLink.tsx index 8563e344e..5f0cb8c6b 100644 --- a/app/src/screens/dev/CreateMockScreenDeepLink.tsx +++ b/app/src/screens/dev/CreateMockScreenDeepLink.tsx @@ -14,13 +14,15 @@ import { countryCodes } from '@selfxyz/common/constants'; import { getCountryISO2 } from '@selfxyz/common/constants/countries'; import type { IdDocInput } from '@selfxyz/common/utils'; import { genMockIdDocAndInitDataParsing } from '@selfxyz/common/utils/passports'; +import { + BodyText, + Description, + PrimaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; import { MockDataEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; import ButtonsContainer from '@/components/ButtonsContainer'; -import { BodyText } from '@/components/typography/BodyText'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; import type { RootStackParamList } from '@/navigation'; import { storePassportData } from '@/providers/passportDataProvider'; import useUserStore from '@/stores/userStore'; @@ -196,7 +198,7 @@ const CreateMockScreenDeepLink: React.FC = () => { > - + Onboarding your Developer ID diff --git a/app/src/screens/documents/aadhaar/AadhaarUploadErrorScreen.tsx b/app/src/screens/documents/aadhaar/AadhaarUploadErrorScreen.tsx index c5ef05f47..dd73ce08d 100644 --- a/app/src/screens/documents/aadhaar/AadhaarUploadErrorScreen.tsx +++ b/app/src/screens/documents/aadhaar/AadhaarUploadErrorScreen.tsx @@ -8,12 +8,14 @@ import type { RouteProp } from '@react-navigation/native'; import { useNavigation, useRoute } from '@react-navigation/native'; import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { + BodyText, + PrimaryButton, + SecondaryButton, +} from '@selfxyz/mobile-sdk-alpha/components'; import { AadhaarEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; import { getErrorMessages } from '@selfxyz/mobile-sdk-alpha/onboarding/import-aadhaar'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; -import { BodyText } from '@/components/typography/BodyText'; import WarningIcon from '@/images/warning.svg'; import { useSafeAreaInsets } from '@/mocks/react-native-safe-area-context'; import { black, slate100, slate200, slate500, white } from '@/utils/colors'; @@ -58,14 +60,16 @@ const AadhaarUploadErrorScreen: React.FC = () => { borderBlockWidth={1} borderBlockColor={slate200} > - + {title} {description} diff --git a/app/src/screens/documents/aadhaar/AadhaarUploadScreen.tsx b/app/src/screens/documents/aadhaar/AadhaarUploadScreen.tsx index a914e8b19..55705a51e 100644 --- a/app/src/screens/documents/aadhaar/AadhaarUploadScreen.tsx +++ b/app/src/screens/documents/aadhaar/AadhaarUploadScreen.tsx @@ -10,11 +10,10 @@ import { useNavigation } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { BodyText, PrimaryButton } from '@selfxyz/mobile-sdk-alpha/components'; import { AadhaarEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; import { useAadhaar } from '@selfxyz/mobile-sdk-alpha/onboarding/import-aadhaar'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { BodyText } from '@/components/typography/BodyText'; import { useModal } from '@/hooks/useModal'; import AadhaarImage from '@/images/512w.png'; import { useSafeAreaInsets } from '@/mocks/react-native-safe-area-context'; @@ -170,17 +169,23 @@ const AadhaarUploadScreen: React.FC = () => { borderBlockWidth={1} borderBlockColor={slate200} > - + Generate a QR code from the mAadaar app - + Save the QR code to your photo library and upload it here. SELF DOES NOT STORE THIS INFORMATION. diff --git a/app/src/screens/documents/aadhaar/AadhaarUploadedSuccessScreen.tsx b/app/src/screens/documents/aadhaar/AadhaarUploadedSuccessScreen.tsx index df161f318..2af3e05ba 100644 --- a/app/src/screens/documents/aadhaar/AadhaarUploadedSuccessScreen.tsx +++ b/app/src/screens/documents/aadhaar/AadhaarUploadedSuccessScreen.tsx @@ -8,10 +8,9 @@ import { useNavigation } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { BodyText, PrimaryButton } from '@selfxyz/mobile-sdk-alpha/components'; import { AadhaarEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { BodyText } from '@/components/typography/BodyText'; import BlueCheckIcon from '@/images/blue_check.svg'; import { useSafeAreaInsets } from '@/mocks/react-native-safe-area-context'; import type { RootStackParamList } from '@/navigation'; @@ -45,14 +44,16 @@ const AadhaarUploadedSuccessScreen: React.FC = () => { borderBlockWidth={1} borderBlockColor={slate200} > - + QR code upload successful You are ready to register your Aadhaar card with Self. diff --git a/app/src/screens/documents/management/DocumentDataInfoScreen.tsx b/app/src/screens/documents/management/DocumentDataInfoScreen.tsx index 5da9abf3c..270427f1f 100644 --- a/app/src/screens/documents/management/DocumentDataInfoScreen.tsx +++ b/app/src/screens/documents/management/DocumentDataInfoScreen.tsx @@ -10,9 +10,9 @@ import { useFocusEffect } from '@react-navigation/native'; import type { PassportMetadata } from '@selfxyz/common/types'; import type { AadhaarData } from '@selfxyz/common/utils/types'; import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { Caption } from '@selfxyz/mobile-sdk-alpha/components'; import { DocumentEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import { Caption } from '@/components/typography/Caption'; import { usePassport } from '@/providers/passportDataProvider'; import { black, slate200, white } from '@/utils/colors'; import { extraYPadding } from '@/utils/constants'; @@ -63,10 +63,8 @@ const InfoRow: React.FC<{ }> = ({ label, value }) => ( - {label} - - {value} - + {label} + {value} diff --git a/app/src/screens/documents/management/ManageDocumentsScreen.tsx b/app/src/screens/documents/management/ManageDocumentsScreen.tsx index f0e030aa7..d14eb9f47 100644 --- a/app/src/screens/documents/management/ManageDocumentsScreen.tsx +++ b/app/src/screens/documents/management/ManageDocumentsScreen.tsx @@ -15,10 +15,12 @@ import type { DocumentMetadata, } from '@selfxyz/common/utils/types'; import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { + PrimaryButton, + SecondaryButton, +} from '@selfxyz/mobile-sdk-alpha/components'; import { DocumentEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; import ButtonsContainer from '@/components/ButtonsContainer'; import type { RootStackParamList } from '@/navigation'; import { usePassport } from '@/providers/passportDataProvider'; diff --git a/app/src/screens/documents/scanning/DocumentCameraScreen.tsx b/app/src/screens/documents/scanning/DocumentCameraScreen.tsx index 890091447..0bf74f621 100644 --- a/app/src/screens/documents/scanning/DocumentCameraScreen.tsx +++ b/app/src/screens/documents/scanning/DocumentCameraScreen.tsx @@ -11,6 +11,12 @@ import { hasAnyValidRegisteredDocument, useSelfClient, } from '@selfxyz/mobile-sdk-alpha'; +import { + Additional, + Description, + SecondaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; import { PassportEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; import { mrzReadInstructions, @@ -18,12 +24,8 @@ import { } from '@selfxyz/mobile-sdk-alpha/onboarding/read-mrz'; import passportScanAnimation from '@/assets/animations/passport_scan.json'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; import { DelayedLottieView } from '@/components/DelayedLottieView'; import { PassportCamera } from '@/components/native/PassportCamera'; -import Additional from '@/components/typography/Additional'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import Scan from '@/images/icons/passport_camera_scan.svg'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; diff --git a/app/src/screens/documents/scanning/DocumentCameraTroubleScreen.tsx b/app/src/screens/documents/scanning/DocumentCameraTroubleScreen.tsx index a8d1b4e1f..765d6f1eb 100644 --- a/app/src/screens/documents/scanning/DocumentCameraTroubleScreen.tsx +++ b/app/src/screens/documents/scanning/DocumentCameraTroubleScreen.tsx @@ -4,9 +4,10 @@ import React, { useEffect } from 'react'; +import { Caption } from '@selfxyz/mobile-sdk-alpha/components'; + import type { TipProps } from '@/components/Tips'; import Tips from '@/components/Tips'; -import { Caption } from '@/components/typography/Caption'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import Activity from '@/images/icons/activity.svg'; import PassportCameraBulb from '@/images/icons/passport_camera_bulb.svg'; @@ -60,12 +61,12 @@ const DocumentCameraTroubleScreen: React.FC = () => { title="Having trouble scanning your ID?" onDismiss={go} header={ - + Here are a few tips that might help: } footer={ - + Following these steps should help your phone's camera capture the ID page quickly and clearly! diff --git a/app/src/screens/documents/scanning/DocumentNFCMethodSelectionScreen.tsx b/app/src/screens/documents/scanning/DocumentNFCMethodSelectionScreen.tsx index 092170176..326423bef 100644 --- a/app/src/screens/documents/scanning/DocumentNFCMethodSelectionScreen.tsx +++ b/app/src/screens/documents/scanning/DocumentNFCMethodSelectionScreen.tsx @@ -9,13 +9,15 @@ import { useNavigation } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { + BodyText, + Description, + PrimaryButton, + SecondaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; import ButtonsContainer from '@/components/ButtonsContainer'; -import { BodyText } from '@/components/typography/BodyText'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import type { RootStackParamList } from '@/navigation'; import { white } from '@/utils/colors'; @@ -173,7 +175,9 @@ const DocumentNFCMethodSelectionScreen: React.FC = () => { maxLength={6} /> {error ? ( - {error} + + {error} + ) : null} )} diff --git a/app/src/screens/documents/scanning/DocumentNFCScanScreen.tsx b/app/src/screens/documents/scanning/DocumentNFCScanScreen.tsx index e3b5b8284..040144bb8 100644 --- a/app/src/screens/documents/scanning/DocumentNFCScanScreen.tsx +++ b/app/src/screens/documents/scanning/DocumentNFCScanScreen.tsx @@ -36,15 +36,17 @@ import { hasAnyValidRegisteredDocument, useSelfClient, } from '@selfxyz/mobile-sdk-alpha'; +import { + BodyText, + PrimaryButton, + SecondaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; import { PassportEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; import passportVerifyAnimation from '@/assets/animations/passport_verify.json'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; import ButtonsContainer from '@/components/ButtonsContainer'; import TextsContainer from '@/components/TextsContainer'; -import { BodyText } from '@/components/typography/BodyText'; -import { Title } from '@/components/typography/Title'; import { useFeedbackAutoHide } from '@/hooks/useFeedbackAutoHide'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import NFC_IMAGE from '@/images/nfc.png'; @@ -546,7 +548,7 @@ const DocumentNFCScanScreen: React.FC = () => { <> - <BodyText textAlign="center"> + <BodyText style={{ textAlign: 'center' }}> {nfcMessage && nfcMessage.trim().length > 0 ? ( nfcMessage ) : ( @@ -588,24 +590,22 @@ const DocumentNFCScanScreen: React.FC = () => { </GestureDetector> {isNfcEnabled ? ( <> - <Title style={styles.title} marginTop="$2"> + <Title style={[styles.title, { marginTop: 8 }]}> Find the RFID chip in your ID Place your phone against the chip and keep it still until the sensor reads it. - + SELF DOES NOT STORE THIS INFORMATION. ) : ( <> - + {dialogMessage} diff --git a/app/src/screens/documents/scanning/DocumentNFCScanScreen.web.tsx b/app/src/screens/documents/scanning/DocumentNFCScanScreen.web.tsx index 1a6afeadb..73608bf5c 100644 --- a/app/src/screens/documents/scanning/DocumentNFCScanScreen.web.tsx +++ b/app/src/screens/documents/scanning/DocumentNFCScanScreen.web.tsx @@ -9,13 +9,15 @@ import { hasAnyValidRegisteredDocument, useSelfClient, } from '@selfxyz/mobile-sdk-alpha'; +import { + BodyText, + SecondaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; import { PassportEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; import ButtonsContainer from '@/components/ButtonsContainer'; import TextsContainer from '@/components/TextsContainer'; -import { BodyText } from '@/components/typography/BodyText'; -import { Title } from '@/components/typography/Title'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import NFC_IMAGE from '@/images/nfc.png'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; @@ -48,7 +50,7 @@ const DocumentNFCScanScreen: React.FC = () => { <> - <BodyText textAlign="center">TODO implement</BodyText> + <BodyText style={{ textAlign: 'center' }}>TODO implement</BodyText> </TextsContainer> <Image height="$8" diff --git a/app/src/screens/documents/scanning/DocumentNFCTroubleScreen.tsx b/app/src/screens/documents/scanning/DocumentNFCTroubleScreen.tsx index c49b3c655..107ab90b9 100644 --- a/app/src/screens/documents/scanning/DocumentNFCTroubleScreen.tsx +++ b/app/src/screens/documents/scanning/DocumentNFCTroubleScreen.tsx @@ -7,10 +7,10 @@ import { View } from 'react-native'; import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import { YStack } from 'tamagui'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; +import { Caption, SecondaryButton } from '@selfxyz/mobile-sdk-alpha/components'; + import type { TipProps } from '@/components/Tips'; import Tips from '@/components/Tips'; -import { Caption } from '@/components/typography/Caption'; import { useFeedbackAutoHide } from '@/hooks/useFeedbackAutoHide'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import SimpleScrolledTitleLayout from '@/layouts/SimpleScrolledTitleLayout'; @@ -80,7 +80,7 @@ const DocumentNFCTroubleScreen: React.FC = () => { origin: 'passport/nfc-trouble', }) } - marginBottom={0} + style={{ marginBottom: 0 }} > Report Issue </SecondaryButton> @@ -95,13 +95,13 @@ const DocumentNFCTroubleScreen: React.FC = () => { > <GestureDetector gesture={devModeTap}> <View collapsable={false}> - <Caption size="large" color={slate500}> + <Caption size="large" style={{ color: slate500 }}> Here are some tips to help you successfully scan the RFID chip: </Caption> </View> </GestureDetector> <Tips items={tips} /> - <Caption size="large" color={slate500}> + <Caption size="large" style={{ color: slate500 }}> These steps should help improve the success rate of reading the RFID chip in your passport. If the issue persists, double-check that your device supports NFC and that your passport's RFID is functioning diff --git a/app/src/screens/documents/selection/ConfirmBelongingScreen.tsx b/app/src/screens/documents/selection/ConfirmBelongingScreen.tsx index bdc9ae4d4..97d4b3eca 100644 --- a/app/src/screens/documents/selection/ConfirmBelongingScreen.tsx +++ b/app/src/screens/documents/selection/ConfirmBelongingScreen.tsx @@ -8,6 +8,11 @@ import { usePreventRemove } from '@react-navigation/native'; import type { DocumentCategory } from '@selfxyz/common/utils/types'; import { loadSelectedDocument, useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { + Description, + PrimaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; import { PassportEvents, ProofEvents, @@ -15,10 +20,7 @@ import { import { getPreRegistrationDescription } from '@selfxyz/mobile-sdk-alpha/onboarding/confirm-identification'; import successAnimation from '@/assets/animations/loading/success.json'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; import { DelayedLottieView } from '@/components/DelayedLottieView'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import { styles } from '@/screens/verification/ProofRequestStatusScreen'; @@ -144,8 +146,8 @@ const ConfirmBelongingScreen: React.FC<ConfirmBelongingScreenProps> = () => { paddingBottom={20} backgroundColor={white} > - <Title textAlign="center">Confirm your identity - + Confirm your identity + {getPreRegistrationDescription()} - + {countryName} @@ -126,10 +125,10 @@ const CountryPickerScreen: React.FC = () => { - + Select the country that issued your ID - + Self has support for over 300 ID types. You can select the type of ID in the next step @@ -141,11 +140,13 @@ const CountryPickerScreen: React.FC = () => { {showSuggestion && ( SUGGESTION @@ -156,11 +157,13 @@ const CountryPickerScreen: React.FC = () => { onSelect={onPressCountry} /> SELECT AN ISSUING COUNTRY diff --git a/app/src/screens/documents/selection/DocumentOnboardingScreen.tsx b/app/src/screens/documents/selection/DocumentOnboardingScreen.tsx index 5390b63ae..4d3dc4bbd 100644 --- a/app/src/screens/documents/selection/DocumentOnboardingScreen.tsx +++ b/app/src/screens/documents/selection/DocumentOnboardingScreen.tsx @@ -8,16 +8,18 @@ import { StyleSheet } from 'react-native'; import { SystemBars } from 'react-native-edge-to-edge'; import { useNavigation } from '@react-navigation/native'; +import { + Additional, + Description, + PrimaryButton, + SecondaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; import { PassportEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; import passportOnboardingAnimation from '@/assets/animations/passport_onboarding.json'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; import ButtonsContainer from '@/components/ButtonsContainer'; import TextsContainer from '@/components/TextsContainer'; -import Additional from '@/components/typography/Additional'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import { black, slate100, white } from '@/utils/colors'; diff --git a/app/src/screens/documents/selection/IDPickerScreen.tsx b/app/src/screens/documents/selection/IDPickerScreen.tsx index f4bd27112..229eaf6d9 100644 --- a/app/src/screens/documents/selection/IDPickerScreen.tsx +++ b/app/src/screens/documents/selection/IDPickerScreen.tsx @@ -9,14 +9,13 @@ import type { RouteProp } from '@react-navigation/native'; import { useRoute } from '@react-navigation/native'; import { SdkEvents, useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { BodyText, RoundFlag } from '@selfxyz/mobile-sdk-alpha/components'; import AadhaarLogo from '@selfxyz/mobile-sdk-alpha/svgs/icons/aadhaar.svg'; import EPassportLogoRounded from '@selfxyz/mobile-sdk-alpha/svgs/icons/epassport_rounded.svg'; import PlusIcon from '@selfxyz/mobile-sdk-alpha/svgs/icons/plus.svg'; import SelfLogo from '@selfxyz/mobile-sdk-alpha/svgs/logo.svg'; -import { RoundFlag } from '@/components/flag/RoundFlag'; import { DocumentFlowNavBar } from '@/components/NavBar/DocumentFlowNavBar'; -import { BodyText } from '@/components/typography/BodyText'; import type { RootStackParamList } from '@/navigation'; import { black, slate100, slate300, slate400, white } from '@/utils/colors'; import { extraYPadding } from '@/utils/constants'; @@ -132,10 +131,12 @@ const IDPickerScreen: React.FC = () => { Select an ID type @@ -156,10 +157,18 @@ const IDPickerScreen: React.FC = () => { {getDocumentLogo(docType)} - + {getDocumentName(docType)} - + {getDocumentDescription(docType)} @@ -167,10 +176,12 @@ const IDPickerScreen: React.FC = () => { ))} Be sure your document is ready to scan diff --git a/app/src/screens/home/ProofHistoryList.tsx b/app/src/screens/home/ProofHistoryList.tsx index 2efb38e7d..47b3ff43e 100644 --- a/app/src/screens/home/ProofHistoryList.tsx +++ b/app/src/screens/home/ProofHistoryList.tsx @@ -14,7 +14,8 @@ import { useNavigation } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { CheckSquare2, Wallet, XCircle } from '@tamagui/lucide-icons'; -import { BodyText } from '@/components/typography/BodyText'; +import { BodyText } from '@selfxyz/mobile-sdk-alpha/components'; + import type { RootStackParamList } from '@/navigation'; import { useProofHistoryStore } from '@/stores/proofHistoryStore'; import type { ProofHistory } from '@/stores/proofTypes'; @@ -231,14 +232,18 @@ export const ProofHistoryList: React.FC = ({ /> )} - + {item.appName} {formatDate(item.timestamp)} diff --git a/app/src/screens/home/ProofHistoryScreen.tsx b/app/src/screens/home/ProofHistoryScreen.tsx index 7ef9be892..15952c168 100644 --- a/app/src/screens/home/ProofHistoryScreen.tsx +++ b/app/src/screens/home/ProofHistoryScreen.tsx @@ -15,7 +15,8 @@ import { useNavigation } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { CheckSquare2, Wallet, XCircle } from '@tamagui/lucide-icons'; -import { BodyText } from '@/components/typography/BodyText'; +import { BodyText } from '@selfxyz/mobile-sdk-alpha/components'; + import type { RootStackParamList } from '@/navigation'; import { useProofHistoryStore } from '@/stores/proofHistoryStore'; import type { ProofHistory } from '@/stores/proofTypes'; @@ -238,10 +239,12 @@ const ProofHistoryScreen: React.FC = () => { /> )} - + {item.appName} - + {formatDate(item.timestamp)} diff --git a/app/src/screens/onboarding/AccountVerifiedSuccessScreen.tsx b/app/src/screens/onboarding/AccountVerifiedSuccessScreen.tsx index 9281a703f..1469839d1 100644 --- a/app/src/screens/onboarding/AccountVerifiedSuccessScreen.tsx +++ b/app/src/screens/onboarding/AccountVerifiedSuccessScreen.tsx @@ -7,13 +7,15 @@ import { YStack } from 'tamagui'; import { useNavigation } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { + Description, + PrimaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; import { BackupEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; import proofSuccessAnimation from '@/assets/animations/proof_success.json'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; import { DelayedLottieView } from '@/components/DelayedLottieView'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import type { RootStackParamList } from '@/navigation'; import { styles } from '@/screens/verification/ProofRequestStatusScreen'; diff --git a/app/src/screens/onboarding/DisclaimerScreen.tsx b/app/src/screens/onboarding/DisclaimerScreen.tsx index a428cd938..28a68253e 100644 --- a/app/src/screens/onboarding/DisclaimerScreen.tsx +++ b/app/src/screens/onboarding/DisclaimerScreen.tsx @@ -8,13 +8,15 @@ import { YStack } from 'tamagui'; import { useNavigation } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { + Caution, + PrimaryButton, + SubHeader, +} from '@selfxyz/mobile-sdk-alpha/components'; import { AppEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; import warningAnimation from '@/assets/animations/warning.json'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; import { DelayedLottieView } from '@/components/DelayedLottieView'; -import Caution from '@/components/typography/Caution'; -import { SubHeader } from '@/components/typography/SubHeader'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import type { RootStackParamList } from '@/navigation'; import { useSettingStore } from '@/stores/settingStore'; diff --git a/app/src/screens/onboarding/SaveRecoveryPhraseScreen.tsx b/app/src/screens/onboarding/SaveRecoveryPhraseScreen.tsx index 1115d014e..4b2b03550 100644 --- a/app/src/screens/onboarding/SaveRecoveryPhraseScreen.tsx +++ b/app/src/screens/onboarding/SaveRecoveryPhraseScreen.tsx @@ -4,12 +4,15 @@ import React, { useCallback, useState } from 'react'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; +import { + Caption, + Description, + PrimaryButton, + SecondaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; + import Mnemonic from '@/components/Mnemonic'; -import { Caption } from '@/components/typography/Caption'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import useMnemonic from '@/hooks/useMnemonic'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; @@ -42,10 +45,10 @@ const SaveRecoveryPhraseScreen: React.FC = () => { justifyContent="space-between" gap={10} > - + <Title style={{ paddingTop: 20, textAlign: 'center' }}> Save your recovery phrase - + This phrase is the only way to recover your account. Keep it secret, keep it safe. @@ -56,7 +59,7 @@ const SaveRecoveryPhraseScreen: React.FC = () => { backgroundColor={white} > - + You can reveal your recovery phrase in settings. diff --git a/app/src/screens/shared/ComingSoonScreen.tsx b/app/src/screens/shared/ComingSoonScreen.tsx index 8c5862523..8a480167d 100644 --- a/app/src/screens/shared/ComingSoonScreen.tsx +++ b/app/src/screens/shared/ComingSoonScreen.tsx @@ -12,13 +12,15 @@ import { hasAnyValidRegisteredDocument, useSelfClient, } from '@selfxyz/mobile-sdk-alpha'; +import { + BodyText, + PrimaryButton, + RoundFlag, + SecondaryButton, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; import { PassportEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { SecondaryButton } from '@/components/buttons/SecondaryButton'; -import { RoundFlag } from '@/components/flag/RoundFlag'; -import { BodyText } from '@/components/typography/BodyText'; -import { Title } from '@/components/typography/Title'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import analytics from '@/utils/analytics'; @@ -135,30 +137,36 @@ const ComingSoonScreen: React.FC = ({ route }) => { )} Coming Soon {documentTypeText ? `We're working to roll out support for ${documentTypeText} in ${countryName}.` : `We're working to roll out support in ${countryName}.`} Sign up for live updates. diff --git a/app/src/screens/verification/ProofRequestStatusScreen.tsx b/app/src/screens/verification/ProofRequestStatusScreen.tsx index 2271cab7e..11fe31dc8 100644 --- a/app/src/screens/verification/ProofRequestStatusScreen.tsx +++ b/app/src/screens/verification/ProofRequestStatusScreen.tsx @@ -11,16 +11,18 @@ import { ScrollView, Spinner } from 'tamagui'; import { useIsFocused } from '@react-navigation/native'; import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { + BodyText, + Description, + PrimaryButton, + Title, + typography, +} from '@selfxyz/mobile-sdk-alpha/components'; import { ProofEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; import loadingAnimation from '@/assets/animations/loading/misc.json'; import failAnimation from '@/assets/animations/proof_failed.json'; import succesAnimation from '@/assets/animations/proof_success.json'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import { BodyText } from '@/components/typography/BodyText'; -import Description from '@/components/typography/Description'; -import { typography } from '@/components/typography/styles'; -import { Title } from '@/components/typography/Title'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import { useProofHistoryStore } from '@/stores/proofHistoryStore'; diff --git a/app/src/screens/verification/ProveScreen.tsx b/app/src/screens/verification/ProveScreen.tsx index d73b4c5af..4444dbcbd 100644 --- a/app/src/screens/verification/ProveScreen.tsx +++ b/app/src/screens/verification/ProveScreen.tsx @@ -24,13 +24,15 @@ import { Eye, EyeOff } from '@tamagui/lucide-icons'; import type { SelfAppDisclosureConfig } from '@selfxyz/common/utils/appType'; import { formatEndpoint } from '@selfxyz/common/utils/scope'; import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { + BodyText, + Caption, + HeldPrimaryButtonProveScreen, +} from '@selfxyz/mobile-sdk-alpha/components'; import { ProofEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; import miscAnimation from '@/assets/animations/loading/misc.json'; -import { HeldPrimaryButtonProveScreen } from '@/components/buttons/HeldPrimaryButtonProveScreen'; import Disclosures from '@/components/Disclosures'; -import { BodyText } from '@/components/typography/BodyText'; -import { Caption } from '@/components/typography/Caption'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import type { RootStackParamList } from '@/navigation'; import { @@ -238,10 +240,14 @@ const ProveScreen: React.FC = () => { objectFit="contain" /> )} - + {url} - + {selectedApp.appName} is requesting that you prove the following information: @@ -267,10 +273,12 @@ const ProveScreen: React.FC = () => { {formattedUserId && ( {selectedApp?.userIdType === 'hex' ? 'Connected Wallet' @@ -294,15 +302,16 @@ const ProveScreen: React.FC = () => { marginRight={selectedApp?.userIdType === 'hex' ? 12 : 0} > {selectedApp?.userIdType === 'hex' && showFullAddress ? selectedApp.userId @@ -321,10 +330,12 @@ const ProveScreen: React.FC = () => { {selectedApp?.userIdType === 'hex' && ( {showFullAddress ? 'Tap to hide address' @@ -340,10 +351,12 @@ const ProveScreen: React.FC = () => { {selectedApp?.userDefinedData && ( Additional Information: @@ -353,7 +366,9 @@ const ProveScreen: React.FC = () => { borderRadius={8} marginBottom={10} > - + {selectedApp.userDefinedData} @@ -362,12 +377,14 @@ const ProveScreen: React.FC = () => { Self will confirm that these details are accurate and none of your confidential info will be revealed to {selectedApp?.appName} diff --git a/app/src/screens/verification/QRCodeTroubleScreen.tsx b/app/src/screens/verification/QRCodeTroubleScreen.tsx index 17094704d..b65266ce2 100644 --- a/app/src/screens/verification/QRCodeTroubleScreen.tsx +++ b/app/src/screens/verification/QRCodeTroubleScreen.tsx @@ -4,9 +4,10 @@ import React, { useEffect } from 'react'; +import { Caption } from '@selfxyz/mobile-sdk-alpha/components'; + import type { TipProps } from '@/components/Tips'; import Tips from '@/components/Tips'; -import { Caption } from '@/components/typography/Caption'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import SimpleScrolledTitleLayout from '@/layouts/SimpleScrolledTitleLayout'; import { flushAllAnalytics } from '@/utils/analytics'; @@ -55,7 +56,7 @@ const QRCodeTrouble: React.FC = () => { title="Having trouble scanning the QR code?" onDismiss={go} > - + Here are some tips to help you successfully scan the QR code: diff --git a/app/src/screens/verification/QRCodeViewFinderScreen.tsx b/app/src/screens/verification/QRCodeViewFinderScreen.tsx index feb2841ae..c8d2b15c2 100644 --- a/app/src/screens/verification/QRCodeViewFinderScreen.tsx +++ b/app/src/screens/verification/QRCodeViewFinderScreen.tsx @@ -14,14 +14,16 @@ import { import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { + Additional, + Description, + Title, +} from '@selfxyz/mobile-sdk-alpha/components'; import { ProofEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; import qrScanAnimation from '@/assets/animations/qr_scan.json'; import type { QRCodeScannerViewProps } from '@/components/native/QRCodeScanner'; import { QRCodeScannerView } from '@/components/native/QRCodeScanner'; -import Additional from '@/components/typography/Additional'; -import Description from '@/components/typography/Description'; -import { Title } from '@/components/typography/Title'; import useConnectionModal from '@/hooks/useConnectionModal'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import QRScan from '@/images/icons/qr_code.svg'; diff --git a/packages/mobile-sdk-alpha/README.md b/packages/mobile-sdk-alpha/README.md index 848a98c1c..3804408db 100644 --- a/packages/mobile-sdk-alpha/README.md +++ b/packages/mobile-sdk-alpha/README.md @@ -21,7 +21,70 @@ Alpha SDK for registering and proving. Adapters-first, React Native-first with w - The `browser` build replaces the scanner with `webNFCScannerShim`, which throws for NFC scanning (not supported on web). -## Quick start (local, monorepo) +## Installation & Setup + +### 1. Install the package + +```bash +npm install @selfxyz/mobile-sdk-alpha +# or +yarn add @selfxyz/mobile-sdk-alpha +``` + +### 2. Link native dependencies and assets + +The SDK includes custom fonts that need to be linked to your app: + +#### Automatic Linking (Recommended) + +If your app uses React Native autolinking (RN 0.60+), run: + +```bash +npx react-native-asset +# or +yarn react-native-asset +``` + +This will automatically copy the font files to your iOS and Android projects. + +#### Manual Linking + +If autolinking doesn't work or you need manual control: + +**iOS:** + +1. Add fonts to your Xcode project: + - Open your Xcode workspace + - Drag the font files from `node_modules/@selfxyz/mobile-sdk-alpha/assets/fonts/` to your project + - Ensure "Copy items if needed" is checked + - Add to your app target + +2. Update `Info.plist` to include the fonts: + ```xml + UIAppFonts + + Advercase-Regular.otf + DINOT-Medium.otf + IBMPlexMono-Regular.otf + + ``` + +**Android:** + +1. Copy font files to your Android project: + + ```bash + cp node_modules/@selfxyz/mobile-sdk-alpha/assets/fonts/* android/app/src/main/assets/fonts/ + ``` + +2. If the `fonts` directory doesn't exist, create it: + ```bash + mkdir -p android/app/src/main/assets/fonts + ``` + +The fonts will be automatically available to your app. + +### 3. Initialize the SDK Provide `scanner`, `network`, and `crypto` adapters. `storage`, `clock`, and `logger` default to no-ops. diff --git a/packages/mobile-sdk-alpha/assets/fonts/Advercase-Regular.otf b/packages/mobile-sdk-alpha/assets/fonts/Advercase-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..e8f7f49ee647a6cf402fb76f850591a6ba223e9c GIT binary patch literal 69692 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 literal 0 HcmV?d00001 diff --git a/packages/mobile-sdk-alpha/assets/fonts/IBMPlexMono-Regular.otf b/packages/mobile-sdk-alpha/assets/fonts/IBMPlexMono-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..e5b6bfed7a43c85006feae87acb008e214c30a52 GIT binary patch literal 82328 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%0AKxA4O%6?^QOYTo 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 void; } @@ -28,11 +23,8 @@ interface AbstractButtonProps extends ButtonProps { borderColor?: string; borderWidth?: number; color: string; - onPress?: ((e: GestureResponderEvent) => void) | null | undefined; } -const { trackEvent: analyticsTrackEvent } = analytics(); - /* Base Button component that can be used to create different types of buttons use PrimaryButton and SecondaryButton instead of this component or create a new button component @@ -51,6 +43,7 @@ export default function AbstractButton({ onPress, ...props }: AbstractButtonProps) { + const selfClient = useSelfClient(); const hasBorder = borderColor ? true : false; const handlePress = (e: GestureResponderEvent) => { @@ -60,7 +53,7 @@ export default function AbstractButton({ if (parsedEvent) { trackEvent = parsedEvent; } - analyticsTrackEvent(`Click: ${trackEvent}`); + selfClient.trackEvent(`Click: ${trackEvent}`); } if (onPress) { onPress(e); @@ -68,11 +61,10 @@ export default function AbstractButton({ }; return ( - + ); } diff --git a/app/src/components/buttons/HeldPrimaryButtonProveScreen.tsx b/packages/mobile-sdk-alpha/src/components/buttons/HeldPrimaryButtonProveScreen.tsx similarity index 94% rename from app/src/components/buttons/HeldPrimaryButtonProveScreen.tsx rename to packages/mobile-sdk-alpha/src/components/buttons/HeldPrimaryButtonProveScreen.tsx index 05aa961ab..72be9f09f 100644 --- a/app/src/components/buttons/HeldPrimaryButtonProveScreen.tsx +++ b/packages/mobile-sdk-alpha/src/components/buttons/HeldPrimaryButtonProveScreen.tsx @@ -2,16 +2,17 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import React, { useEffect } from 'react'; +import type React from 'react'; +import { useEffect } from 'react'; import { ActivityIndicator, View } from 'react-native'; import { assign, createMachine } from 'xstate'; -import { useMachine } from '@xstate/react'; -import { ProofEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; +import { ProofEvents } from '../../constants/analytics'; +import { black } from '../../constants/colors'; +import Description from '../typography/Description'; +import { HeldPrimaryButton } from './PrimaryButtonLongHold'; -import { HeldPrimaryButton } from '@/components/buttons/PrimaryButtonLongHold'; -import Description from '@/components/typography/Description'; -import { black } from '@/utils/colors'; +import { useMachine } from '@xstate/react'; interface HeldPrimaryButtonProveScreenProps { onVerify: () => void; @@ -184,9 +185,7 @@ const buttonMachine = createMachine( }, ); -export const HeldPrimaryButtonProveScreen: React.FC< - HeldPrimaryButtonProveScreenProps -> = ({ +export const HeldPrimaryButtonProveScreen: React.FC = ({ onVerify, selectedAppSessionId, hasScrolledToBottom, diff --git a/app/src/components/buttons/PrimaryButton.tsx b/packages/mobile-sdk-alpha/src/components/buttons/PrimaryButton.tsx similarity index 72% rename from app/src/components/buttons/PrimaryButton.tsx rename to packages/mobile-sdk-alpha/src/components/buttons/PrimaryButton.tsx index 4c2ce4eb6..99bfa3a28 100644 --- a/app/src/components/buttons/PrimaryButton.tsx +++ b/packages/mobile-sdk-alpha/src/components/buttons/PrimaryButton.tsx @@ -2,12 +2,10 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import React from 'react'; - -import type { ButtonProps } from '@/components/buttons/AbstractButton'; -import AbstractButton from '@/components/buttons/AbstractButton'; -import { amber50, black, slate300, white } from '@/utils/colors'; -import { normalizeBorderWidth } from '@/utils/styleUtils'; +import { amber50, black, slate300, white } from '../../constants/colors'; +import { normalizeBorderWidth } from '../../utils/styleUtils'; +import type { ButtonProps } from './AbstractButton'; +import AbstractButton from './AbstractButton'; export function PrimaryButton({ children, ...props }: ButtonProps) { const { borderWidth, ...restProps } = props; diff --git a/app/src/components/buttons/PrimaryButtonLongHold.shared.ts b/packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.shared.ts similarity index 86% rename from app/src/components/buttons/PrimaryButtonLongHold.shared.ts rename to packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.shared.ts index 03e9c5087..fcad55581 100644 --- a/app/src/components/buttons/PrimaryButtonLongHold.shared.ts +++ b/packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.shared.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import type { ButtonProps } from '@/components/buttons/AbstractButton'; +import type { ButtonProps } from './AbstractButton'; export interface HeldPrimaryButtonProps extends ButtonProps { onLongPress: () => void; diff --git a/app/src/components/buttons/PrimaryButtonLongHold.tsx b/packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.tsx similarity index 85% rename from app/src/components/buttons/PrimaryButtonLongHold.tsx rename to packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.tsx index f6aecedf6..ef819d641 100644 --- a/app/src/components/buttons/PrimaryButtonLongHold.tsx +++ b/packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.tsx @@ -2,22 +2,15 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import type { LayoutChangeEvent } from 'react-native'; import { Animated, StyleSheet, useAnimatedValue } from 'react-native'; -import { PrimaryButton } from '@/components/buttons/PrimaryButton'; -import type { HeldPrimaryButtonProps } from '@/components/buttons/PrimaryButtonLongHold.shared'; -import { - ACTION_TIMER, - COLORS, -} from '@/components/buttons/PrimaryButtonLongHold.shared'; +import { PrimaryButton } from './PrimaryButton'; +import type { HeldPrimaryButtonProps } from './PrimaryButtonLongHold.shared'; +import { ACTION_TIMER, COLORS } from './PrimaryButtonLongHold.shared'; -export function HeldPrimaryButton({ - children, - onLongPress, - ...props -}: HeldPrimaryButtonProps) { +export function HeldPrimaryButton({ children, onLongPress, ...props }: HeldPrimaryButtonProps) { const [hasTriggered, setHasTriggered] = useState(false); const [size, setSize] = useState({ width: 0, height: 0 }); diff --git a/packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.web.tsx b/packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.web.tsx new file mode 100644 index 000000000..5b9a7bb40 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.web.tsx @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import { useEffect, useState } from 'react'; +import type { LayoutChangeEvent } from 'react-native'; +import { Animated } from 'react-native'; + +import { PrimaryButton } from './PrimaryButton'; +import type { HeldPrimaryButtonProps } from './PrimaryButtonLongHold.shared'; +import { ACTION_TIMER, COLORS } from './PrimaryButtonLongHold.shared'; + +export function HeldPrimaryButton({ children, onLongPress, ...props }: HeldPrimaryButtonProps) { + const [hasTriggered, setHasTriggered] = useState(false); + const [size, setSize] = useState({ width: 0, height: 0 }); + const [isPressed, setIsPressed] = useState(false); + const animationValue = new Animated.Value(0); + + const onPressIn = () => { + setHasTriggered(false); + setIsPressed(true); + Animated.timing(animationValue, { + toValue: 1, + duration: ACTION_TIMER, + useNativeDriver: false, + }).start(); + }; + + const onPressOut = () => { + setIsPressed(false); + if (!hasTriggered) { + Animated.timing(animationValue, { + toValue: 0, + duration: 200, + useNativeDriver: false, + }).start(); + } + }; + + const getButtonSize = (e: LayoutChangeEvent) => { + const width = e.nativeEvent.layout.width - 1; + const height = e.nativeEvent.layout.height - 1; + setSize({ width, height }); + }; + + useEffect(() => { + // Use animation listener to trigger onLongPress + const listener = animationValue.addListener(({ value }) => { + if (value >= 0.95 && !hasTriggered && isPressed) { + setHasTriggered(true); + onLongPress(); + } + }); + return () => { + animationValue.removeListener(listener); + }; + }, [animationValue, hasTriggered, onLongPress, isPressed]); + + const renderAnimatedComponent = () => { + // Use React Native Animated.View for consistent behavior + const width = animationValue.interpolate({ + inputRange: [0, 1], + outputRange: [0, size.width], + }); + + return ( + + ); + }; + + return ( + + {children} + + ); +} diff --git a/app/src/components/buttons/SecondaryButton.tsx b/packages/mobile-sdk-alpha/src/components/buttons/SecondaryButton.tsx similarity index 71% rename from app/src/components/buttons/SecondaryButton.tsx rename to packages/mobile-sdk-alpha/src/components/buttons/SecondaryButton.tsx index 935150e82..0212bd9c8 100644 --- a/app/src/components/buttons/SecondaryButton.tsx +++ b/packages/mobile-sdk-alpha/src/components/buttons/SecondaryButton.tsx @@ -2,12 +2,10 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import React from 'react'; - -import type { ButtonProps } from '@/components/buttons/AbstractButton'; -import AbstractButton from '@/components/buttons/AbstractButton'; -import { slate200, slate300, slate500, white } from '@/utils/colors'; -import { normalizeBorderWidth } from '@/utils/styleUtils'; +import { slate200, slate300, slate500, white } from '../../constants/colors'; +import { normalizeBorderWidth } from '../../utils/styleUtils'; +import type { ButtonProps } from './AbstractButton'; +import AbstractButton from './AbstractButton'; export function SecondaryButton({ children, ...props }: ButtonProps) { const { borderWidth, ...restProps } = props; diff --git a/app/src/components/buttons/pressedStyle.tsx b/packages/mobile-sdk-alpha/src/components/buttons/pressedStyle.tsx similarity index 100% rename from app/src/components/buttons/pressedStyle.tsx rename to packages/mobile-sdk-alpha/src/components/buttons/pressedStyle.tsx diff --git a/app/src/components/flag/RoundFlag.tsx b/packages/mobile-sdk-alpha/src/components/flag/RoundFlag.tsx similarity index 79% rename from app/src/components/flag/RoundFlag.tsx rename to packages/mobile-sdk-alpha/src/components/flag/RoundFlag.tsx index a5c06da3e..86c0a7ca0 100644 --- a/app/src/components/flag/RoundFlag.tsx +++ b/packages/mobile-sdk-alpha/src/components/flag/RoundFlag.tsx @@ -2,13 +2,13 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import React from 'react'; +import type React from 'react'; import { View } from 'react-native'; import * as CountryFlags from 'react-native-svg-circle-country-flags'; import { alpha3ToAlpha2 } from '@selfxyz/common/constants/countries'; -import { slate300 } from '@/utils/colors'; +import { slate300 } from '../../constants/colors'; type CountryFlagComponent = React.ComponentType<{ width: number; @@ -22,16 +22,15 @@ interface RoundFlagProps { size: number; } -const findFlagComponent = (formattedCode: string) => { +const findFlagComponent = (CountryFlags: CountryFlagsRecord, formattedCode: string) => { const patterns = [ formattedCode, formattedCode.toLowerCase(), - formattedCode.charAt(0).toUpperCase() + - formattedCode.charAt(1).toLowerCase(), + formattedCode.charAt(0).toUpperCase() + formattedCode.charAt(1).toLowerCase(), ]; for (const pattern of patterns) { - const component = (CountryFlags as unknown as CountryFlagsRecord)[pattern]; + const component = CountryFlags[pattern]; if (component) { return component; } @@ -48,7 +47,7 @@ const getCountryFlag = (countryCode: string): CountryFlagComponent | null => { } const formattedCode = iso2.toUpperCase(); - return findFlagComponent(formattedCode); + return findFlagComponent(CountryFlags as unknown as CountryFlagsRecord, formattedCode); } catch (error) { console.error('Error getting country flag:', error); return null; @@ -64,6 +63,7 @@ export const RoundFlag: React.FC = ({ countryCode, size }) => { style={{ width: size, height: size, + borderRadius: size / 2, backgroundColor: slate300, }} /> diff --git a/packages/mobile-sdk-alpha/src/components/index.ts b/packages/mobile-sdk-alpha/src/components/index.ts index 595a0e96a..2be12d7e2 100644 --- a/packages/mobile-sdk-alpha/src/components/index.ts +++ b/packages/mobile-sdk-alpha/src/components/index.ts @@ -2,4 +2,37 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. +export { default as AbstractButton } from './buttons/AbstractButton'; + +export { default as Additional } from './typography/Additional'; + +// Typography components +export { BodyText } from './typography/BodyText'; + +export { Caption } from './typography/Caption'; + +export { default as Caution } from './typography/Caution'; + +export { default as Description } from './typography/Description'; + +export { DescriptionTitle } from './typography/DescriptionTitle'; + +export { HeldPrimaryButton } from './buttons/PrimaryButtonLongHold'; + +export { HeldPrimaryButtonProveScreen } from './buttons/HeldPrimaryButtonProveScreen'; + export { MRZScannerView } from './MRZScannerView'; +// Button components +export { PrimaryButton } from './buttons/PrimaryButton'; +// Flag components +export { RoundFlag } from './flag/RoundFlag'; + +export { SecondaryButton } from './buttons/SecondaryButton'; + +export { SubHeader } from './typography/SubHeader'; + +export { Title } from './typography/Title'; + +export { pressedStyle } from './buttons/pressedStyle'; + +export { typography } from './typography/styles'; diff --git a/packages/mobile-sdk-alpha/src/components/screens/NFCScannerScreen.tsx b/packages/mobile-sdk-alpha/src/components/screens/NFCScannerScreen.tsx index ef7b17e92..a1f17fed7 100644 --- a/packages/mobile-sdk-alpha/src/components/screens/NFCScannerScreen.tsx +++ b/packages/mobile-sdk-alpha/src/components/screens/NFCScannerScreen.tsx @@ -3,8 +3,7 @@ // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. import { useCallback } from 'react'; -import { StyleSheet, Text, TouchableOpacity } from 'react-native'; -import { View } from 'tamagui'; +import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { getSKIPEM, initPassportDataParsing } from '@selfxyz/common'; diff --git a/packages/mobile-sdk-alpha/src/components/screens/PassportCameraScreen.tsx b/packages/mobile-sdk-alpha/src/components/screens/PassportCameraScreen.tsx index 5e78c36b6..8400b105a 100644 --- a/packages/mobile-sdk-alpha/src/components/screens/PassportCameraScreen.tsx +++ b/packages/mobile-sdk-alpha/src/components/screens/PassportCameraScreen.tsx @@ -2,8 +2,7 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import { StyleSheet, Text, TouchableOpacity } from 'react-native'; -import { View } from 'tamagui'; +import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import type { PassportCameraProps } from '../../types/ui'; import { MRZScannerView } from '../MRZScannerView'; diff --git a/packages/mobile-sdk-alpha/src/components/screens/QRCodeScreen.tsx b/packages/mobile-sdk-alpha/src/components/screens/QRCodeScreen.tsx index 1320a0ab9..4e5aa4e17 100644 --- a/packages/mobile-sdk-alpha/src/components/screens/QRCodeScreen.tsx +++ b/packages/mobile-sdk-alpha/src/components/screens/QRCodeScreen.tsx @@ -2,8 +2,7 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import { StyleSheet, Text, TouchableOpacity } from 'react-native'; -import { View } from 'tamagui'; +import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import type { ScreenProps } from '../../types/ui'; diff --git a/app/src/components/typography/Additional.tsx b/packages/mobile-sdk-alpha/src/components/typography/Additional.tsx similarity index 86% rename from app/src/components/typography/Additional.tsx rename to packages/mobile-sdk-alpha/src/components/typography/Additional.tsx index 1aac09b21..5aa93c0be 100644 --- a/app/src/components/typography/Additional.tsx +++ b/packages/mobile-sdk-alpha/src/components/typography/Additional.tsx @@ -2,12 +2,11 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import React from 'react'; import type { TextProps } from 'react-native'; import { StyleSheet, Text } from 'react-native'; -import { slate400 } from '@/utils/colors'; -import { dinot } from '@/utils/fonts'; +import { slate400 } from '../../constants/colors'; +import { dinot } from '../../utils/fonts'; type AdditionalProps = TextProps; diff --git a/packages/mobile-sdk-alpha/src/components/typography/BodyText.tsx b/packages/mobile-sdk-alpha/src/components/typography/BodyText.tsx new file mode 100644 index 000000000..6f8c15dac --- /dev/null +++ b/packages/mobile-sdk-alpha/src/components/typography/BodyText.tsx @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import type React from 'react'; +import type { TextProps } from 'react-native'; +import { Text } from 'react-native'; + +import { dinot } from '../../utils/fonts'; + +export const BodyText: React.FC = ({ style, ...props }) => ( + +); diff --git a/packages/mobile-sdk-alpha/src/components/typography/Caption.tsx b/packages/mobile-sdk-alpha/src/components/typography/Caption.tsx new file mode 100644 index 000000000..9d9c545d9 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/components/typography/Caption.tsx @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import type React from 'react'; +import type { TextProps } from 'react-native'; + +import { slate400 } from '../../constants/colors'; +import { BodyText } from './BodyText'; + +type CaptionProps = TextProps & { + size?: 'small' | 'large'; +}; + +export const Caption: React.FC = ({ size, style, ...props }) => { + const fontSize = size === 'small' ? 14 : size === 'large' ? 16 : 15; + + return ; +}; diff --git a/app/src/components/typography/Caution.tsx b/packages/mobile-sdk-alpha/src/components/typography/Caution.tsx similarity index 85% rename from app/src/components/typography/Caution.tsx rename to packages/mobile-sdk-alpha/src/components/typography/Caution.tsx index 42dd54056..86556e558 100644 --- a/app/src/components/typography/Caution.tsx +++ b/packages/mobile-sdk-alpha/src/components/typography/Caution.tsx @@ -2,12 +2,11 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import React from 'react'; import type { TextProps } from 'react-native'; import { StyleSheet, Text } from 'react-native'; -import { slate700 } from '@/utils/colors'; -import { dinot } from '@/utils/fonts'; +import { slate700 } from '../../constants/colors'; +import { dinot } from '../../utils/fonts'; type CautionProps = TextProps; diff --git a/packages/mobile-sdk-alpha/src/components/typography/Description.tsx b/packages/mobile-sdk-alpha/src/components/typography/Description.tsx new file mode 100644 index 000000000..43b845381 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/components/typography/Description.tsx @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import type { TextProps } from 'react-native'; +import { StyleSheet, Text } from 'react-native'; + +import { slate500 } from '../../constants/colors'; +import { dinot } from '../../utils/fonts'; + +type DescriptionProps = TextProps & { + color?: string; +}; + +const Description = ({ children, style, color, ...props }: DescriptionProps) => { + return ( + + {children} + + ); +}; + +export default Description; + +const styles = StyleSheet.create({ + description: { + color: slate500, + fontSize: 18, + lineHeight: 23, + textAlign: 'center', + fontFamily: dinot, + }, +}); diff --git a/packages/mobile-sdk-alpha/src/components/typography/DescriptionTitle.tsx b/packages/mobile-sdk-alpha/src/components/typography/DescriptionTitle.tsx new file mode 100644 index 000000000..1ea05aa84 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/components/typography/DescriptionTitle.tsx @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import type React from 'react'; +import type { TextProps } from 'react-native'; +import { Text } from 'react-native'; + +import { dinot } from '../../utils/fonts'; + +export const DescriptionTitle: React.FC = ({ style, ...props }) => ( + +); diff --git a/packages/mobile-sdk-alpha/src/components/typography/SubHeader.tsx b/packages/mobile-sdk-alpha/src/components/typography/SubHeader.tsx new file mode 100644 index 000000000..12f0c1009 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/components/typography/SubHeader.tsx @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import type React from 'react'; +import type { TextProps } from 'react-native'; +import { Text } from 'react-native'; + +import { dinot } from '../../utils/fonts'; + +export const SubHeader: React.FC = ({ style, ...props }) => ( + +); diff --git a/packages/mobile-sdk-alpha/src/components/typography/Title.tsx b/packages/mobile-sdk-alpha/src/components/typography/Title.tsx new file mode 100644 index 000000000..938752634 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/components/typography/Title.tsx @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import type React from 'react'; +import type { StyleProp, TextProps, TextStyle } from 'react-native'; +import { Text } from 'react-native'; + +import { advercase } from '../../utils/fonts'; + +type TitleProps = TextProps & { + size?: 'large'; + style?: StyleProp; +}; + +export const Title: React.FC = ({ size, style, children, ...rest }) => { + const baseStyle: TextStyle = { + fontSize: 28, + lineHeight: 35, + fontFamily: advercase, + }; + + const largeStyle: TextStyle = + size === 'large' + ? { + fontSize: 38, + lineHeight: 47, + } + : {}; + + return ( + + {children} + + ); +}; diff --git a/app/src/components/typography/styles.ts b/packages/mobile-sdk-alpha/src/components/typography/styles.ts similarity index 86% rename from app/src/components/typography/styles.ts rename to packages/mobile-sdk-alpha/src/components/typography/styles.ts index 0ba262674..f123b9cce 100644 --- a/app/src/components/typography/styles.ts +++ b/packages/mobile-sdk-alpha/src/components/typography/styles.ts @@ -4,7 +4,7 @@ import { StyleSheet } from 'react-native'; -import { black } from '@/utils/colors'; +import { black } from '../../constants/colors'; export const typography = StyleSheet.create({ strong: { diff --git a/packages/mobile-sdk-alpha/src/utils/fonts.ts b/packages/mobile-sdk-alpha/src/utils/fonts.ts new file mode 100644 index 000000000..c47b2f486 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/utils/fonts.ts @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import { Platform } from 'react-native'; + +export const advercase = 'Advercase-Regular'; +export const dinot = 'DINOT-Medium'; +export const plexMono = Platform.OS === 'ios' ? 'IBM Plex Mono' : 'IBMPlexMono-Regular'; diff --git a/packages/mobile-sdk-alpha/src/utils/styleUtils.ts b/packages/mobile-sdk-alpha/src/utils/styleUtils.ts new file mode 100644 index 000000000..2c76debf1 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/utils/styleUtils.ts @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +/** + * Utility functions for style, design, and layout operations. + */ + +/** + * Normalizes borderWidth value. + * Validates and converts borderWidth to a non-negative number or undefined. + * @param borderWidth - The borderWidth value to normalize + * @returns Normalized borderWidth (non-negative number) or undefined + */ +export function normalizeBorderWidth(borderWidth: unknown): number | undefined { + if (typeof borderWidth === 'number' && borderWidth >= 0) { + return borderWidth; + } + return undefined; +} diff --git a/packages/mobile-sdk-alpha/tsup.config.ts b/packages/mobile-sdk-alpha/tsup.config.ts index 5271cfefe..6d5144aa9 100644 --- a/packages/mobile-sdk-alpha/tsup.config.ts +++ b/packages/mobile-sdk-alpha/tsup.config.ts @@ -39,6 +39,7 @@ const entry = { browser: 'src/browser.ts', 'constants/analytics': 'src/constants/analytics.ts', 'constants/colors': 'src/constants/colors.ts', + 'components/index': 'src/components/index.ts', stores: 'src/stores/index.ts', ...flowEntries, }; @@ -69,6 +70,7 @@ export default defineConfig([ '@react-native-async-storage/async-storage', 'react-native-keychain', 'react-native-sqlite-storage', + // State management (xstate included in bundle) ], esbuildOptions(options) { options.supported = { @@ -113,6 +115,7 @@ export default defineConfig([ '@react-native-async-storage/async-storage', 'react-native-keychain', 'react-native-sqlite-storage', + // State management (xstate included in bundle) ], outExtension: ({ format }) => ({ js: format === 'cjs' ? '.cjs' : '.js' }), esbuildOptions(options) { diff --git a/yarn.lock b/yarn.lock index 2fe87e5ba..ba96bd4fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7416,7 +7416,6 @@ __metadata: react-native-screens: "npm:4.15.3" react-native-sqlite-storage: "npm:^6.0.1" react-native-svg: "npm:15.12.1" - react-native-svg-circle-country-flags: "npm:^0.2.2" react-native-svg-transformer: "npm:^1.5.1" react-native-svg-web: "npm:^1.0.9" react-native-web: "npm:^0.19.0" @@ -7443,12 +7442,12 @@ __metadata: dependencies: "@babel/runtime": "npm:^7.28.3" "@selfxyz/common": "workspace:^" - "@tamagui/types": "npm:1.126.14" "@testing-library/react": "npm:^14.1.2" "@types/react": "npm:^18.3.4" "@types/react-dom": "npm:^18.3.0" "@typescript-eslint/eslint-plugin": "npm:^8.0.0" "@typescript-eslint/parser": "npm:^8.0.0" + "@xstate/react": "npm:^5.0.5" eslint: "npm:^8.57.0" eslint-config-prettier: "npm:^10.1.8" eslint-import-resolver-typescript: "npm:^4.4.4" @@ -7464,9 +7463,9 @@ __metadata: react-dom: "npm:^18.3.1" react-native: "npm:0.76.9" react-native-localize: "npm:^3.5.2" + react-native-svg-circle-country-flags: "npm:^0.2.2" react-native-web: "npm:^0.21.1" socket.io-client: "npm:^4.8.1" - tamagui: "npm:1.126.14" tsup: "npm:^8.0.1" typescript: "npm:^5.9.2" uuid: "npm:^11.1.0" @@ -7477,10 +7476,7 @@ __metadata: react: ^18.3.1 react-native: 0.76.9 react-native-localize: "*" - tamagui: 1.126.x - peerDependenciesMeta: - tamagui: - optional: false + react-native-svg: "*" languageName: unknown linkType: soft @@ -12914,7 +12910,7 @@ __metadata: languageName: node linkType: hard -"@xstate/react@npm:^5.0.3": +"@xstate/react@npm:^5.0.3, @xstate/react@npm:^5.0.5": version: 5.0.5 resolution: "@xstate/react@npm:5.0.5" dependencies: From 6fbb3cc6b93113e3189e0d0069b0d36bb2d73430 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 11 Oct 2025 03:26:08 -0700 Subject: [PATCH 2/6] chore: bump mobile app version to 2.6.9 [skip ci] (#1265) Co-authored-by: github-actions[bot] --- app/version.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/version.json b/app/version.json index fd428910f..25e3f1a7d 100644 --- a/app/version.json +++ b/app/version.json @@ -1,10 +1,10 @@ { "ios": { - "build": 179, + "build": 180, "lastDeployed": "2025-10-07T05:58:42Z" }, "android": { - "build": 108, + "build": 109, "lastDeployed": "2025-10-01T08:00:07Z" } } From 54e65845fab381bd63b0830204e09fdeccd7210c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 11 Oct 2025 03:26:08 -0700 Subject: [PATCH 3/6] chore: bump mobile app version to 2.6.9 (#1265) --- app/version.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/version.json b/app/version.json index fd428910f..25e3f1a7d 100644 --- a/app/version.json +++ b/app/version.json @@ -1,10 +1,10 @@ { "ios": { - "build": 179, + "build": 180, "lastDeployed": "2025-10-07T05:58:42Z" }, "android": { - "build": 108, + "build": 109, "lastDeployed": "2025-10-01T08:00:07Z" } } From 8fc31912d38ca55a440a81abf76262794e28bbf1 Mon Sep 17 00:00:00 2001 From: Justin Hernandez Date: Sat, 11 Oct 2025 03:39:36 -0700 Subject: [PATCH 4/6] remove ci (#1270) --- .github/workflows/mobile-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mobile-deploy.yml b/.github/workflows/mobile-deploy.yml index 274a4c3ab..4c360eedf 100644 --- a/.github/workflows/mobile-deploy.yml +++ b/.github/workflows/mobile-deploy.yml @@ -1347,7 +1347,7 @@ jobs: exit 0 fi - git commit -m "chore: bump mobile app version to $VERSION [skip ci]" + git commit -m "chore: bump mobile app version to $VERSION" # Create new branch from current HEAD (bump target branch with version bump) git checkout -b ${BRANCH_NAME} From 38e2bea6c0551c4847570e26b772d4c367c0c21f Mon Sep 17 00:00:00 2001 From: Justin Hernandez Date: Sat, 11 Oct 2025 12:20:38 -0700 Subject: [PATCH 5/6] bugfix: update migrated components default text color (#1271) * fix caption and title text color * fix nfc body text color * fix issues * use memo --- packages/mobile-sdk-alpha/README.md | 107 ++++++++++++++++-- .../buttons/PrimaryButtonLongHold.web.tsx | 5 +- .../src/components/typography/BodyText.tsx | 3 +- .../src/components/typography/Caption.tsx | 6 +- .../src/components/typography/Title.tsx | 9 +- 5 files changed, 114 insertions(+), 16 deletions(-) diff --git a/packages/mobile-sdk-alpha/README.md b/packages/mobile-sdk-alpha/README.md index 3804408db..6186e3788 100644 --- a/packages/mobile-sdk-alpha/README.md +++ b/packages/mobile-sdk-alpha/README.md @@ -37,15 +37,25 @@ The SDK includes custom fonts that need to be linked to your app: #### Automatic Linking (Recommended) -If your app uses React Native autolinking (RN 0.60+), run: +React Native autolinking (RN 0.60+) does not link assets by default. First, configure your app's assets: + +Create or update `react-native.config.js` at the app root: + +```js +module.exports = { + assets: ['./node_modules/@selfxyz/mobile-sdk-alpha/assets/fonts'], +}; +``` + +Then run: ```bash npx react-native-asset -# or -yarn react-native-asset +# or (Yarn 2+) +yarn dlx react-native-asset ``` -This will automatically copy the font files to your iOS and Android projects. +This copies the font files to your iOS and Android projects. #### Manual Linking @@ -71,20 +81,38 @@ If autolinking doesn't work or you need manual control: **Android:** -1. Copy font files to your Android project: +1. Ensure the `fonts` directory exists: ```bash - cp node_modules/@selfxyz/mobile-sdk-alpha/assets/fonts/* android/app/src/main/assets/fonts/ + mkdir -p android/app/src/main/assets/fonts ``` -2. If the `fonts` directory doesn't exist, create it: +2. Copy font files to your Android project: ```bash - mkdir -p android/app/src/main/assets/fonts + cp node_modules/@selfxyz/mobile-sdk-alpha/assets/fonts/* android/app/src/main/assets/fonts/ ``` The fonts will be automatically available to your app. -### 3. Initialize the SDK +### 3. Install peer dependencies + +This SDK requires `react-native-svg` as a peer dependency. Install it in your app: + +```bash +npm install react-native-svg +# or +yarn add react-native-svg +``` + +**Minimum required version:** `react-native-svg@*` (any version compatible with your React Native version) + +For iOS, run `pod install` after installation: + +```bash +cd ios && pod install && cd .. +``` + +### 4. Initialize the SDK Provide `scanner`, `network`, and `crypto` adapters. `storage`, `clock`, and `logger` default to no-ops. @@ -100,6 +128,67 @@ const sdk = createSelfClient({ }); ``` +## Migration from Tamagui + +If you're upgrading from a Tamagui-based version of this SDK, please note the following breaking changes: + +### Breaking Changes + +**1. UI Component System** + +- **Removed:** Tamagui dependency and Tamagui-based components +- **Added:** Custom React Native components with direct styling +- **Impact:** Any custom theme overrides or Tamagui-specific configurations will need to be replaced + +**2. Font System** + +- **Changed:** Fonts are now bundled directly with the package +- **Required:** Manual font linking step (see installation section above) +- **Impact:** You must run `react-native-asset` or manually link fonts + +**3. Peer Dependencies** + +- **Added:** `react-native-svg` is now a required peer dependency +- **Required:** Install `react-native-svg` in your app +- **Impact:** SVG-based UI components now use `react-native-svg` directly + +### Upgrade Steps + +1. **Remove Tamagui dependencies** (if you installed them specifically for this SDK): + + ```bash + # Only if these were installed solely for the SDK + npm uninstall @tamagui/core @tamagui/config + ``` + +2. **Install required peer dependencies:** + + ```bash + npm install react-native-svg + cd ios && pod install && cd .. + ``` + +3. **Link fonts** following the asset linking instructions in the installation section above + +4. **Update your imports** - Component imports remain the same, but internal implementation has changed: + + ```ts + // These imports still work + import { PrimaryButton, Title, Body } from '@selfxyz/mobile-sdk-alpha/components'; + ``` + +5. **Remove Tamagui configuration** - If you had Tamagui config specifically for this SDK, it's no longer needed + +6. **Test your UI** - Components now use platform-native styling instead of Tamagui + +### Style Customization + +Component styling is no longer customizable via Tamagui themes. The SDK now uses fixed styles optimized for the verification flow. If you need to customize UI: + +- Use the component composition patterns provided by the SDK +- Wrap SDK components with your own styled containers +- Use the `style` prop where available + ## SDK Events The SDK emits events throughout the verification lifecycle. Subscribe using `selfClient.on(event, callback)`. diff --git a/packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.web.tsx b/packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.web.tsx index 5b9a7bb40..acefe14c4 100644 --- a/packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.web.tsx +++ b/packages/mobile-sdk-alpha/src/components/buttons/PrimaryButtonLongHold.web.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import type { LayoutChangeEvent } from 'react-native'; import { Animated } from 'react-native'; @@ -14,7 +14,8 @@ export function HeldPrimaryButton({ children, onLongPress, ...props }: HeldPrima const [hasTriggered, setHasTriggered] = useState(false); const [size, setSize] = useState({ width: 0, height: 0 }); const [isPressed, setIsPressed] = useState(false); - const animationValue = new Animated.Value(0); + const animationValueRef = useRef(new Animated.Value(0)); + const animationValue = animationValueRef.current; const onPressIn = () => { setHasTriggered(false); diff --git a/packages/mobile-sdk-alpha/src/components/typography/BodyText.tsx b/packages/mobile-sdk-alpha/src/components/typography/BodyText.tsx index 6f8c15dac..8f5860768 100644 --- a/packages/mobile-sdk-alpha/src/components/typography/BodyText.tsx +++ b/packages/mobile-sdk-alpha/src/components/typography/BodyText.tsx @@ -6,8 +6,9 @@ import type React from 'react'; import type { TextProps } from 'react-native'; import { Text } from 'react-native'; +import { slate500 } from '../../constants/colors'; import { dinot } from '../../utils/fonts'; export const BodyText: React.FC = ({ style, ...props }) => ( - + ); diff --git a/packages/mobile-sdk-alpha/src/components/typography/Caption.tsx b/packages/mobile-sdk-alpha/src/components/typography/Caption.tsx index 9d9c545d9..bcabf63ef 100644 --- a/packages/mobile-sdk-alpha/src/components/typography/Caption.tsx +++ b/packages/mobile-sdk-alpha/src/components/typography/Caption.tsx @@ -3,7 +3,8 @@ // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. import type React from 'react'; -import type { TextProps } from 'react-native'; +import { useMemo } from 'react'; +import { StyleSheet, type TextProps } from 'react-native'; import { slate400 } from '../../constants/colors'; import { BodyText } from './BodyText'; @@ -14,6 +15,7 @@ type CaptionProps = TextProps & { export const Caption: React.FC = ({ size, style, ...props }) => { const fontSize = size === 'small' ? 14 : size === 'large' ? 16 : 15; + const flattenedStyle = useMemo(() => StyleSheet.flatten([{ fontSize, color: slate400 }, style]), [fontSize, style]); - return ; + return ; }; diff --git a/packages/mobile-sdk-alpha/src/components/typography/Title.tsx b/packages/mobile-sdk-alpha/src/components/typography/Title.tsx index 938752634..c78eb9b95 100644 --- a/packages/mobile-sdk-alpha/src/components/typography/Title.tsx +++ b/packages/mobile-sdk-alpha/src/components/typography/Title.tsx @@ -3,9 +3,11 @@ // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. import type React from 'react'; +import { useMemo } from 'react'; import type { StyleProp, TextProps, TextStyle } from 'react-native'; -import { Text } from 'react-native'; +import { StyleSheet, Text } from 'react-native'; +import { black } from '../../constants/colors'; import { advercase } from '../../utils/fonts'; type TitleProps = TextProps & { @@ -18,6 +20,7 @@ export const Title: React.FC = ({ size, style, children, ...rest }) fontSize: 28, lineHeight: 35, fontFamily: advercase, + color: black, }; const largeStyle: TextStyle = @@ -28,8 +31,10 @@ export const Title: React.FC = ({ size, style, children, ...rest }) } : {}; + const flattenedStyle = useMemo(() => StyleSheet.flatten([baseStyle, largeStyle, style]), [size, style]); + return ( - + {children} ); From ae4aebb88af9c851e7767835758b7631f875a838 Mon Sep 17 00:00:00 2001 From: Justin Hernandez Date: Sat, 11 Oct 2025 14:56:00 -0700 Subject: [PATCH 6/6] optimize github workflow cache space (#1273) * update workflows with cleaner caching logic * cache fixes --- .github/actions/cache-gradle/action.yml | 4 ++- .../cleanup-gradle-artifacts/action.yml | 23 ++++++++++++++++ .github/actions/free-disk-space/action.yml | 26 +++++++++++++++++++ .github/workflows/mobile-ci.yml | 4 +++ .github/workflows/mobile-deploy.yml | 6 +++++ .github/workflows/mobile-e2e.yml | 7 +++++ .github/workflows/mobile-sdk-demo-e2e.yml | 4 +++ 7 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 .github/actions/cleanup-gradle-artifacts/action.yml create mode 100644 .github/actions/free-disk-space/action.yml diff --git a/.github/actions/cache-gradle/action.yml b/.github/actions/cache-gradle/action.yml index 91c10e920..973fe5b31 100644 --- a/.github/actions/cache-gradle/action.yml +++ b/.github/actions/cache-gradle/action.yml @@ -7,7 +7,9 @@ inputs: description: Paths to cache required: false default: | - ~/.gradle/caches + ~/.gradle/caches/modules-* + ~/.gradle/caches/jars-* + ~/.gradle/caches/build-cache-* ~/.gradle/wrapper cache-version: description: Additional cache version segment diff --git a/.github/actions/cleanup-gradle-artifacts/action.yml b/.github/actions/cleanup-gradle-artifacts/action.yml new file mode 100644 index 000000000..98643bac4 --- /dev/null +++ b/.github/actions/cleanup-gradle-artifacts/action.yml @@ -0,0 +1,23 @@ +name: Clean Up Gradle Artifacts + +description: Clean up unnecessary Gradle build artifacts to save disk space and reduce cache size. + +runs: + using: "composite" + steps: + - name: Clean up Gradle build artifacts + shell: bash + run: | + echo "Cleaning up unnecessary build artifacts to save disk space..." + # Remove build outputs (APK/AAB already tested, no need to cache them) + # Find all Android build directories and clean up intermediates and tmp + find . -type d -path "*/android/app/build/intermediates" -exec rm -rf {} + 2>/dev/null || true + find . -type d -path "*/android/app/build/tmp" -exec rm -rf {} + 2>/dev/null || true + # Clean up Gradle daemon logs and lock files + rm -rf ~/.gradle/daemon + rm -rf ~/.gradle/*.lock + # Remove large cache files that aren't needed for subsequent builds + find ~/.gradle/caches -name "*.lock" -delete 2>/dev/null || true + find ~/.gradle/caches -type f -name "gc.properties" -delete 2>/dev/null || true + echo "Disk usage after cleanup:" + df -h diff --git a/.github/actions/free-disk-space/action.yml b/.github/actions/free-disk-space/action.yml new file mode 100644 index 000000000..c15ab5e98 --- /dev/null +++ b/.github/actions/free-disk-space/action.yml @@ -0,0 +1,26 @@ +name: Free Disk Space + +description: Free up disk space on GitHub Actions runners by removing unnecessary pre-installed tools. + +runs: + using: "composite" + steps: + - name: Free up disk space + shell: bash + run: | + echo "Disk usage before cleanup:" + df -h + # Remove unnecessary pre-installed tools to free up space + # These are commonly available on GitHub runners but not needed for most builds + # DO NOT remove $AGENT_TOOLSDIRECTORY as it contains active tools like Node.js + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf /usr/local/share/boost + # Remove unused languages from hostedtoolcache to save space + # but preserve the directory structure and Node.js + sudo rm -rf /opt/hostedtoolcache/CodeQL 2>/dev/null || true + sudo rm -rf /opt/hostedtoolcache/PyPy 2>/dev/null || true + sudo rm -rf /opt/hostedtoolcache/go 2>/dev/null || true + sudo rm -rf /opt/hostedtoolcache/Ruby 2>/dev/null || true + echo "Disk usage after cleanup:" + df -h diff --git a/.github/workflows/mobile-ci.yml b/.github/workflows/mobile-ci.yml index b7c8f1bfb..745484169 100644 --- a/.github/workflows/mobile-ci.yml +++ b/.github/workflows/mobile-ci.yml @@ -419,6 +419,8 @@ jobs: node_modules app/node_modules cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }} + - name: Free up disk space + uses: ./.github/actions/free-disk-space - name: Cache Gradle uses: ./.github/actions/cache-gradle with: @@ -459,3 +461,5 @@ jobs: - name: Build Android (with AAPT2 symlink fix) run: yarn android:ci working-directory: ./app + - name: Clean up Gradle build artifacts + uses: ./.github/actions/cleanup-gradle-artifacts diff --git a/.github/workflows/mobile-deploy.yml b/.github/workflows/mobile-deploy.yml index 4c360eedf..9df3b2ada 100644 --- a/.github/workflows/mobile-deploy.yml +++ b/.github/workflows/mobile-deploy.yml @@ -836,6 +836,8 @@ jobs: project_id: "plucky-tempo-454713-r0" workload_identity_provider: "projects/852920390127/locations/global/workloadIdentityPools/gh-self/providers/github-by-repos" service_account: "self-xyz@plucky-tempo-454713-r0.iam.gserviceaccount.com" + - name: Free up disk space + uses: ./.github/actions/free-disk-space # Fail fast: set up JDK for keytool and verify Android secrets early - name: Setup Java environment if: inputs.platform != 'ios' @@ -1178,6 +1180,10 @@ jobs: echo "✅ Android build output verification passed" + - name: Clean up Gradle build artifacts + if: inputs.platform != 'ios' + uses: ./.github/actions/cleanup-gradle-artifacts + - name: Upload to Google Play Store using WIF if: inputs.platform != 'ios' && inputs.test_mode != true timeout-minutes: 10 diff --git a/.github/workflows/mobile-e2e.yml b/.github/workflows/mobile-e2e.yml index 2a5545c80..fad79f285 100644 --- a/.github/workflows/mobile-e2e.yml +++ b/.github/workflows/mobile-e2e.yml @@ -92,6 +92,8 @@ jobs: - name: Add Maestro to path if: false # Skip for build-only test - keep logic for future E2E run: echo "$HOME/.maestro/bin" >> "$GITHUB_PATH" + - name: Free up disk space + uses: ./.github/actions/free-disk-space - name: Setup Java environment uses: actions/setup-java@v4 with: @@ -108,6 +110,9 @@ jobs: - name: Build dependencies (outside emulator) run: | echo "Building dependencies..." + # Ensure Yarn 4.6.0 is active + corepack enable + corepack prepare yarn@4.6.0 --activate yarn workspace @selfxyz/mobile-app run build:deps || { echo "❌ Dependency build failed"; exit 1; } echo "✅ Dependencies built successfully" - name: Clone android-passport-nfc-reader @@ -121,6 +126,8 @@ jobs: chmod +x app/android/gradlew (cd app/android && ./gradlew assembleDebug --quiet --parallel --build-cache --no-configuration-cache) || { echo "❌ Android build failed"; exit 1; } echo "✅ Android build succeeded" + - name: Clean up Gradle build artifacts + uses: ./.github/actions/cleanup-gradle-artifacts - name: Verify APK and android-passport-nfc-reader integration run: | echo "🔍 Verifying build artifacts..." diff --git a/.github/workflows/mobile-sdk-demo-e2e.yml b/.github/workflows/mobile-sdk-demo-e2e.yml index 26b6d1441..a410f8dfd 100644 --- a/.github/workflows/mobile-sdk-demo-e2e.yml +++ b/.github/workflows/mobile-sdk-demo-e2e.yml @@ -92,6 +92,8 @@ jobs: - name: Add Maestro to path if: false # Skip for build-only test - keep logic for future E2E run: echo "$HOME/.maestro/bin" >> "$GITHUB_PATH" + - name: Free up disk space + uses: ./.github/actions/free-disk-space - name: Setup Java environment uses: actions/setup-java@v4 with: @@ -124,6 +126,8 @@ jobs: chmod +x packages/mobile-sdk-demo/android/gradlew (cd packages/mobile-sdk-demo/android && ./gradlew assembleDebug --quiet --parallel --build-cache --no-configuration-cache) || { echo "❌ Android build failed"; exit 1; } echo "✅ Android build succeeded" + - name: Clean up Gradle build artifacts + uses: ./.github/actions/cleanup-gradle-artifacts - name: Verify APK build run: | echo "🔍 Verifying build artifacts..."