diff --git a/README.md b/README.md index 862e1d8b..10b9d755 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ :fireworks: Clean and minimalist React Native template for a quick start with TypeScript and so much more components. -## Current version: 0.74.5 +## Current version: 0.75.4 ## :star: Features diff --git a/package.json b/package.json index 7310c138..94589c07 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "rn-boiler-template", "private": false, - "version": "1.74.9", + "version": "1.75.1", "description": "Clean and minimalist React Native template for a quick start with TypeScript and components", "scripts": { "test": "exit 0" diff --git a/template/Gemfile b/template/Gemfile index 35f8ba5c..3b17116b 100644 --- a/template/Gemfile +++ b/template/Gemfile @@ -3,10 +3,9 @@ source 'https://rubygems.org' # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version ruby ">= 2.6.10" -# Cocoapods 1.15 introduced a bug which break the build. We will remove the upper -# bound in the template on Cocoapods with next React Native release. -gem 'cocoapods', '>= 1.13', '< 1.15' -gem 'activesupport', '>= 6.1.7.5', '< 7.1.0' +# Exclude problematic versions of cocoapods and activesupport that causes build failures. +gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' +gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' gem "fastlane" plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') eval(File.read(plugins_path), binding) if File.exist?(plugins_path) \ No newline at end of file diff --git a/template/android/app/build.gradle b/template/android/app/build.gradle index d501661d..83fbeae0 100644 --- a/template/android/app/build.gradle +++ b/template/android/app/build.gradle @@ -16,14 +16,14 @@ apply from: project(':react-native-keys').projectDir.getPath() + "/RNKeys.gradle */ react { /* Folders */ - // The root of your project, i.e. where "package.json" lives. Default is '..' - // root = file("../") - // The folder where the react-native NPM package is. Default is ../node_modules/react-native - // reactNativeDir = file("../node_modules/react-native") - // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen - // codegenDir = file("../node_modules/@react-native/codegen") - // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js - // cliFile = file("../node_modules/react-native/cli.js") + // The root of your project, i.e. where "package.json" lives. Default is '../..' + // root = file("../../") + // The folder where the react-native NPM package is. Default is ../../node_modules/react-native + // reactNativeDir = file("../../node_modules/react-native") + // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen + // codegenDir = file("../../node_modules/@react-native/codegen") + // The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js + // cliFile = file("../../node_modules/react-native/cli.js") /* Variants */ // The list of variants to that are debuggable. For those we're going to @@ -58,6 +58,10 @@ react { // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" // hermesFlags = ["-O", "-output-source-map"] // + + /* Autolinking */ + autolinkLibrariesWithApp() + // Added by install-expo-modules entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", rootDir.getAbsoluteFile().getParentFile().getAbsolutePath(), "android", "absolute"].execute(null, rootDir).text.trim()) cliFile = new File(["node", "--print", "require.resolve('@expo/cli')"].execute(null, rootDir).text.trim()) @@ -134,7 +138,6 @@ dependencies { } } -apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) task prepare() { def flavor = project.keys.get("FLAVOR") def envPath = project.ext.keyFiles.find { it.key == flavor }?.value diff --git a/template/android/app/src/main/AndroidManifest.xml b/template/android/app/src/main/AndroidManifest.xml index b02c3d4a..5ff6b06d 100644 --- a/template/android/app/src/main/AndroidManifest.xml +++ b/template/android/app/src/main/AndroidManifest.xml @@ -9,7 +9,8 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" - android:theme="@style/AppTheme"> + android:theme="@style/AppTheme" + android:supportsRtl="false"> ex.autolinkLibrariesFromCommand() } rootProject.name = 'HelloWorld' -apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) include ':app' includeBuild('../node_modules/@react-native/gradle-plugin') diff --git a/template/env-config.ts b/template/env-config.ts index c82668a9..3cae26ab 100644 --- a/template/env-config.ts +++ b/template/env-config.ts @@ -4,7 +4,7 @@ import Keys from 'react-native-keys'; -export const APP_BUILD_VERSION = '1.0.0.2024.07.16.20.16'; +export const APP_BUILD_VERSION = '1.0.0.2024.11.24.13.12'; export const { DEFAULT_FALLBACK_LNG_I18n } = Keys; diff --git a/template/eslint.config.mjs b/template/eslint.config.mjs index 97efd5ef..ab41b3cf 100644 --- a/template/eslint.config.mjs +++ b/template/eslint.config.mjs @@ -183,12 +183,6 @@ export default [ }, ], 'prefer-destructuring': 2, - 'prettier/prettier': [ - 'error', - { - endOfLine: 'auto', - }, - ], quotes: [ 'error', 'single', diff --git a/template/ios/HelloWorld.xcodeproj/project.pbxproj b/template/ios/HelloWorld.xcodeproj/project.pbxproj index 6a49d9dc..1d178f7e 100644 --- a/template/ios/HelloWorld.xcodeproj/project.pbxproj +++ b/template/ios/HelloWorld.xcodeproj/project.pbxproj @@ -349,8 +349,8 @@ baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-HelloWorld.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "$(ASSETCATALOG_COMPILER_APPICON_NAME)"; - CODE_SIGN_STYLE = Manual; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = "$(APPLE_DEVELOPMENT_TEAM)"; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = "$(APPLE_DEVELOPMENT_TEAM)"; @@ -387,8 +387,8 @@ baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-HelloWorld.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "$(ASSETCATALOG_COMPILER_APPICON_NAME)"; - CODE_SIGN_STYLE = Manual; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = "$(APPLE_DEVELOPMENT_TEAM)"; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = "$(APPLE_DEVELOPMENT_TEAM)"; @@ -498,6 +498,7 @@ ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; USE_HERMES = true; }; name = Debug; diff --git a/template/package.json b/template/package.json index 6762105d..22d29623 100644 --- a/template/package.json +++ b/template/package.json @@ -21,43 +21,42 @@ }, "dependencies": { "@gorhom/portal": "^1.0.14", - "@hookform/resolvers": "^3.7.0", + "@hookform/resolvers": "^3.9.1", "@react-native-community/netinfo": "^11.4.1", - "@react-native-masked-view/masked-view": "^0.3.1", - "@react-navigation/native": "^6.1.18", - "@react-navigation/native-stack": "^6.11.0", + "@react-native-masked-view/masked-view": "^0.3.2", + "@react-navigation/native": "^7.0.4", + "@react-navigation/native-stack": "^7.1.1", "@reduxjs/toolkit": "^2.2.6", - "@shopify/flash-list": "^1.6.4", - "@shopify/react-native-skia": "^1.3.7", - "axios": "^1.7.2", - "expo": "^51.0.0", - "expo-dev-client": "~4.0.19", - "expo-font": "^12.0.7", - "expo-image": "^1.12.12", + "@shopify/flash-list": "^1.7.2", + "@shopify/react-native-skia": "^1.5.8", + "axios": "^1.7.7", + "expo": "^51.0.39", + "expo-dev-client": "~4.0.29", + "expo-font": "^12.0.10", + "expo-image": "^1.13.0", "expo-status-bar": "^1.12.1", "i18next": "^23.15.1", "moment": "^2.29.4", - "react": "18.2.0", + "react": "18.3.1", "react-fast-compare": "^3.2.2", "react-freeze": "^1.0.4", - "react-hook-form": "^7.53.0", + "react-hook-form": "^7.53.2", "react-i18next": "^15.0.2", - "react-native": "0.74.5", - "react-native-animateable-text": "^0.12.1", - "react-native-bootsplash": "^5.5.3", - "react-native-gesture-handler": "^2.20.0", - "react-native-keyboard-controller": "^1.14.0", + "react-native": "0.75.4", + "react-native-animateable-text": "^0.13.0", + "react-native-bootsplash": "^6.3.1", + "react-native-gesture-handler": "^2.21.2", + "react-native-keyboard-controller": "^1.14.5", "react-native-keys": "^0.7.10", "react-native-linear-gradient": "^2.8.3", "react-native-mmkv": "^2.12.2", - "react-native-reanimated": "^3.15.4", - "react-native-safe-area-context": "^4.11.0", - "react-native-screens": "^3.34.0", - "react-native-svg": "^15.7.1", - "react-native-unistyles": "^2.9.2", + "react-native-reanimated": "^3.16.2", + "react-native-safe-area-context": "^4.14.0", + "react-native-screens": "^4.3.0", + "react-native-unistyles": "^2.20.0", "react-native-vector-icons": "^10.2.0", "react-redux": "^9.1.2", - "zod": "^3.22.4" + "zod": "^3.23.8" }, "devDependencies": { "@babel/core": "^7.20.0", @@ -66,10 +65,10 @@ "@eslint/compat": "^1.1.1", "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.7.0", - "@react-native/babel-preset": "0.74.87", - "@react-native/eslint-config": "0.74.87", - "@react-native/metro-config": "0.74.87", - "@react-native/typescript-config": "0.74.87", + "@react-native/babel-preset": "0.75.4", + "@react-native/eslint-config": "0.75.4", + "@react-native/metro-config": "0.75.4", + "@react-native/typescript-config": "0.75.4", "@types/jest": "^29.5.11", "@types/node": "^18.14.6", "@types/react": "^18.2.6", @@ -87,7 +86,7 @@ "lefthook": "1.5.2", "patch-package": "^8.0.0", "prettier": "2.8.8", - "react-test-renderer": "18.2.0", + "react-test-renderer": "18.3.1", "typescript": "5.0.4", "typescript-eslint": "^7.16.1" }, diff --git a/template/patches/@expo+cli+0.18.30.patch b/template/patches/@expo+cli+0.18.31.patch similarity index 100% rename from template/patches/@expo+cli+0.18.30.patch rename to template/patches/@expo+cli+0.18.31.patch diff --git a/template/patches/@expo+config-plugins+8.0.8.patch b/template/patches/@expo+config-plugins+8.0.11.patch similarity index 100% rename from template/patches/@expo+config-plugins+8.0.8.patch rename to template/patches/@expo+config-plugins+8.0.11.patch diff --git a/template/src/app.tsx b/template/src/app.tsx index d51f1e74..47165122 100644 --- a/template/src/app.tsx +++ b/template/src/app.tsx @@ -5,11 +5,12 @@ import { I18nextProvider } from 'react-i18next'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { KeyboardProvider as RNKeyboardProvider } from 'react-native-keyboard-controller'; import { SafeAreaProvider } from 'react-native-safe-area-context'; +import { UnistylesProvider } from 'react-native-unistyles'; import { Provider } from 'react-redux'; import { PortalProvider } from '@gorhom/portal'; import { useDidMount } from '@hooks'; -import { AppContainer } from '@navigation/app-navigation'; +import { AppContainer } from '@navigation/app-container'; import { store } from '@store/store'; import { useLoadFont } from '@theme/typography'; import I18n from '@utils/i18n/i18n'; @@ -70,20 +71,22 @@ export const MyApp = () => { // render return ( - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + ); }; diff --git a/template/src/app/navigation/app-container.tsx b/template/src/app/navigation/app-container.tsx new file mode 100644 index 00000000..0578b65f --- /dev/null +++ b/template/src/app/navigation/app-container.tsx @@ -0,0 +1,36 @@ +import React, { useEffect } from 'react'; + +import { useSelector } from 'react-redux'; + +import { dispatch, RXStore } from '@common/redux'; +import { SnackBar } from '@components/snack-bar'; +import { PortalHost } from '@gorhom/portal'; +import { RootNavigation } from '@navigation/root-navigator'; +import { selectAppConfig } from '@redux-selector/app'; +import { appActions } from '@redux-slice/app'; +import { StatusBar } from 'expo-status-bar'; + +export const AppContainer = () => { + // state + const { loadingApp } = useSelector(selectAppConfig); + + // effect + useEffect(() => { + dispatch(appActions.startLoadApp()); + }, []); + + // render + return ( + <> + + {!loadingApp && ( + <> + + + + + )} + + + ); +}; diff --git a/template/src/app/navigation/app-navigation.tsx b/template/src/app/navigation/app-navigation.tsx deleted file mode 100644 index 8c4b931c..00000000 --- a/template/src/app/navigation/app-navigation.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { useEffect } from 'react'; - -import { useStyles } from 'react-native-unistyles'; -import { useSelector } from 'react-redux'; - -import { dispatch, RXStore } from '@common/redux'; -import { SnackBar } from '@components/snack-bar'; -import { PortalHost } from '@gorhom/portal'; -import { RootNavigation } from '@navigation/root-navigator'; -import { - DefaultTheme, - NavigationContainer, - useNavigationContainerRef, -} from '@react-navigation/native'; -import { selectAppConfig } from '@redux-selector/app'; -import { appActions } from '@redux-slice/app'; -import { StatusBar } from 'expo-status-bar'; - -import { NavigationService } from './navigation-service'; - -export const AppContainer = () => { - // state - const navigationRef = useNavigationContainerRef(); - - const { theme } = useStyles(); - - const { loadingApp } = useSelector(selectAppConfig); - - // effect - useEffect(() => { - dispatch(appActions.startLoadApp()); - }, []); - - // render - return ( - - <> - - {!loadingApp && ( - <> - - - - - )} - - - - - ); -}; diff --git a/template/src/app/navigation/navigation-service.tsx b/template/src/app/navigation/navigation-service.tsx index d3b7bdf8..6f62a893 100644 --- a/template/src/app/navigation/navigation-service.tsx +++ b/template/src/app/navigation/navigation-service.tsx @@ -1,47 +1,12 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import React, { createRef, forwardRef, useImperativeHandle } from 'react'; - import { CommonActions, + createNavigationContainerRef, StackActions, - useNavigation, } from '@react-navigation/native'; import { RootStackParamList } from './screen-types'; -const NavigationComponent = forwardRef((_, ref) => { - // state - const navigation = useNavigation(); - - // effect - useImperativeHandle( - ref, - () => ({ - dispatch: (args: any) => { - navigation.dispatch(args); - }, - navigate: (...args: any[]) => { - // @ts-ignore - navigation.navigate(...(args as never)); - }, - }), - [navigation], - ); - - return null; -}); - -type NavigationRef = { - navigate: (...args: any[]) => void; - dispatch: (...args: any[]) => void; -}; - -const navigationRef = createRef(); - -export const NavigationService = () => ( - -); +export const navigationRef = createNavigationContainerRef(); export function navigateScreen( ...arg: undefined extends RootStackParamList[RouteName] @@ -50,10 +15,7 @@ export function navigateScreen( | [screen: RouteName, params?: RootStackParamList[RouteName]] : [screen: RouteName, params: RootStackParamList[RouteName]] ) { - navigationRef.current?.navigate( - arg[0] as any, - arg.length > 1 ? arg[1] : undefined, - ); + navigationRef.navigate(arg[0], arg.length > 1 ? arg[1] : undefined); } export function pushScreen( @@ -63,8 +25,8 @@ export function pushScreen( | [screen: RouteName, params?: RootStackParamList[RouteName]] : [screen: RouteName, params: RootStackParamList[RouteName]] ) { - navigationRef.current?.dispatch( - StackActions.push(arg[0] as any, arg.length > 1 ? arg[1] : undefined), + navigationRef.dispatch( + StackActions.push(arg[0], arg.length > 1 ? arg[1] : undefined), ); } @@ -75,11 +37,11 @@ export function replaceScreen( | [screen: RouteName, params?: RootStackParamList[RouteName]] : [screen: RouteName, params: RootStackParamList[RouteName]] ) { - navigationRef.current?.dispatch( - StackActions.replace(arg[0] as any, arg.length > 1 ? arg[1] : undefined), + navigationRef.dispatch( + StackActions.replace(arg[0], arg.length > 1 ? arg[1] : undefined), ); } export function goBack() { - navigationRef.current?.dispatch(CommonActions.goBack); + navigationRef.dispatch(CommonActions.goBack); } diff --git a/template/src/app/navigation/root-navigator.tsx b/template/src/app/navigation/root-navigator.tsx index bc6e67f5..faa66f43 100644 --- a/template/src/app/navigation/root-navigator.tsx +++ b/template/src/app/navigation/root-navigator.tsx @@ -1,19 +1,59 @@ import React, { useEffect } from 'react'; import BootSplash from 'react-native-bootsplash'; +import { useStyles } from 'react-native-unistyles'; import { useSelector } from 'react-redux'; -import { APP_SCREEN, RootStackParamList } from '@navigation/screen-types'; +import { APP_SCREEN } from '@navigation/screen-types'; +import { createStaticNavigation, DefaultTheme } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { selectAppToken } from '@redux-selector/app'; import { Home } from '@screens/authentication/home'; import { Login } from '@screens/un-authentication/login'; -const RootStack = createNativeStackNavigator(); +import { navigationRef } from './navigation-service'; + +const useAppLoggedIn = () => { + return useSelector(selectAppToken) !== undefined; +}; + +const useAppLoggedOut = () => { + return !useAppLoggedIn(); +}; + +const RootStack = createNativeStackNavigator({ + groups: { + [APP_SCREEN.UN_AUTHORIZE]: { + if: useAppLoggedIn, + screens: { + [APP_SCREEN.HOME]: Home, + }, + }, + [APP_SCREEN.AUTHORIZE]: { + if: useAppLoggedOut, + screenOptions: { + headerShown: false, + }, + screens: { + [APP_SCREEN.LOGIN]: { + screen: Login, + }, + }, + }, + }, + screenOptions: { + freezeOnBlur: true, + navigationBarColor: '#ffffff', + statusBarTranslucent: true, + }, +}); + +const Navigation = createStaticNavigation(RootStack); export const RootNavigation = () => { // state - const token = useSelector(selectAppToken); + + const { theme } = useStyles(); // effect useEffect(() => { @@ -26,24 +66,15 @@ export const RootNavigation = () => { // render return ( - - {token === undefined ? ( - - - - ) : ( - - - - )} - + ); }; diff --git a/template/src/app/screens/authentication/home/index.tsx b/template/src/app/screens/authentication/home/index.tsx index 6998b422..4502c824 100644 --- a/template/src/app/screens/authentication/home/index.tsx +++ b/template/src/app/screens/authentication/home/index.tsx @@ -1,10 +1,8 @@ -import React, { memo } from 'react'; - -import isEqual from 'react-fast-compare'; +import React from 'react'; import { Text, View } from '@rn-core'; -const HomeComponent = () => { +export const Home = () => { // render return ( @@ -12,5 +10,3 @@ const HomeComponent = () => { ); }; - -export const Home = memo(HomeComponent, isEqual); diff --git a/template/src/app/screens/un-authentication/login/index.tsx b/template/src/app/screens/un-authentication/login/index.tsx index 28164954..d8ce5711 100644 --- a/template/src/app/screens/un-authentication/login/index.tsx +++ b/template/src/app/screens/un-authentication/login/index.tsx @@ -66,6 +66,8 @@ export const Login = () => { }; const handleChangeTheme = async () => { + await wait(300); + opacity.value = 1; const overlay1 = await makeImageFromView(rootRef);