This commit is contained in:
Abin Toppo
2025-12-10 10:15:38 +05:30
parent 3468de396c
commit bd7dbd864c
51 changed files with 1168 additions and 1132 deletions
+25 -18
View File
@@ -1,36 +1,39 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { StyleSheet, useColorScheme } from 'react-native'; import { StyleSheet, useColorScheme } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context'; import { SafeAreaProvider } from 'react-native-safe-area-context';
import { PaperProvider } from 'react-native-paper'; import { PaperProvider } from 'react-native-paper';
import AppNavigator from './src/navigation/AppNavigator'; import AppNavigator from './src/navigation/AppNavigator';
import notifee from '@notifee/react-native'; import notifee from '@notifee/react-native';
import { backgroundHandler, foregroundListener, requestPermission, setupFirebaseHandlers, setupNotificationPressHandlers, tokenRefreshHandler } from './src/utils/firebase/firebaseServices';
import {
backgroundHandler,
foregroundListener,
requestPermission,
setupFirebaseHandlers,
setupNotificationPressHandlers,
tokenRefreshHandler,
} from './src/utils/firebase/firebaseServices';
import { dark, light } from './src/theme/theme';
import { ThemeProvider} from './src/theme/ThemeProvider';
function App() { function App() {
const isDarkMode = useColorScheme() === 'dark';
useEffect(() => { useEffect(() => {
// 🔹 Ask for permission
requestPermission(); requestPermission();
// 🔹 Handle background + quit state
backgroundHandler(); backgroundHandler();
setupFirebaseHandlers() setupFirebaseHandlers();
setupNotificationPressHandlers() setupNotificationPressHandlers();
// 🔹 Listen for foreground messages
const unsubscribe = foregroundListener(); const unsubscribe = foregroundListener();
// 🔹 Listen for token refresh
const unsubscribeRefresh = tokenRefreshHandler(); const unsubscribeRefresh = tokenRefreshHandler();
// 🔹 Create default notification channel (Android)
notifee.createChannel({ notifee.createChannel({
id: 'default', id: 'default',
name: 'Default Channel', name: 'Default Channel',
importance: 4, // HIGH importance: 4,
}); });
return () => { return () => {
@@ -39,19 +42,23 @@ function App() {
}; };
}, []); }, []);
return ( return (
<SafeAreaProvider> <SafeAreaProvider>
<PaperProvider> <ThemeProvider>
<AppNavigator /> <PaperProvider >
</PaperProvider> <AppNavigator />
</PaperProvider>
</ThemeProvider>
</SafeAreaProvider> </SafeAreaProvider>
); );
} }
export default App;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
}, },
}); });
export default App;
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+69
View File
@@ -0,0 +1,69 @@
{
"migIndex": 1,
"data": [
{
"path": "src/assets/fonts/Nunito-Black.ttf",
"sha1": "bede9c89e905ae0b91404768e1167e3e5d3c9c8e"
},
{
"path": "src/assets/fonts/Nunito-BlackItalic.ttf",
"sha1": "5b27538c43c1afcd5b4a8547854522a805f4c302"
},
{
"path": "src/assets/fonts/Nunito-Bold.ttf",
"sha1": "e50cf9134308e37008cbd39da1dc637aef022641"
},
{
"path": "src/assets/fonts/Nunito-BoldItalic.ttf",
"sha1": "5ab6843366253b2e8009cf3384e6d275004ad5f3"
},
{
"path": "src/assets/fonts/Nunito-ExtraBold.ttf",
"sha1": "37ba2b2e9e7dd74a67b8b994a1aebe333af7fc39"
},
{
"path": "src/assets/fonts/Nunito-ExtraBoldItalic.ttf",
"sha1": "fab683da6e0b9ff687a6ca683f96127e84919b24"
},
{
"path": "src/assets/fonts/Nunito-ExtraLight.ttf",
"sha1": "5473261207bdc8a16cf78d26e598becddeb8155e"
},
{
"path": "src/assets/fonts/Nunito-ExtraLightItalic.ttf",
"sha1": "fd4b64a944d4353f216df49c028cde717161dd24"
},
{
"path": "src/assets/fonts/Nunito-Italic.ttf",
"sha1": "0c994f04c2bb328d87069d32e7422b559c05d5a7"
},
{
"path": "src/assets/fonts/Nunito-Light.ttf",
"sha1": "e69e3dfdaf5baaa6fc6db1dd031ed18e1dca38f3"
},
{
"path": "src/assets/fonts/Nunito-LightItalic.ttf",
"sha1": "6855c33fb876932b819dc445eeb4ae3267c4ef7d"
},
{
"path": "src/assets/fonts/Nunito-Medium.ttf",
"sha1": "75324fedc01df018cdf436bb5beba504bd82af0d"
},
{
"path": "src/assets/fonts/Nunito-MediumItalic.ttf",
"sha1": "e582f36e0b5d13c9e3ac827bd879593dec1a06d0"
},
{
"path": "src/assets/fonts/Nunito-Regular.ttf",
"sha1": "929ddc840610489715952aaa10af50e9c1a6a4a6"
},
{
"path": "src/assets/fonts/Nunito-SemiBold.ttf",
"sha1": "c4a5281a3e088e1d2604635d4d09a1583d036db6"
},
{
"path": "src/assets/fonts/Nunito-SemiBoldItalic.ttf",
"sha1": "96d4f551a905d9dd509fec0ae085f35666d8f797"
}
]
}
@@ -11,6 +11,22 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
77E955B03ACA476189F41279 /* Nunito-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CD6281C4A8224BDC9C99061C /* Nunito-Black.ttf */; };
69272C3CFEEF44F4BB88C9BB /* Nunito-BlackItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 8B63FD1EEF264E8E9FF335EC /* Nunito-BlackItalic.ttf */; };
84A9D5DED8094CB4A153268F /* Nunito-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AEAB2A7CD23248A9A542D0CA /* Nunito-Bold.ttf */; };
D67F05517BE4422F808A5EB3 /* Nunito-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4C5A03C0427D4F17A5988F0F /* Nunito-BoldItalic.ttf */; };
E007418A1C2740F79785A324 /* Nunito-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 60736DFE1D9549AEB020EE49 /* Nunito-ExtraBold.ttf */; };
CA97D53F7B8349D19F07E434 /* Nunito-ExtraBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D41A02AA33B74B74A5EA8D28 /* Nunito-ExtraBoldItalic.ttf */; };
FC3942C6BF94431DAF7D34CA /* Nunito-ExtraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C2154C491E214E5B87CE0D6E /* Nunito-ExtraLight.ttf */; };
BF72BEDD2BEF41D5A740F6DB /* Nunito-ExtraLightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1BB241E150DC4271B6DB8035 /* Nunito-ExtraLightItalic.ttf */; };
C755B258B1CE4C7D9F669459 /* Nunito-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5E80FD17FB314B54B4070AE7 /* Nunito-Italic.ttf */; };
4432DD78D2D8420098273374 /* Nunito-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 8654D08FDB97420F9054EC82 /* Nunito-Light.ttf */; };
7E3B3883C3FA414C9770A859 /* Nunito-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 50F514E360C2402797F5FB3B /* Nunito-LightItalic.ttf */; };
4B27568F87CA45C18D781B4E /* Nunito-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 13508BA55A1D4761A085FB3C /* Nunito-Medium.ttf */; };
0E89A7DC000347CCB92D18FA /* Nunito-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5744AD8D40A949D6B7F3C816 /* Nunito-MediumItalic.ttf */; };
609A14F1490B46E9966AD947 /* Nunito-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FAD14762400E476AA7B1075D /* Nunito-Regular.ttf */; };
430150A883E743E9A59596D5 /* Nunito-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 499363DC0EDB4DA4A3B87FA4 /* Nunito-SemiBold.ttf */; };
E3AC45B155004DE6BD7233A4 /* Nunito-SemiBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 62F0D71A41214365A475156B /* Nunito-SemiBoldItalic.ttf */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
@@ -24,6 +40,22 @@
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = CircleCollect/AppDelegate.swift; sourceTree = "<group>"; }; 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = CircleCollect/AppDelegate.swift; sourceTree = "<group>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = CircleCollect/LaunchScreen.storyboard; sourceTree = "<group>"; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = CircleCollect/LaunchScreen.storyboard; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
CD6281C4A8224BDC9C99061C /* Nunito-Black.ttf */ = {isa = PBXFileReference; name = "Nunito-Black.ttf"; path = "../src/assets/fonts/Nunito-Black.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
8B63FD1EEF264E8E9FF335EC /* Nunito-BlackItalic.ttf */ = {isa = PBXFileReference; name = "Nunito-BlackItalic.ttf"; path = "../src/assets/fonts/Nunito-BlackItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
AEAB2A7CD23248A9A542D0CA /* Nunito-Bold.ttf */ = {isa = PBXFileReference; name = "Nunito-Bold.ttf"; path = "../src/assets/fonts/Nunito-Bold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
4C5A03C0427D4F17A5988F0F /* Nunito-BoldItalic.ttf */ = {isa = PBXFileReference; name = "Nunito-BoldItalic.ttf"; path = "../src/assets/fonts/Nunito-BoldItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
60736DFE1D9549AEB020EE49 /* Nunito-ExtraBold.ttf */ = {isa = PBXFileReference; name = "Nunito-ExtraBold.ttf"; path = "../src/assets/fonts/Nunito-ExtraBold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
D41A02AA33B74B74A5EA8D28 /* Nunito-ExtraBoldItalic.ttf */ = {isa = PBXFileReference; name = "Nunito-ExtraBoldItalic.ttf"; path = "../src/assets/fonts/Nunito-ExtraBoldItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
C2154C491E214E5B87CE0D6E /* Nunito-ExtraLight.ttf */ = {isa = PBXFileReference; name = "Nunito-ExtraLight.ttf"; path = "../src/assets/fonts/Nunito-ExtraLight.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
1BB241E150DC4271B6DB8035 /* Nunito-ExtraLightItalic.ttf */ = {isa = PBXFileReference; name = "Nunito-ExtraLightItalic.ttf"; path = "../src/assets/fonts/Nunito-ExtraLightItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
5E80FD17FB314B54B4070AE7 /* Nunito-Italic.ttf */ = {isa = PBXFileReference; name = "Nunito-Italic.ttf"; path = "../src/assets/fonts/Nunito-Italic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
8654D08FDB97420F9054EC82 /* Nunito-Light.ttf */ = {isa = PBXFileReference; name = "Nunito-Light.ttf"; path = "../src/assets/fonts/Nunito-Light.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
50F514E360C2402797F5FB3B /* Nunito-LightItalic.ttf */ = {isa = PBXFileReference; name = "Nunito-LightItalic.ttf"; path = "../src/assets/fonts/Nunito-LightItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
13508BA55A1D4761A085FB3C /* Nunito-Medium.ttf */ = {isa = PBXFileReference; name = "Nunito-Medium.ttf"; path = "../src/assets/fonts/Nunito-Medium.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
5744AD8D40A949D6B7F3C816 /* Nunito-MediumItalic.ttf */ = {isa = PBXFileReference; name = "Nunito-MediumItalic.ttf"; path = "../src/assets/fonts/Nunito-MediumItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
FAD14762400E476AA7B1075D /* Nunito-Regular.ttf */ = {isa = PBXFileReference; name = "Nunito-Regular.ttf"; path = "../src/assets/fonts/Nunito-Regular.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
499363DC0EDB4DA4A3B87FA4 /* Nunito-SemiBold.ttf */ = {isa = PBXFileReference; name = "Nunito-SemiBold.ttf"; path = "../src/assets/fonts/Nunito-SemiBold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
62F0D71A41214365A475156B /* Nunito-SemiBoldItalic.ttf */ = {isa = PBXFileReference; name = "Nunito-SemiBoldItalic.ttf"; path = "../src/assets/fonts/Nunito-SemiBoldItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -74,6 +106,7 @@
83CBBA001A601CBA00E9B192 /* Products */, 83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */, 2D16E6871FA4F8E400B85C8A /* Frameworks */,
BBD78D7AC51CEA395F1C20DB /* Pods */, BBD78D7AC51CEA395F1C20DB /* Pods */,
20F28563BF834255BD7383AF /* Resources */,
); );
indentWidth = 2; indentWidth = 2;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -97,6 +130,30 @@
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
20F28563BF834255BD7383AF /* Resources */ = {
isa = "PBXGroup";
children = (
CD6281C4A8224BDC9C99061C /* Nunito-Black.ttf */,
8B63FD1EEF264E8E9FF335EC /* Nunito-BlackItalic.ttf */,
AEAB2A7CD23248A9A542D0CA /* Nunito-Bold.ttf */,
4C5A03C0427D4F17A5988F0F /* Nunito-BoldItalic.ttf */,
60736DFE1D9549AEB020EE49 /* Nunito-ExtraBold.ttf */,
D41A02AA33B74B74A5EA8D28 /* Nunito-ExtraBoldItalic.ttf */,
C2154C491E214E5B87CE0D6E /* Nunito-ExtraLight.ttf */,
1BB241E150DC4271B6DB8035 /* Nunito-ExtraLightItalic.ttf */,
5E80FD17FB314B54B4070AE7 /* Nunito-Italic.ttf */,
8654D08FDB97420F9054EC82 /* Nunito-Light.ttf */,
50F514E360C2402797F5FB3B /* Nunito-LightItalic.ttf */,
13508BA55A1D4761A085FB3C /* Nunito-Medium.ttf */,
5744AD8D40A949D6B7F3C816 /* Nunito-MediumItalic.ttf */,
FAD14762400E476AA7B1075D /* Nunito-Regular.ttf */,
499363DC0EDB4DA4A3B87FA4 /* Nunito-SemiBold.ttf */,
62F0D71A41214365A475156B /* Nunito-SemiBoldItalic.ttf */,
);
name = Resources;
sourceTree = "<group>";
path = "";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@@ -159,6 +216,22 @@
files = ( files = (
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
77E955B03ACA476189F41279 /* Nunito-Black.ttf in Resources */,
69272C3CFEEF44F4BB88C9BB /* Nunito-BlackItalic.ttf in Resources */,
84A9D5DED8094CB4A153268F /* Nunito-Bold.ttf in Resources */,
D67F05517BE4422F808A5EB3 /* Nunito-BoldItalic.ttf in Resources */,
E007418A1C2740F79785A324 /* Nunito-ExtraBold.ttf in Resources */,
CA97D53F7B8349D19F07E434 /* Nunito-ExtraBoldItalic.ttf in Resources */,
FC3942C6BF94431DAF7D34CA /* Nunito-ExtraLight.ttf in Resources */,
BF72BEDD2BEF41D5A740F6DB /* Nunito-ExtraLightItalic.ttf in Resources */,
C755B258B1CE4C7D9F669459 /* Nunito-Italic.ttf in Resources */,
4432DD78D2D8420098273374 /* Nunito-Light.ttf in Resources */,
7E3B3883C3FA414C9770A859 /* Nunito-LightItalic.ttf in Resources */,
4B27568F87CA45C18D781B4E /* Nunito-Medium.ttf in Resources */,
0E89A7DC000347CCB92D18FA /* Nunito-MediumItalic.ttf in Resources */,
609A14F1490B46E9966AD947 /* Nunito-Regular.ttf in Resources */,
430150A883E743E9A59596D5 /* Nunito-SemiBold.ttf in Resources */,
E3AC45B155004DE6BD7233A4 /* Nunito-SemiBoldItalic.ttf in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
+20 -2
View File
@@ -26,14 +26,13 @@
<true/> <true/>
<key>NSAppTransportSecurity</key> <key>NSAppTransportSecurity</key>
<dict> <dict>
<!-- Do not change NSAllowsArbitraryLoads to true, or you will risk app rejection! -->
<key>NSAllowsArbitraryLoads</key> <key>NSAllowsArbitraryLoads</key>
<false/> <false/>
<key>NSAllowsLocalNetworking</key> <key>NSAllowsLocalNetworking</key>
<true/> <true/>
</dict> </dict>
<key>NSLocationWhenInUseUsageDescription</key> <key>NSLocationWhenInUseUsageDescription</key>
<string></string> <string/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key> <key>UIRequiredDeviceCapabilities</key>
@@ -48,5 +47,24 @@
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
<key>UIAppFonts</key>
<array>
<string>Nunito-Black.ttf</string>
<string>Nunito-BlackItalic.ttf</string>
<string>Nunito-Bold.ttf</string>
<string>Nunito-BoldItalic.ttf</string>
<string>Nunito-ExtraBold.ttf</string>
<string>Nunito-ExtraBoldItalic.ttf</string>
<string>Nunito-ExtraLight.ttf</string>
<string>Nunito-ExtraLightItalic.ttf</string>
<string>Nunito-Italic.ttf</string>
<string>Nunito-Light.ttf</string>
<string>Nunito-LightItalic.ttf</string>
<string>Nunito-Medium.ttf</string>
<string>Nunito-MediumItalic.ttf</string>
<string>Nunito-Regular.ttf</string>
<string>Nunito-SemiBold.ttf</string>
<string>Nunito-SemiBoldItalic.ttf</string>
</array>
</dict> </dict>
</plist> </plist>
+69
View File
@@ -0,0 +1,69 @@
{
"migIndex": 1,
"data": [
{
"path": "src/assets/fonts/Nunito-Black.ttf",
"sha1": "bede9c89e905ae0b91404768e1167e3e5d3c9c8e"
},
{
"path": "src/assets/fonts/Nunito-BlackItalic.ttf",
"sha1": "5b27538c43c1afcd5b4a8547854522a805f4c302"
},
{
"path": "src/assets/fonts/Nunito-Bold.ttf",
"sha1": "e50cf9134308e37008cbd39da1dc637aef022641"
},
{
"path": "src/assets/fonts/Nunito-BoldItalic.ttf",
"sha1": "5ab6843366253b2e8009cf3384e6d275004ad5f3"
},
{
"path": "src/assets/fonts/Nunito-ExtraBold.ttf",
"sha1": "37ba2b2e9e7dd74a67b8b994a1aebe333af7fc39"
},
{
"path": "src/assets/fonts/Nunito-ExtraBoldItalic.ttf",
"sha1": "fab683da6e0b9ff687a6ca683f96127e84919b24"
},
{
"path": "src/assets/fonts/Nunito-ExtraLight.ttf",
"sha1": "5473261207bdc8a16cf78d26e598becddeb8155e"
},
{
"path": "src/assets/fonts/Nunito-ExtraLightItalic.ttf",
"sha1": "fd4b64a944d4353f216df49c028cde717161dd24"
},
{
"path": "src/assets/fonts/Nunito-Italic.ttf",
"sha1": "0c994f04c2bb328d87069d32e7422b559c05d5a7"
},
{
"path": "src/assets/fonts/Nunito-Light.ttf",
"sha1": "e69e3dfdaf5baaa6fc6db1dd031ed18e1dca38f3"
},
{
"path": "src/assets/fonts/Nunito-LightItalic.ttf",
"sha1": "6855c33fb876932b819dc445eeb4ae3267c4ef7d"
},
{
"path": "src/assets/fonts/Nunito-Medium.ttf",
"sha1": "75324fedc01df018cdf436bb5beba504bd82af0d"
},
{
"path": "src/assets/fonts/Nunito-MediumItalic.ttf",
"sha1": "e582f36e0b5d13c9e3ac827bd879593dec1a06d0"
},
{
"path": "src/assets/fonts/Nunito-Regular.ttf",
"sha1": "929ddc840610489715952aaa10af50e9c1a6a4a6"
},
{
"path": "src/assets/fonts/Nunito-SemiBold.ttf",
"sha1": "c4a5281a3e088e1d2604635d4d09a1583d036db6"
},
{
"path": "src/assets/fonts/Nunito-SemiBoldItalic.ttf",
"sha1": "96d4f551a905d9dd509fec0ae085f35666d8f797"
}
]
}
+296 -714
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -49,6 +49,7 @@
"react-native-responsive-dimensions": "^3.1.1", "react-native-responsive-dimensions": "^3.1.1",
"react-native-safe-area-context": "^5.6.1", "react-native-safe-area-context": "^5.6.1",
"react-native-screens": "^4.16.0", "react-native-screens": "^4.16.0",
"react-native-size-matters": "^0.0.3",
"react-native-svg": "^15.13.0", "react-native-svg": "^15.13.0",
"react-native-vector-icons": "^10.3.0", "react-native-vector-icons": "^10.3.0",
"react-native-walkthrough-tooltip": "^1.6.0", "react-native-walkthrough-tooltip": "^1.6.0",
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
project: {
ios: {},
android: {},
},
assets: ['./src/assets/fonts/'], // Path to your fonts
};
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+82 -98
View File
@@ -11,6 +11,8 @@ import { BlurView } from "@react-native-community/blur";
import { Ionicons } from "@react-native-vector-icons/ionicons"; import { Ionicons } from "@react-native-vector-icons/ionicons";
import colors from "./colors"; import colors from "./colors";
import { responsiveFontSize } from "react-native-responsive-dimensions"; import { responsiveFontSize } from "react-native-responsive-dimensions";
import { useTheme } from "../theme/ThemeProvider";
import FontsFamily from "../constents/FontsFamily";
export interface AlertButton { export interface AlertButton {
text: string; text: string;
@@ -29,7 +31,6 @@ export interface CustomAlertProps {
onClose?: () => void; onClose?: () => void;
} }
const BORDER_WIDTH = 6;
const RADIUS = 24; const RADIUS = 24;
const CustomAlert: React.FC<CustomAlertProps> = ({ const CustomAlert: React.FC<CustomAlertProps> = ({
@@ -40,6 +41,8 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
alertType = 'info', alertType = 'info',
onClose, onClose,
}) => { }) => {
const { theme } = useTheme();
const getIcon = () => { const getIcon = () => {
switch (alertType) { switch (alertType) {
case 'error': case 'error':
@@ -47,7 +50,7 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
case 'success': case 'success':
return { name: "checkmark-circle", color: colors.success }; return { name: "checkmark-circle", color: colors.success };
default: default:
return { name: "information-circle", color: colors.primary }; return { name: "information-circle", color: theme.colors.primary };
} }
}; };
const icon = getIcon(); const icon = getIcon();
@@ -64,34 +67,30 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
const getButtonStyle = (styleType: string | undefined) => { const getButtonStyle = (styleType: string | undefined) => {
switch (styleType) { switch (styleType) {
case "primary": case "primary":
return [styles.button, styles.primaryButton]; return [styles.primaryButton,{ backgroundColor: theme.colors.textPrimary }];
case "secondary": case "secondary":
return [styles.button, styles.secondaryButton]; return [styles.primaryButton,{ backgroundColor: theme.colors.textPrimary }];
case "outline": case "outline":
return [styles.button, styles.outlineButton]; return [styles.primaryButton,{ backgroundColor: theme.colors.textPrimary }];
case "destructive": case "destructive":
return [styles.button, styles.destructiveButton]; return [styles.primaryButton,{ backgroundColor: theme.colors.textPrimary }];
case "cancel": case "cancel":
return [styles.button, styles.cancelButton]; return [styles.primaryButton,{ backgroundColor: theme.colors.textPrimary }];
default: default:
return [styles.button, styles.defaultButton]; return [styles.primaryButton,{ backgroundColor: theme.colors.textPrimary }];
} }
}; };
const getButtonTextStyle = (styleType: string | undefined) => { const getButtonTextStyle = (styleType: string | undefined) => {
switch (styleType) { switch (styleType) {
case "primary":
return [styles.buttonText, styles.primaryButtonText];
case "secondary":
return [styles.buttonText, styles.secondaryButtonText];
case "outline": case "outline":
return [styles.buttonText, styles.outlineButtonText]; return { color: theme.colors.reverseText };
case "destructive":
return [styles.buttonText, styles.destructiveButtonText];
case "cancel": case "cancel":
return [styles.buttonText, styles.cancelButtonText]; return { color: colors.error };
case "secondary":
return { color: theme.colors.reverseText };
default: default:
return [styles.buttonText, styles.defaultButtonText]; return { color: theme.colors.reverseText };
} }
}; };
@@ -103,35 +102,49 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
onRequestClose={handleRequestClose} onRequestClose={handleRequestClose}
> >
<View style={styles.centeredView}> <View style={styles.centeredView}>
{/* Background Overlay */}
{Platform.OS === 'ios' ? ( {Platform.OS === 'ios' ? (
<BlurView style={styles.absolute} blurType="light" blurAmount={5} /> <BlurView style={styles.absolute} blurType={theme.mode === 'dark' ? "dark" : "light"} blurAmount={5} />
) : ( ) : (
<View style={[styles.absolute, { backgroundColor: colors.androidBackdrop }]} /> <View style={[styles.absolute, { backgroundColor: 'rgba(0,0,0,0.5)' }]} />
)} )}
<View style={styles.box}> {/* Alert Box */}
<View style={styles.faintOverlay} /> <View style={[
styles.box,
{
backgroundColor: theme.colors.card,
borderColor: theme.colors.border,
borderWidth: 1,
}
]}>
<View style={styles.contentContainer}> <View style={styles.contentContainer}>
<View style={styles.iconContainer}> <View style={styles.iconContainer}>
<Ionicons name={icon.name} size={36} color={icon.color} /> {/* smaller icon for vertical compression */} <Ionicons name={icon.name} size={36} color={theme.colors.reverseBackground} />
</View> </View>
<Text style={styles.alertTitle}>{title}</Text>
<Text style={styles.alertMessage}>{message}</Text> <Text style={[styles.alertTitle, { color: theme.colors.textPrimary }]}>{title}</Text>
<Text style={[styles.alertMessage, { color: theme.colors.textSecondary }]}>{message}</Text>
{buttons.length > 0 && ( {buttons.length > 0 && (
<View style={styles.buttonContainer}> <View style={styles.buttonContainer}>
{buttons.map((button, index) => ( {buttons.map((button, index) => (
<TouchableOpacity <TouchableOpacity
key={index} key={index}
style={[ style={[
...getButtonStyle(button.style), styles.button,
getButtonStyle(button.style),
// Custom overrides
button.style !== "cancel" && button.buttonColor ? { backgroundColor: button.buttonColor } : null, button.style !== "cancel" && button.buttonColor ? { backgroundColor: button.buttonColor } : null,
button.style === "outline" && button.buttonColor ? { borderColor: button.buttonColor } : null, button.style === "outline" && button.buttonColor ? { borderColor: button.buttonColor } : null,
]} ]}
onPress={button.onPress} onPress={button.onPress}
> >
<Text style={[ <Text style={[
...getButtonTextStyle(button.style), styles.buttonText,{color: theme.colors.reverseText},
button.style !== "cancel" && button.textColor ? { color: button.textColor } : null, getButtonTextStyle(button.style),
]}> ]}>
{button.text} {button.text}
</Text> </Text>
@@ -153,112 +166,83 @@ const styles = StyleSheet.create({
alignItems: "center", alignItems: "center",
}, },
absolute: { absolute: {
position: 'absolute', ...StyleSheet.absoluteFillObject,
top: 0,
left: 0,
bottom: 0,
right: 0,
}, },
box: { box: {
paddingHorizontal: 2, // reduce horizontal padding width: '85%',
padding: BORDER_WIDTH + 2, // reduce overall padding maxWidth: 340,
borderRadius: RADIUS + BORDER_WIDTH,
width: '90%',
maxWidth: 400,
alignSelf: 'center',
margin: 10, // reduce margin for vertical compression
backgroundColor: colors.pastelPink,
position: 'relative',
overflow: 'hidden',
},
faintOverlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: "rgba(255,255,255,0.77)",
zIndex: 1,
borderRadius: RADIUS, borderRadius: RADIUS,
alignSelf: 'center',
overflow: 'hidden',
shadowColor: "#000",
shadowOffset: { width: 0, height: 10 },
shadowOpacity: 0.25,
shadowRadius: 10,
elevation: 10,
}, },
contentContainer: { contentContainer: {
zIndex: 2, padding: 20,
padding: 12, // reduce container padding
alignItems: "center", alignItems: "center",
}, },
iconContainer: { iconContainer: {
marginBottom: 8, // reduce vertical space below icon marginBottom: 12,
}, },
alertTitle: { alertTitle: {
fontSize: responsiveFontSize(1.8), // smaller font size fontSize: responsiveFontSize(2),
fontWeight: "bold", fontFamily: FontsFamily.nunitoExtraBold,
color: colors.textDark, marginBottom: 8,
marginBottom: 4, // less space below title
textAlign: "center", textAlign: "center",
}, },
alertMessage: { alertMessage: {
fontSize: responsiveFontSize(1.4), // smaller font size fontFamily: FontsFamily.nunitoRegular,
color: colors.textLight, fontSize: responsiveFontSize(1.6),
textAlign: "center", textAlign: "center",
marginBottom: 12, // tighter spacing marginBottom: 20,
lineHeight: 18, // reduced line height lineHeight: 20,
}, },
buttonContainer: { buttonContainer: {
flexDirection: "row", flexDirection: "row",
justifyContent: 'flex-end', justifyContent: 'flex-end',
width: "100%", width: "100%",
gap: 8, // reduce gap between buttons gap: 10,
flexWrap: 'wrap', // Allow wrapping if many buttons
}, },
button: { button: {
paddingVertical: 8, // less vertical padding paddingVertical: 10,
paddingHorizontal: 16, // less horizontal padding paddingHorizontal: 20,
borderRadius: 16, borderRadius: 12,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
minWidth: 72, // slightly smaller minimum width minWidth: 80,
borderWidth: 0,
}, },
// === BUTTON STYLE VARIANTS === buttonText: {
fontSize: responsiveFontSize(1.6),
fontFamily: FontsFamily.nunitoBold,
},
// === BUTTON STYLES ===
defaultButton: { defaultButton: {
backgroundColor: colors.pastelPink, backgroundColor: colors.pastelPink, // Or theme.colors.primary
},
destructiveButton: {
backgroundColor: colors.error,
}, },
primaryButton: { primaryButton: {
backgroundColor: colors.primary, backgroundColor: colors.primary,
}, },
secondaryButton: { secondaryButton: {
backgroundColor: colors.pastelBlue, backgroundColor: '#E0E0E0', // Neutral gray
},
destructiveButton: {
backgroundColor: colors.error,
}, },
outlineButton: { outlineButton: {
borderColor: colors.primary, borderWidth: 1.5,
borderWidth: 2, borderColor: colors.primary, // Will be overridden by theme in render if needed
backgroundColor: "transparent", backgroundColor: 'transparent',
}, },
cancelButton: { cancelButton: {
borderColor: colors.error,
borderWidth: 1, borderWidth: 1,
backgroundColor: "transparent", borderColor: colors.error,
}, backgroundColor: 'transparent',
// === BUTTON TEXT COLOR VARIANTS ===
buttonText: {
fontSize: responsiveFontSize(1.4), // smaller text
fontWeight: "700",
},
defaultButtonText: {
color: colors.white,
},
destructiveButtonText: {
color: colors.white,
},
primaryButtonText: {
color: colors.white,
},
secondaryButtonText: {
color: colors.textDark,
},
outlineButtonText: {
color: colors.primary,
},
cancelButtonText: {
color: colors.error,
}, },
}); });
+11 -9
View File
@@ -1,9 +1,11 @@
import React, { useRef } from "react"; import React, { use, useRef } from "react";
import { View, Text, StyleSheet, Animated, TouchableWithoutFeedback } from "react-native"; import { View, Text, StyleSheet, Animated, TouchableWithoutFeedback } from "react-native";
import LinearGradient from "react-native-linear-gradient"; import LinearGradient from "react-native-linear-gradient";
import { Ionicons } from "@react-native-vector-icons/ionicons"; import { Ionicons } from "@react-native-vector-icons/ionicons";
import colors from "../colors"; import colors from "../colors";
import { responsiveFontSize } from "react-native-responsive-dimensions"; import { responsiveFontSize } from "react-native-responsive-dimensions";
import { useTheme } from "../../theme/ThemeProvider";
import FontsFamily from "../../constents/FontsFamily";
type CardProps = { type CardProps = {
title: string; title: string;
@@ -23,7 +25,7 @@ const AnimatedCard: React.FC<CardProps> = ({
backTwo = colors.pastelPink, backTwo = colors.pastelPink,
}) => { }) => {
const scale = useRef(new Animated.Value(1)).current; const scale = useRef(new Animated.Value(1)).current;
const{ theme } = useTheme()
const handlePress = () => { const handlePress = () => {
Animated.sequence([ Animated.sequence([
Animated.spring(scale, { toValue: 0.95, useNativeDriver: true }), Animated.spring(scale, { toValue: 0.95, useNativeDriver: true }),
@@ -33,11 +35,11 @@ const AnimatedCard: React.FC<CardProps> = ({
return ( return (
<TouchableWithoutFeedback onPress={handlePress}> <TouchableWithoutFeedback onPress={handlePress}>
<Animated.View style={[styles.card, { transform: [{ scale }] }]}> <Animated.View style={[styles.card, { transform: [{ scale }] }]}>
<LinearGradient colors={[backOne, backTwo]} style={styles.gradient}> <View style={[styles.gradient, { backgroundColor: theme.colors.reverseBackground }]}>
<Ionicons name={iconName} size={38} color="#fff" style={styles.icon} /> <Ionicons name={iconName} size={38} color={theme.colors.reverseText} style={styles.icon} />
<Text style={styles.title}>{title}</Text> <Text style={[styles.title, { color: theme.colors.reverseText }]}>{title}</Text>
{subtitle && <Text style={styles.subtitle}>{subtitle}</Text>} {subtitle && <Text style={[styles.subtitle, { color: theme.colors.reverseText }]}>{subtitle}</Text>}
</LinearGradient> </View>
</Animated.View> </Animated.View>
</TouchableWithoutFeedback> </TouchableWithoutFeedback>
); );
@@ -58,8 +60,8 @@ const styles = StyleSheet.create({
alignItems: "center", alignItems: "center",
}, },
icon: { marginBottom: 10 }, icon: { marginBottom: 10 },
title: { fontSize: responsiveFontSize(1.8), fontWeight: "bold", color: "#fff" }, title: { fontSize: responsiveFontSize(2.2), fontFamily: FontsFamily.nunitoExtraBold, color: "#fff" },
subtitle: { fontSize: responsiveFontSize(1.3), color: "#fff", opacity: 0.9 }, subtitle: { fontSize: responsiveFontSize(1.5), fontFamily: FontsFamily.nunitoRegular, opacity: 0.9 },
}); });
export default AnimatedCard; export default AnimatedCard;
+29
View File
@@ -0,0 +1,29 @@
const FontsFamily = {
nunitoLight: 'Nunito-Light',
nunitoBlack: 'Nunito-Black',
nunitoBlackItalic: 'Nunito-BlackItalic',
nunitoBold: 'Nunito-Bold',
nunitoBoldItalic: 'Nunito-BoldItalic',
nunitoExtraBold: 'Nunito-ExtraBold',
nunitoExtraBoldItalic: 'Nunito-ExtraBoldItalic',
nunitoExtraLight: 'Nunito-ExtraLight',
nunitoExtraLightItalic: 'Nunito-ExtraLightItalic',
nunitoItalic: 'Nunito-Italic',
nunitoLightItalic: 'Nunito-LightItalic',
nunitoMedium: 'Nunito-Medium',
nunitoMediumItalic: 'Nunito-MediumItalic',
nunitoRegular: 'Nunito-Regular',
nunitoSemiBold: 'Nunito-SemiBold',
nunitoSemiBoldItalic: 'Nunito-SemiBoldItalic',
};
export default FontsFamily;
+68 -46
View File
@@ -1,6 +1,15 @@
import React from "react"; import React, { useState, useEffect } from "react";
import { BottomTabNavigationProp, createBottomTabNavigator } from "@react-navigation/bottom-tabs"; import {
import { View, Text, StyleSheet, Platform, AccessibilityInfo } from "react-native"; BottomTabNavigationProp,
createBottomTabNavigator,
} from "@react-navigation/bottom-tabs";
import {
View,
Text,
StyleSheet,
Platform,
AccessibilityInfo,
} from "react-native";
import { Ionicons, IoniconsIconName } from "@react-native-vector-icons/ionicons"; import { Ionicons, IoniconsIconName } from "@react-native-vector-icons/ionicons";
import GroupScreen from "../screens/GroupScreen"; import GroupScreen from "../screens/GroupScreen";
import Profile from "../screens/profile/Profile"; import Profile from "../screens/profile/Profile";
@@ -8,8 +17,12 @@ import Home from "../screens/home/Home";
import colors from "../components/colors"; import colors from "../components/colors";
import Setting from "../screens/setting/Setting"; import Setting from "../screens/setting/Setting";
import ManagementScreen from "../screens/management/ManagementScreen"; import ManagementScreen from "../screens/management/ManagementScreen";
import { responsiveFontSize, responsiveHeight } from "react-native-responsive-dimensions"; import {
responsiveFontSize,
responsiveHeight,
} from "react-native-responsive-dimensions";
import { useTheme } from "../theme/ThemeProvider";
import { useSafeAreaInsets } from "react-native-safe-area-context";
const TAB_CONFIG = { const TAB_CONFIG = {
activeColor: "#000", activeColor: "#000",
@@ -19,60 +32,72 @@ const TAB_CONFIG = {
iconSize: 28, iconSize: 28,
}; };
// Background (tab highlight) colors
const activeTabColors: { [key: string]: string } = { const activeTabColors: { [key: string]: string } = {
Home: colors.pastelPink, // Soft pink Home: colors.pastelPink,
Profile: colors.pastelBlue, // Light sky blue Profile: colors.pastelBlue,
Group: colors.pastelGreen, // Gentle mint green Group: colors.pastelGreen,
Management: colors.pastelOrange, // Warm pastel yellow Management: colors.pastelOrange,
}; };
// Icon/Text colors on active tabs
const activeIconColors: { [key: string]: string } = { const activeIconColors: { [key: string]: string } = {
Home: "#333", Home: "#333",
Profile: "#333", Profile: "#333",
Group: "#333", Group: "#333",
Management: "#333", Management: "#333",
} };
export type RootTabParamList = { export type RootTabParamList = {
Home: undefined; Home: undefined;
Profile: undefined; Profile: undefined;
Group: undefined; Group: undefined;
Management: { actionTab?: "My Groups" | "Joined Groups" | "Requests" | "Your Requests" }; Management: {
actionTab?: "My Groups" | "Joined Groups" | "Requests" | "Your Requests";
};
}; };
// Define the navigation prop type for screens within the tab navigator
export type TabNavigationProp = BottomTabNavigationProp<RootTabParamList>; export type TabNavigationProp = BottomTabNavigationProp<RootTabParamList>;
const Tab = createBottomTabNavigator<RootTabParamList>(); const Tab = createBottomTabNavigator<RootTabParamList>();
const BottomTabs: React.FC = () => { const BottomTabs: React.FC = () => {
const { theme } = useTheme();
const insets = useSafeAreaInsets();
const [isReduceMotionEnabled, setIsReduceMotionEnabled] = React.useState<boolean>(false); const [isReduceMotionEnabled, setIsReduceMotionEnabled] =
const [isScreenReaderEnabled, setIsScreenReaderEnabled] = React.useState<boolean>(false); useState<boolean>(false);
const [isScreenReaderEnabled, setIsScreenReaderEnabled] =
useState<boolean>(false);
React.useEffect(() => { useEffect(() => {
// Check for accessibility settings
AccessibilityInfo.isScreenReaderEnabled().then(setIsScreenReaderEnabled); AccessibilityInfo.isScreenReaderEnabled().then(setIsScreenReaderEnabled);
AccessibilityInfo.isReduceMotionEnabled().then(setIsReduceMotionEnabled); AccessibilityInfo.isReduceMotionEnabled().then(setIsReduceMotionEnabled);
const screenReaderSubscription = AccessibilityInfo.addEventListener( const screenReaderSubscription = AccessibilityInfo.addEventListener(
'screenReaderChanged', "screenReaderChanged",
setIsScreenReaderEnabled setIsScreenReaderEnabled
); );
return () => screenReaderSubscription.remove(); return () => screenReaderSubscription.remove();
}, []); }, []);
// console.log("isReduceMotionEnabled : ", isReduceMotionEnabled)
// console.log("isScreenReaderEnabled : ", isScreenReaderEnabled)
return ( return (
<Tab.Navigator <Tab.Navigator
screenOptions={({ route }) => ({ screenOptions={({ route }) => ({
headerShown: false, headerShown: false,
tabBarStyle: [styles.tabBar, {marginVertical:isReduceMotionEnabled ? responsiveHeight(0.5) : responsiveHeight(3.5) || isScreenReaderEnabled ? responsiveHeight(0.5) : responsiveHeight(3.5)}], tabBarStyle: [
[styles.tabBar,{ backgroundColor: theme.colors.reverseBackground }],
{
bottom: insets.bottom + 10
},
],
// Make each tab item occupy the full bar height and center its content
tabBarItemStyle: {
height: "100%",
justifyContent: "center",
alignItems: "center",
padding: 0,
margin: 0,
},
tabBarLabel: ({ focused, color }) => tabBarLabel: ({ focused, color }) =>
!focused ? ( !focused ? (
@@ -82,32 +107,33 @@ const BottomTabs: React.FC = () => {
tabBarIcon: ({ focused }) => { tabBarIcon: ({ focused }) => {
let iconName: IoniconsIconName = "home-outline"; let iconName: IoniconsIconName = "home-outline";
// Determine icon based on route if (route.name === "Home")
if (route.name === "Home") iconName = focused ? "home" : "home-outline"; iconName = focused ? "home" : "home-outline";
else if (route.name === "Profile") iconName = focused ? "person" : "person-outline"; else if (route.name === "Profile")
else if (route.name === "Management") iconName = focused ? "settings" : "settings-outline"; iconName = focused ? "person" : "person-outline";
else if (route.name === "Group") iconName = focused ? "people" : "people-outline"; else if (route.name === "Management")
iconName = focused ? "settings" : "settings-outline";
else if (route.name === "Group")
iconName = focused ? "people" : "people-outline";
// 3. Get the active background color and icon color from the maps const activeBackgroundColor = theme.colors.background;
const activeBackgroundColor = activeTabColors[route.name];
const activeIconColor = activeIconColors[route.name]; const activeIconColor = activeIconColors[route.name];
return ( return (
<View <View
style={[ style={[
styles.iconContainer, styles.iconContainer,
// 4. Apply the dynamic background color when focused
focused && [ focused && [
styles.activeIconContainer, styles.activeIconContainer,
{ backgroundColor: activeBackgroundColor }, { backgroundColor: activeBackgroundColor },
], ],
]} ]}
> >
(route.name === "Management"{<Ionicons <Ionicons
name={iconName} name={iconName}
size={TAB_CONFIG.iconSize} size={TAB_CONFIG.iconSize}
color={focused ? activeIconColor : TAB_CONFIG.inactiveColor} color={focused ? activeIconColor : TAB_CONFIG.inactiveColor}
/>}:null) />
</View> </View>
); );
}, },
@@ -119,7 +145,7 @@ const BottomTabs: React.FC = () => {
<Tab.Screen name="Management" component={ManagementScreen} /> <Tab.Screen name="Management" component={ManagementScreen} />
</Tab.Navigator> </Tab.Navigator>
); );
} };
const styles = StyleSheet.create({ const styles = StyleSheet.create({
screen: { screen: {
@@ -135,19 +161,16 @@ const styles = StyleSheet.create({
}, },
tabBar: { tabBar: {
position: "absolute", position: "absolute",
bottom: 20,
left: 20, left: 20,
right: 20, right: 20,
height: TAB_CONFIG.height, height: TAB_CONFIG.height,
// backgroundColor: TAB_CONFIG.backgroundColor,
borderRadius: 20, borderRadius: 20,
shadowColor: "#000", shadowColor: "#000",
shadowOpacity: 0.1, shadowOpacity: 0.1,
shadowOffset: { width: 0, height: 5 }, shadowOffset: { width: 0, height: 5 },
shadowRadius: 8, shadowRadius: 8,
elevation: 10, elevation: 10,
paddingBottom: Platform.OS === "ios" ? 25 : 10, paddingTop: 0,
paddingTop: 10,
marginHorizontal: 10, marginHorizontal: 10,
}, },
label: { label: {
@@ -155,19 +178,18 @@ const styles = StyleSheet.create({
fontSize: responsiveFontSize(1.2), fontSize: responsiveFontSize(1.2),
}, },
iconContainer: { iconContainer: {
width: 60,
height: 50,
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
width: 80, // Reduced size for a sleeker look
height: 60, // Reduced size
marginTop: 10,
borderRadius: 15, borderRadius: 15,
}, },
activeIconContainer: { activeIconContainer: {
// The default active style, background color is now applied dynamically
elevation: 2, elevation: 2,
shadowColor: '#000', shadowColor: "#000",
shadowOpacity: 0.1, shadowOpacity: 0.1,
shadowRadius: 5, shadowRadius: 5,
}, },
}); });
export default BottomTabs
export default BottomTabs;
+85 -72
View File
@@ -22,14 +22,24 @@ import Ionicons from '@react-native-vector-icons/ionicons';
import MaterialRipple from 'react-native-material-ripple'; import MaterialRipple from 'react-native-material-ripple';
import CustomAlert from '../components/CostomAlart'; import CustomAlert from '../components/CostomAlart';
import colors from '../components/colors'; import colors from '../components/colors';
import { responsiveFontSize } from 'react-native-responsive-dimensions'; import {
responsiveHeight,
responsiveWidth,
responsiveFontSize
} from "react-native-responsive-dimensions";
import { useTheme } from '../theme/ThemeProvider';
import FontsFamily from '../constents/FontsFamily';
const { width } = Dimensions.get('window'); const { width } = Dimensions.get('window');
const BLOCK_SIZE = width / 3; const BLOCK_SIZE = width / 3;
// Note: You will need to install react-native-material-ripple // Note: You will need to install react-native-material-ripple
// npm install react-native-material-ripple // npm install react-native-material-ripple
export default function LoginScreen() { export default function LoginScreen() {
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>(); const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();
const [mobile, setMobile] = useState(''); const [mobile, setMobile] = useState('');
@@ -42,9 +52,12 @@ export default function LoginScreen() {
alertType: 'info' as 'info' | 'success' | 'error', alertType: 'info' as 'info' | 'success' | 'error',
}); });
const fade = useRef(new Animated.Value(0)).current; const fade = useRef(new Animated.Value(0)).current;
const slide = useRef(new Animated.Value(30)).current; const slide = useRef(new Animated.Value(30)).current;
const ctaScale = useRef(new Animated.Value(1)).current; const ctaScale = useRef(new Animated.Value(1)).current;
const { theme } = useTheme();
useEffect(() => { useEffect(() => {
Animated.parallel([ Animated.parallel([
@@ -53,6 +66,7 @@ export default function LoginScreen() {
]).start(); ]).start();
}, []); }, []);
const showAlert = ( const showAlert = (
title: string, title: string,
message: string, message: string,
@@ -62,10 +76,12 @@ export default function LoginScreen() {
setAlertInfo({ visible: true, title, message, buttons, alertType }); setAlertInfo({ visible: true, title, message, buttons, alertType });
}; };
const hideAlert = () => { const hideAlert = () => {
setAlertInfo({ ...alertInfo, visible: false }); setAlertInfo({ ...alertInfo, visible: false });
}; };
const handleContinue = async () => { const handleContinue = async () => {
if (mobile.length === 10) { if (mobile.length === 10) {
setLoading(true); setLoading(true);
@@ -87,58 +103,64 @@ export default function LoginScreen() {
} }
}; };
const colorsBlocks = [ const colorsBlocks = [
colors.pastelPink, theme.colors.logincard,
colors.pastelBlue, theme.colors.logincard1,
colors.pastelGreen, theme.colors.logincard1,
colors.pastelOrange, theme.colors.logincard,
colors.pastelBlue, theme.colors.logincard,
colors.pastelPink, theme.colors.logincard1,
colors.pastelGreen, theme.colors.logincard1,
colors.pastelOrange, theme.colors.logincard,
colors.pastelPink, theme.colors.logincard,
]; ];
return (
<View style={styles.container}>
<StatusBar barStyle="dark-content" backgroundColor={colors.pastelOrange} />
{/* Funky grid blocks background (Unchanged) */} return (
<View style={styles.gridContainer}> <View style={[ styles.container, {backgroundColor: theme.colors.background }]}>
{colorsBlocks.map((bgColor, index) => ( <StatusBar barStyle={theme.mode === "dark" ? "light-content" : "dark-content"} backgroundColor={colors.pastelOrange} />
<View
key={index}
style={[ {/* Funky grid blocks background (Unchanged) */}
styles.gridBlock, <View style={styles.gridContainer}>
{ backgroundColor: bgColor }, {colorsBlocks.map((bgColor, index) => (
index % 2 === 0 ? styles.elevatedBlock : {}, <View
]} key={index}
/> style={[
))} styles.gridBlock,
</View> { backgroundColor: bgColor ,borderRadius:16 },
index % 2 === 0 ? styles.elevatedBlock : {},
]}
/>
))}
</View>
<KeyboardAvoidingView <KeyboardAvoidingView
style={styles.keyboardAvoidingContainer} style={styles.keyboardAvoidingContainer}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
> >
<View style={styles.contentContainer}> <View style={styles.contentContainer}>
<View style={styles.topSection}> <View style={[ styles.topSection, {marginTop:60}]}>
<Animated.View style={{ opacity: fade, transform: [{ translateY: slide }] }}> <Animated.View style={{ opacity: fade, transform: [{ translateY: slide }] }}>
<Text style={styles.brand}>CircleCollect</Text> <Text style={[styles.brand, { color: theme.colors.textPrimary }]}>CircleCollect</Text>
<Text style={styles.title}>Welcome Back!</Text> <Text style={[styles.title, { color: theme.colors.textPrimary }]}>Welcome Back!</Text>
<Text style={styles.subtitle}> <Text style={[styles.subtitle, { color: theme.colors.textSecondary }]}>
Let's get you signed in to your community. Let's get you signed in to your community.
</Text> </Text>
</Animated.View> </Animated.View>
</View> </View>
<Animated.View style={{ opacity: fade, transform: [{ translateY: slide }] }}>
<View style={styles.inputWrapper}> <Animated.View style={{ opacity: fade, transform: [{ translateY: slide }] ,marginTop: 150}}>
<Ionicons name="call-outline" size={24} color={colors.textLight} style={styles.inputIcon} /> <View style={[styles.inputWrapper, { backgroundColor: theme.colors.reverseBackground }]}>
<Ionicons name="call-outline" size={24} color={theme.colors.reverseText} style={styles.inputIcon} />
<TextInput <TextInput
style={styles.input} style={[ styles.input, { color: theme.colors.reverseText, fontFamily: FontsFamily.nunitoRegular }]}
placeholder="Enter your mobile number" placeholder="Enter your mobile number"
placeholderTextColor={colors.textLight} placeholderTextColor={theme.colors.reverseText}
value={mobile} value={mobile}
onChangeText={setMobile} onChangeText={setMobile}
keyboardType="phone-pad" keyboardType="phone-pad"
@@ -148,6 +170,7 @@ export default function LoginScreen() {
</View> </View>
</Animated.View> </Animated.View>
<Animated.View style={[styles.bottomSection, { transform: [{ scale: ctaScale }] }]}> <Animated.View style={[styles.bottomSection, { transform: [{ scale: ctaScale }] }]}>
<MaterialRipple <MaterialRipple
onPress={handleContinue} onPress={handleContinue}
@@ -157,21 +180,16 @@ export default function LoginScreen() {
rippleCentered rippleCentered
rippleContainerBorderRadius={30} rippleContainerBorderRadius={30}
> >
<LinearGradient <View style={[styles.buttonGradient, { backgroundColor: theme.colors.reverseBackground }]} >
colors={[colors.pastelPink, colors.pastelOrange]}
style={styles.buttonGradient}
start={{x: 0, y: 0}}
end={{x: 1, y: 1}}
>
{loading ? ( {loading ? (
<ActivityIndicator color={colors.white} /> <ActivityIndicator color={theme.colors.reverseText} />
) : ( ) : (
<View style={styles.buttonContent}> <View style={styles.buttonContent}>
<Text style={styles.buttonText}>Continue</Text> <Text style={[styles.buttonText, { color: theme.colors.reverseText }]}>Continue</Text>
<Ionicons name="arrow-forward-circle-outline" size={22} color={colors.white} style={{marginLeft: 8}} /> <Ionicons name="arrow-forward-circle-outline" size={22} color={theme.colors.reverseText} style={{marginLeft: 8}} />
</View> </View>
)} )}
</LinearGradient> </View>
</MaterialRipple> </MaterialRipple>
<Text style={styles.footerText}> <Text style={styles.footerText}>
Your privacy is our priority. Your information is always secure. Your privacy is our priority. Your information is always secure.
@@ -180,6 +198,7 @@ export default function LoginScreen() {
</View> </View>
</KeyboardAvoidingView> </KeyboardAvoidingView>
<CustomAlert <CustomAlert
visible={alertInfo.visible} visible={alertInfo.visible}
title={alertInfo.title} title={alertInfo.title}
@@ -191,6 +210,7 @@ export default function LoginScreen() {
); );
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
@@ -207,11 +227,11 @@ const styles = StyleSheet.create({
borderRadius: 16, borderRadius: 16,
}, },
elevatedBlock: { elevatedBlock: {
shadowColor: '#000', shadowColor: '#ffffffff',
shadowOffset: { width: 0, height: 5 }, shadowOffset: { width: 1, height: 5 },
shadowOpacity: 0.15, shadowOpacity: 1,
shadowRadius: 10, shadowRadius: 15,
elevation: 7, elevation: 10,
}, },
keyboardAvoidingContainer: { keyboardAvoidingContainer: {
flex: 1, flex: 1,
@@ -227,8 +247,7 @@ const styles = StyleSheet.create({
alignItems: 'center', alignItems: 'center',
}, },
brand: { brand: {
fontSize: responsiveFontSize(2.8), fontSize: responsiveFontSize(3),
fontWeight: '700',
color: colors.primary, color: colors.primary,
textAlign: 'center', textAlign: 'center',
letterSpacing: 2, letterSpacing: 2,
@@ -236,33 +255,29 @@ const styles = StyleSheet.create({
textShadowColor: 'rgba(0, 0, 0, 0.1)', textShadowColor: 'rgba(0, 0, 0, 0.1)',
textShadowOffset: { width: 1, height: 1 }, textShadowOffset: { width: 1, height: 1 },
textShadowRadius: 2, textShadowRadius: 2,
fontFamily: FontsFamily.nunitoExtraBold,
}, },
title: { title: {
fontSize: responsiveFontSize(4), fontSize: responsiveFontSize(5),
fontWeight: 'bold', fontFamily: FontsFamily.nunitoBold,
color: colors.textDark,
textAlign: 'center', textAlign: 'center',
marginBottom: 14, marginBottom: 14,
}, },
subtitle: { subtitle: {
fontSize: responsiveFontSize(1.6), fontSize: responsiveFontSize(1.6),
color: colors.textDark, fontFamily: FontsFamily.nunitoRegular,
textAlign: 'center', textAlign: 'center',
lineHeight: 26, lineHeight: 26,
maxWidth: 320, maxWidth: 320,
}, },
inputWrapper: { inputWrapper: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
backgroundColor: 'rgba(255, 255, 255, 0.85)',
borderRadius: 36, borderRadius: 36,
borderWidth: 2, borderWidth: 2,
borderColor: colors.pastelPink, paddingHorizontal: responsiveHeight(2),
paddingHorizontal: 20,
shadowColor: colors.primary,
shadowOffset: { width: 0, height: 6 },
shadowOpacity: 0.2,
shadowRadius: 14,
elevation: 8, elevation: 8,
}, },
inputIcon: { inputIcon: {
@@ -271,9 +286,9 @@ const styles = StyleSheet.create({
input: { input: {
flex: 1, flex: 1,
fontSize: responsiveFontSize(1.8), fontSize: responsiveFontSize(1.8),
fontWeight: '600', fontFamily: FontsFamily.nunitoSemiBold,
color: colors.textLight, color: colors.textLight,
height: 58, height: responsiveHeight(5),
}, },
bottomSection: { bottomSection: {
alignItems: 'center', alignItems: 'center',
@@ -282,16 +297,13 @@ const styles = StyleSheet.create({
button: { button: {
width: '100%', width: '100%',
borderRadius: 30, borderRadius: 30,
shadowColor: colors.primary,
shadowOffset: { width: 0, height: 10 },
shadowOpacity: 0.4,
shadowRadius: 20,
elevation: 14, elevation: 14,
marginTop: 20, marginTop: 20,
}, },
buttonGradient: { buttonGradient: {
borderRadius: 30,
paddingVertical: 18, borderRadius:42,
paddingVertical: 14,
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
@@ -302,8 +314,8 @@ const styles = StyleSheet.create({
}, },
buttonText: { buttonText: {
color: colors.white, color: colors.white,
fontWeight: '800',
fontSize: responsiveFontSize(1.8), fontSize: responsiveFontSize(1.8),
fontFamily: FontsFamily.nunitoBold,
}, },
footerText: { footerText: {
fontSize: responsiveFontSize(1.4), fontSize: responsiveFontSize(1.4),
@@ -311,5 +323,6 @@ const styles = StyleSheet.create({
marginTop: 28, marginTop: 28,
textAlign: 'center', textAlign: 'center',
maxWidth: 300, maxWidth: 300,
fontFamily: FontsFamily.nunitoRegular,
}, },
}); });
+87 -57
View File
@@ -21,6 +21,8 @@ import { LOGIN_URL, VARIFY_OTP_URL } from "../utils/ApiUrls";
import colors from "../components/colors"; import colors from "../components/colors";
import CustomAlert from "../components/CostomAlart"; import CustomAlert from "../components/CostomAlart";
import { getFcmToken } from "../utils/firebase/firebaseServices"; import { getFcmToken } from "../utils/firebase/firebaseServices";
import { useTheme } from "../theme/ThemeProvider";
import FontsFamily from "../constents/FontsFamily";
const CELL_COUNT = 6; const CELL_COUNT = 6;
const { width } = Dimensions.get('window'); const { width } = Dimensions.get('window');
@@ -50,6 +52,7 @@ export default function OtpVerificationScreen() {
buttons: [], buttons: [],
alertType: "info", alertType: "info",
}); });
const { theme } = useTheme();
const inputs = useRef<Array<TextInput | null>>([]); const inputs = useRef<Array<TextInput | null>>([]);
const route = useRoute<OtpVerifyRouteProp>(); const route = useRoute<OtpVerifyRouteProp>();
@@ -133,17 +136,17 @@ export default function OtpVerificationScreen() {
await getFcmToken(res.data?.data?.userId); await getFcmToken(res.data?.data?.userId);
let hasActionRun = false; let hasActionRun = false;
const successAction = () => { const successAction = () => {
if (hasActionRun) return; if (hasActionRun) return;
hasActionRun = true; hasActionRun = true;
hideAlert(); hideAlert();
navigation.dispatch( navigation.dispatch(
CommonActions.reset({ CommonActions.reset({
index: 0, index: 0,
routes: [{ name: 'Home' }], routes: [{ name: 'Home' }],
}) })
); );
}; };
triggerAlert( triggerAlert(
'Success!', 'Success!',
@@ -238,56 +241,64 @@ export default function OtpVerificationScreen() {
} }
}; };
const colorsBlocks = [ const colorsBlocks = [
colors.pastelPink, theme.colors.logincard,
colors.pastelBlue, theme.colors.logincard1,
colors.pastelGreen, theme.colors.logincard1,
colors.pastelOrange, theme.colors.logincard,
colors.pastelBlue, theme.colors.logincard,
colors.pastelPink, theme.colors.logincard1,
colors.pastelGreen, theme.colors.logincard1,
colors.pastelOrange, theme.colors.logincard,
colors.pastelPink, theme.colors.logincard,
]; ];
return ( return (
<View style={styles.container}> <View style={[styles.container, {backgroundColor: theme.colors.background }]}>
<StatusBar barStyle="dark-content" backgroundColor={colors.pastelOrange} /> <StatusBar barStyle={theme.mode === "dark" ? "light-content" : "dark-content"} backgroundColor={colors.pastelOrange} />
<View style={styles.gridContainer}> <View style={styles.gridContainer}>
{colorsBlocks.map((bgColor, index) => ( {colorsBlocks.map((bgColor, index) => (
<View <View
key={index} key={index}
style={[ style={[
styles.gridBlock, styles.gridBlock,
{ backgroundColor: bgColor }, { backgroundColor: bgColor, borderRadius: 16 },
index % 2 === 0 ? styles.elevatedBlock : {}, index % 2 === 0 ? styles.elevatedBlock : {},
]} ]}
/> />
))} ))}
</View> </View>
<KeyboardAvoidingView style={styles.keyboardAvoidingContainer} behavior={Platform.OS === "ios" ? "padding" : undefined}> <KeyboardAvoidingView style={styles.keyboardAvoidingContainer} behavior={Platform.OS === "ios" ? "padding" : undefined}>
<CustomAlert {...alertConfig} /> <CustomAlert {...alertConfig} />
<View style={styles.contentPanel}> <View style={[styles.contentPanel, { backgroundColor: theme.colors.reverseBackground }]}>
<View style={styles.topSection}> <View style={styles.topSection}>
<Text style={styles.title}>Verify Your Code</Text> <Text style={[styles.title, { color: theme.colors.reverseText }]}>Verify Your Code</Text>
<Text style={styles.subtitle}>We’ve sent a 6-digit verification code to +91 {mobile}.</Text> <Text style={[styles.subtitle, { color: theme.colors.reverseText }]}>We’ve sent a 6-digit verification code to +91 {mobile}.</Text>
<Animated.View style={[styles.otpRow, { opacity: fade, transform: [{ translateY: slide }] }]}> <Animated.View style={[styles.otpRow, { transform: [{ translateY: slide }] }]}>
{Array.from({ length: CELL_COUNT }).map((_, i) => ( {Array.from({ length: CELL_COUNT }).map((_, i) => (
<TextInput <TextInput
key={`otp-${i}`} key={`otp-${i}`}
ref={(r) => { inputs.current[i] = r; }} ref={(r) => { inputs.current[i] = r; }}
style={[styles.cell, code[i] ? styles.cellFilled : null]} // UPDATED CELL STYLE
style={[
styles.cell,
code[i] ? styles.cellFilled : null,
{
backgroundColor: theme.colors.optCard,
color: theme.colors.reverseText,
}
]}
keyboardType="number-pad" keyboardType="number-pad"
maxLength={i === 0 ? CELL_COUNT : 1} maxLength={i === 0 ? CELL_COUNT : 1}
value={code[i]} value={code[i]}
onChangeText={(t) => (i === 0 ? handleFirstCellChange(t) : updateIndex(i, t))} onChangeText={(t) => (i === 0 ? handleFirstCellChange(t) : updateIndex(i, t))}
onKeyPress={(e) => handleKeyPress(i, e)} onKeyPress={(e) => handleKeyPress(i, e)}
placeholder="•" placeholder="*"
placeholderTextColor="#BDBDBD" placeholderTextColor="#BDBDBD"
textContentType="oneTimeCode" textContentType="oneTimeCode"
/> />
@@ -300,16 +311,16 @@ export default function OtpVerificationScreen() {
onPressIn={() => Animated.spring(ctaScale, { toValue: 0.97, useNativeDriver: true }).start()} onPressIn={() => Animated.spring(ctaScale, { toValue: 0.97, useNativeDriver: true }).start()}
onPressOut={() => Animated.spring(ctaScale, { toValue: 1, useNativeDriver: true }).start()} onPressOut={() => Animated.spring(ctaScale, { toValue: 1, useNativeDriver: true }).start()}
onPress={() => onSubmit(code.join(""), mobile)} onPress={() => onSubmit(code.join(""), mobile)}
style={styles.button} style={[styles.button, { backgroundColor: theme.colors.background }]}
> >
<Text style={styles.buttonText}>Verify & Continue</Text> <Text style={[styles.buttonText, { color: theme.colors.textPrimary }]}>Verify & Continue</Text>
</Pressable> </Pressable>
{timer > 0 ? ( {timer > 0 ? (
<Text style={styles.footerText}>Resend code in 00:{timer.toString().padStart(2, "0")}</Text> <Text style={[styles.footerText, { color: theme.colors.reverseText }]}>Resend code in 00:{timer.toString().padStart(2, "0")}</Text>
) : ( ) : (
<Pressable onPress={resendOtp} disabled={isResending}> <Pressable onPress={resendOtp} disabled={isResending}>
<Text style={[styles.footerText, { color: colors.pastelPink, fontWeight: '600' }]}> <Text style={[styles.footerText, { color: theme.colors.reverseText, fontWeight: '600' }]}>
{isResending ? "Resending..." : "Resend Code"} {isResending ? "Resending..." : "Resend Code"}
</Text> </Text>
</Pressable> </Pressable>
@@ -335,16 +346,15 @@ const styles = StyleSheet.create({
borderRadius: 16, borderRadius: 16,
}, },
elevatedBlock: { elevatedBlock: {
shadowColor: '#000', shadowColor: '#ffffffff',
shadowOffset: { width: 0, height: 5 }, shadowOffset: { width: 1, height: 5 },
shadowOpacity: 0.15, shadowOpacity: 1,
shadowRadius: 10, shadowRadius: 15,
elevation: 7, elevation: 10,
}, },
keyboardAvoidingContainer: { flex: 1, justifyContent: 'center' }, keyboardAvoidingContainer: { flex: 1, justifyContent: 'center' },
contentPanel: { contentPanel: {
marginHorizontal: 24, marginHorizontal: 24,
backgroundColor: 'rgba(255, 255, 255, 0.95)',
borderRadius: 32, borderRadius: 32,
padding: 28, padding: 28,
shadowColor: '#000', shadowColor: '#000',
@@ -354,13 +364,33 @@ const styles = StyleSheet.create({
elevation: 12, elevation: 12,
}, },
topSection: { alignItems: 'center', marginBottom: 24 }, topSection: { alignItems: 'center', marginBottom: 24 },
title: { fontSize: 28, fontWeight: "bold", color: colors.textDark, textAlign: 'center', marginBottom: 12 }, title: { fontSize: 28, color: colors.textDark, textAlign: 'center', marginBottom: 12 ,fontFamily: FontsFamily.nunitoExtraBold},
subtitle: { fontSize: 16, color: colors.textLight, textAlign: 'center', marginBottom: 32, lineHeight: 24, maxWidth: 300 }, subtitle: { fontSize: 16, color: colors.textLight, textAlign: 'center', marginBottom: 32, lineHeight: 24, maxWidth: 300 ,fontFamily: FontsFamily.nunitoMedium},
otpRow: { flexDirection: "row", justifyContent: "space-between", width: '100%', paddingHorizontal: 10 },
cell: { width: 48, height: 56, borderRadius: 12, borderWidth: 1.5, borderColor: colors.inputBorder, backgroundColor: colors.white, textAlign: "center", fontSize: 22, fontWeight: '700', color: colors.textDark, shadowColor: "#000", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.08, shadowRadius: 8, elevation: 3 }, otpRow: {
cellFilled: { borderColor: colors.primary, backgroundColor: '#F7F2FF' }, flexDirection: "row",
justifyContent: "space-between",
width: '100%',
paddingHorizontal: 0, // Removed padding to use full width
},
// FIX: Updated Cell Styling for clean smooth radius
cell: {
width: 45,
height: 52,
borderRadius: 14, // Smoother radius
textAlign: "center",
fontSize: 20,
fontWeight: "600",
elevation: 0, // Removed elevation for flat look (or keep if you want shadow)
overflow: 'hidden',
},
cellFilled: {
// Optional: Add specific style when filled if needed, handled inline above
},
bottomSection: { paddingBottom: Platform.OS === "ios" ? 20 : 10, paddingTop: 10 }, bottomSection: { paddingBottom: Platform.OS === "ios" ? 20 : 10, paddingTop: 10 },
button: { backgroundColor: colors.pastelPink, paddingVertical: 18, borderRadius: 30, alignItems: "center", justifyContent: "center", shadowColor: colors.primary, shadowOffset: { width: 0, height: 8 }, shadowOpacity: 0.3, shadowRadius: 15, elevation: 8 ,borderWidth:2, borderColor:colors.pastelPink}, button: { backgroundColor: colors.pastelPink, paddingVertical: 18, borderRadius: 30, alignItems: "center", justifyContent: "center",},
buttonText: { color: colors.white, fontWeight: "700", fontSize: 16 }, buttonText: { color: colors.white, fontSize: 16, fontFamily: FontsFamily.nunitoBold },
footerText: { textAlign: "center", color: colors.textLight, fontSize: 14, marginTop: 20 }, footerText: { textAlign: "center", fontFamily: FontsFamily.nunitoBold, fontSize: 14, marginTop: 20 },
}); });
+19 -11
View File
@@ -10,6 +10,7 @@ import {
ListRenderItem, ListRenderItem,
Alert, Alert,
RefreshControl, RefreshControl,
StatusBar,
} from "react-native"; } from "react-native";
import AppView from "../../components/AppView"; import AppView from "../../components/AppView";
import colors from "../../components/colors"; import colors from "../../components/colors";
@@ -23,6 +24,10 @@ import AnimatedCard from "../../components/common/AnimatedCard";
import { Ionicons } from "@react-native-vector-icons/ionicons"; import { Ionicons } from "@react-native-vector-icons/ionicons";
import { apiCall } from "../../utils/ApiHelper"; import { apiCall } from "../../utils/ApiHelper";
import CustomAlert, { CustomAlertProps } from "../../components/CostomAlart"; import CustomAlert, { CustomAlertProps } from "../../components/CostomAlart";
import { useTheme } from "../../theme/ThemeProvider";
import FontsFamily from "../../constents/FontsFamily";
import { responsiveFontSize, responsiveHeight } from "react-native-responsive-dimensions";
import { useSafeAreaInsets } from "react-native-safe-area-context";
if (Platform.OS === "android" && UIManager.setLayoutAnimationEnabledExperimental) { if (Platform.OS === "android" && UIManager.setLayoutAnimationEnabledExperimental) {
UIManager.setLayoutAnimationEnabledExperimental(true); UIManager.setLayoutAnimationEnabledExperimental(true);
@@ -80,6 +85,8 @@ const Home: React.FC = () => {
const [userDashboard, setUserDashboard] = useState<any>(null); const [userDashboard, setUserDashboard] = useState<any>(null);
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
const [statusData, setStatusData] = useState<any>(null); const [statusData, setStatusData] = useState<any>(null);
const insets = useSafeAreaInsets();
const {theme} = useTheme();
const getDashboardData = async () => { const getDashboardData = async () => {
try { try {
const result = (await apiCall(DASHBOARD_URL, "GET")) as any; const result = (await apiCall(DASHBOARD_URL, "GET")) as any;
@@ -186,7 +193,8 @@ const Home: React.FC = () => {
}; };
return ( return (
<AppView barStyle="dark-content" style={styles.container}> <View style={[ styles.container, {backgroundColor: theme.colors.background, paddingBottom: insets.bottom + responsiveHeight(10) }]}>
<StatusBar barStyle={theme.mode === "dark" ? "light-content" : "dark-content"}/>
<FlatList <FlatList
data={actions} data={actions}
numColumns={2} numColumns={2}
@@ -195,18 +203,19 @@ const Home: React.FC = () => {
ListHeaderComponent={ ListHeaderComponent={
<> <>
<View style={styles.header}> <View style={styles.header}>
<View> <View style={{ flexDirection: "row", alignItems: "center", gap: 8 ,marginTop: responsiveHeight(2) }} >
<Text style={styles.greeting}> <Text style={[styles.greeting, { color: theme.colors.textPrimary }]}>
Hello, <Text style={{ fontWeight: 'bold' }}>{user.firstName || apiUser?.firstName || "User"}</Text> Hello,
</Text> </Text>
<Text style={{ fontFamily:FontsFamily.nunitoExtraBold,color: theme.colors.textPrimary,fontSize:responsiveFontSize(2.5) }}>{user.firstName || apiUser?.firstName || "User"}</Text>
</View> </View>
<TouchableOpacity onPress={() => navigation.navigate("Notification" as never)}> <TouchableOpacity onPress={() => navigation.navigate("Notification" as never)} style={{ marginTop: responsiveHeight(2) ,}}>
{unreadCount > 0 && ( {unreadCount > 0 && (
<View style={styles.badge}> <View style={styles.badge}>
<Text style={styles.badgeText}>{unreadCount}</Text> <Text style={styles.badgeText}>{unreadCount}</Text>
</View> </View>
)} )}
<Ionicons name="notifications-outline" size={30} color={colors.textDark} /> <Ionicons name="notifications-outline" size={30} color={theme.colors.textPrimary} />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
@@ -217,7 +226,7 @@ const Home: React.FC = () => {
/> />
<View style={styles.actionHeader}> <View style={styles.actionHeader}>
<Text style={styles.sectionTitle}>Quick Actions</Text> <Text style={[styles.sectionTitle,{ color: theme.colors.textPrimary }]}>Quick Actions </Text>
</View> </View>
</> </>
@@ -243,7 +252,7 @@ const Home: React.FC = () => {
onClose={() => setAlertVisible(false)} onClose={() => setAlertVisible(false)}
/> />
)} )}
</AppView> </View>
); );
}; };
@@ -252,7 +261,6 @@ export default Home;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
backgroundColor: colors.white,
}, },
header: { header: {
flexDirection: "row", flexDirection: "row",
@@ -262,8 +270,8 @@ const styles = StyleSheet.create({
paddingTop: 24, paddingTop: 24,
paddingBottom: 16, paddingBottom: 16,
}, },
greeting: { fontSize: 22, color: colors.textDark }, greeting: { fontSize: 22,fontFamily: FontsFamily.nunitoLight },
sectionTitle: { fontSize: 20, fontWeight: "600", color: colors.textDark, flex: 1 }, sectionTitle: { fontSize: 20, fontWeight: "600", color: colors.textDark, flex: 1 ,fontFamily: FontsFamily.nunitoSemiBold},
actionHeader: { actionHeader: {
flexDirection: "row", flexDirection: "row",
justifyContent: "space-between", justifyContent: "space-between",
+87 -101
View File
@@ -21,6 +21,8 @@ import {
responsiveWidth, responsiveWidth,
responsiveFontSize responsiveFontSize
} from "react-native-responsive-dimensions"; } from "react-native-responsive-dimensions";
import { useTheme } from "../../theme/ThemeProvider";
import FontsFamily from "../../constents/FontsFamily";
type StatusData = { type StatusData = {
role?: string; role?: string;
@@ -73,7 +75,7 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
const [showPayDialog, setShowPayDialog] = useState(false); const [showPayDialog, setShowPayDialog] = useState(false);
const [selectedGroupIds, setSelectedGroupIds] = useState<string[]>([]); const [selectedGroupIds, setSelectedGroupIds] = useState<string[]>([]);
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
const {theme} = useTheme();
// console.log("user me : ", userDashboard) // console.log("user me : ", userDashboard)
// Preselect all due groups at mount and on change // Preselect all due groups at mount and on change
useEffect(() => { useEffect(() => {
@@ -92,12 +94,12 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
// Top grid info items // Top grid info items
const infoItems = useMemo( const infoItems = useMemo(
() => [ () => [
{ icon: <Ionicons name="person-outline" size={20} color={colors.primary} />, label: "Role", value: statusData?.role || "-" }, { icon: <Ionicons name="person-outline" size={20} color={theme.colors.black} />, label: "Role", value: statusData?.role || "-" },
{ icon: <Ionicons name="pie-chart-outline" size={20} color={colors.primary} />, label: "Active Groups", value: userDashboard?.activeStatusMyGroupCount?.toString() || "0" }, { icon: <Ionicons name="pie-chart-outline" size={20} color={theme.colors.black} />, label: "Active Groups", value: userDashboard?.activeStatusMyGroupCount?.toString() || "0" },
{ icon: <Ionicons name="book-outline" size={20} color={colors.primary} />, label: "Joined Groups", value: userDashboard?.myJoinedGroups?.length?.toString() || "0" }, { icon: <Ionicons name="book-outline" size={20} color={theme.colors.black} />, label: "Joined Groups", value: userDashboard?.myJoinedGroups?.length?.toString() || "0" },
{ icon: <Ionicons name="people-outline" size={20} color={colors.primary} />, label: "My Groups", value: userDashboard?.myCreatedGroups?.length?.toString() || "0" }, { icon: <Ionicons name="people-outline" size={20} color={theme.colors.black} />, label: "My Groups", value: userDashboard?.myCreatedGroups?.length?.toString() || "0" },
{ icon: <Ionicons name="document-text-outline" size={20} color={colors.primary} />, label: "Total Due", value: `₹${userDashboard?.multiplePayAmount || 0}` }, { icon: <Ionicons name="document-text-outline" size={20} color={theme.colors.black} />, label: "Total Due", value: `₹${userDashboard?.multiplePayAmount || 0}` },
{ icon: <Ionicons name="checkmark-done-outline" size={20} color={colors.primary} />, label: "Completed", value: userDashboard?.completeStatusMyGroupCount?.toString() || "0" }, { icon: <Ionicons name="checkmark-done-outline" size={20} color={theme.colors.black} />, label: "Completed", value: userDashboard?.completeStatusMyGroupCount?.toString() || "0" },
], ],
[userDashboard, statusData] [userDashboard, statusData]
); );
@@ -153,18 +155,18 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
// Bottom bar // Bottom bar
const renderDueBar = () => ( const renderDueBar = () => (
<View style={styles.dueBar}> <View style={[ styles.dueBar, { borderTopColor: theme.colors.border, backgroundColor: theme.colors.gray }]}>
<Text style={styles.dueBarText}> <Text style={[styles.dueBarText, { color: theme.colors.black }]}>
Total Due: <Text style={{ color: "#FFDE03", fontWeight: "bold" }}>₹{totalDue}</Text> Total Due: <Text style={{ color: theme.colors.black, fontWeight: "bold" }}>₹{totalDue}</Text>
</Text> </Text>
<TouchableOpacity <TouchableOpacity
style={styles.payAllButton} style={[styles.payAllButton, { backgroundColor: theme.colors.black }]}
onPress={() => setShowPayDialog(true)} onPress={() => setShowPayDialog(true)}
disabled={dueGroups.length === 0} disabled={dueGroups.length === 0}
activeOpacity={0.8} activeOpacity={0.8}
> >
<Ionicons name="wallet-outline" size={20} color="#000000ff" /> <Ionicons name="wallet-outline" size={20} color={theme.colors.white} />
<Text style={styles.payAllButtonText}>Pay All</Text> <Text style={[styles.payAllButtonText, { color: theme.colors.white }]}>Pay All</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
); );
@@ -173,17 +175,14 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
const renderPayDialog = () => ( const renderPayDialog = () => (
<Modal visible={showPayDialog} transparent animationType="fade" onRequestClose={() => setShowPayDialog(false)}> <Modal visible={showPayDialog} transparent animationType="fade" onRequestClose={() => setShowPayDialog(false)}>
<View style={styles.payModalOverlay}> <View style={styles.payModalOverlay}>
<LinearGradient <View
colors={[colors.white, colors.white]} style={[styles.payModalBox, { backgroundColor: theme.colors.reverseBackground }]}
start={{ x: 0.2, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.payModalBox}
> >
<View style={styles.payDialogHeader}> <View style={styles.payDialogHeader}>
<Ionicons name="card-outline" color="#000000ff" size={22} /> <Ionicons name="card-outline" color={theme.colors.reverseText} size={22} />
<Text style={styles.payDialogTitle}>Select Groups to Pay</Text> <Text style={[styles.payDialogTitle, { color: theme.colors.reverseText }]}>Select Groups to Pay</Text>
<TouchableOpacity onPress={() => setShowPayDialog(false)}> <TouchableOpacity onPress={() => setShowPayDialog(false)}>
<Ionicons name="close-circle" color="#000000ff" size={24} /> <Ionicons name="close-circle" color={theme.colors.reverseText} size={24} />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<ScrollView <ScrollView
@@ -193,70 +192,73 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
{dueGroups.map((group) => ( {dueGroups.map((group) => (
<TouchableOpacity <TouchableOpacity
key={group.groupId} key={group.groupId}
style={[ style={[
styles.payGroupOption, styles.payGroupOption,
selectedGroupIds.includes(group.groupId) && styles.payOptionSelected, { borderColor: theme.colors.background, borderWidth: 1 },
]}
selectedGroupIds.includes(group.groupId) && {
...styles.payOptionSelected,
borderColor: theme.colors.reverseBackground,
},
]}
onPress={() => handleToggleGroup(group.groupId)} onPress={() => handleToggleGroup(group.groupId)}
activeOpacity={0.85} activeOpacity={0.85}
> >
<Ionicons <Ionicons
name={selectedGroupIds.includes(group.groupId) ? "checkbox" : "square-outline"} name={selectedGroupIds.includes(group.groupId) ? "checkbox" : "square-outline"}
size={22} size={22}
color={selectedGroupIds.includes(group.groupId) ? colors.primary : "#000000ff"} color={selectedGroupIds.includes(group.groupId) ? theme.colors.reverseText : theme.colors.reverseText}
/> />
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<Text style={[styles.payGroupName, { color: "#000000ff" }]}>{group.groupName}</Text> <Text style={[styles.payGroupName, {color: theme.colors.reverseText}]}>{group.groupName}</Text>
<Text style={styles.payDueText}> <Text style={[styles.payDueText, {color: theme.colors.reverseText}]}>
₹{group.baseAmount} ₹{group.baseAmount}
{group.lateFee > 0 ? ` (Late: ₹${group.lateFee})` : ""} {group.lateFee > 0 ? ` (Late: ₹${group.lateFee})` : ""}
</Text> </Text>
</View> </View>
<Text style={[styles.payAmount, { color: "#4CAF50" }]}>₹{group.totalDue}</Text> {/* <Text style={[styles.payAmount, { color: "#4CAF50" }]}>₹{group.totalDue}</Text> */}
</TouchableOpacity> </TouchableOpacity>
))} ))}
</ScrollView> </ScrollView>
<View style={styles.payDialogFooter}> <View style={styles.payDialogFooter}>
<Text style={styles.payDialogTotal}> <Text style={[styles.payDialogTotal, { color: theme.colors.reverseText }]}>
Total: <Text style={{ fontWeight: "bold", color: "#ff5f03ff" }}>₹{totalDueSelected}</Text> Total : <Text style={{ fontFamily: FontsFamily.nunitoBold, color: theme.colors.red }}>₹{totalDueSelected}</Text>
</Text> </Text>
<TouchableOpacity <TouchableOpacity
style={[styles.payConfirmButton, { opacity: submitting ? 0.7 : 1 }]} style={[styles.payConfirmButton, { opacity: submitting ? 0.7 : 1 , backgroundColor: theme.colors.background}]}
onPress={handlePayAll} onPress={handlePayAll}
disabled={submitting || selectedGroupIds.length === 0} disabled={submitting || selectedGroupIds.length === 0}
activeOpacity={0.8} activeOpacity={0.8}
> >
{submitting ? ( {submitting ? (
<ActivityIndicator color="#fff" /> <ActivityIndicator color={theme.colors.textPrimary} />
) : ( ) : (
<> <>
<Ionicons name="checkmark-circle" size={22} color="#ffffffff" /> <Ionicons name="checkmark-circle" size={22} color={theme.colors.textPrimary} />
<Text style={styles.payButtonText}>Pay Selected</Text> <Text style={[styles.payButtonText, { fontFamily: FontsFamily.nunitoSemiBold ,color: theme.colors.textPrimary}]}>Pay Selected</Text>
</> </>
)} )}
</TouchableOpacity> </TouchableOpacity>
</View> </View>
</LinearGradient> </View>
</View> </View>
</Modal> </Modal>
); );
return ( return (
<View style={styles.container}> <View style={styles.container}>
<LinearGradient <View
colors={[colors.primary, colors.pastelPink]} style={[styles.card, { backgroundColor: theme.colors.reverseBackground }]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.card}
> >
<View style={styles.headerSection}> <View style={styles.headerSection}>
<View style={styles.titleWithIcon}> <View style={styles.titleWithIcon}>
<Ionicons name="stats-chart" size={24} color="#fff" style={styles.headerIcon} /> <Ionicons name="stats-chart" size={24} color={theme.colors.reverseText} style={styles.headerIcon} />
<Text style={styles.header}>Profile Overview</Text> <Text style={[styles.header, { color: theme.colors.reverseText} ]}>Profile Overview</Text>
</View> </View>
<View style={styles.statusBadge}> <View style={[styles.statusBadge, { backgroundColor: theme.colors.background}]}>
<Ionicons name="shield-checkmark" size={16} color="#fff" /> <Ionicons name="shield-checkmark" size={16} color={theme.colors.textPrimary} />
<Text style={styles.statusBadgeText}> <Text style={[styles.statusBadgeText, { color: theme.colors.textPrimary }]}>
{statusData?.profileStatus ? "Verified" : "Pending"} {statusData?.profileStatus ? "Verified" : "Pending"}
</Text> </Text>
</View> </View>
@@ -265,16 +267,16 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
<View style={styles.leftSection}> <View style={styles.leftSection}>
<View style={styles.infoGrid}> <View style={styles.infoGrid}>
{infoItems.map((item, idx) => ( {infoItems.map((item, idx) => (
<View key={item.label} style={styles.infoCard}> <View key={item.label} style={[styles.infoCard,{backgroundColor: theme.colors.background}]}>
<View style={styles.infoCardHeader}> <View style={styles.infoCardHeader}>
<View style={styles.iconContainer}> <View style={[styles.iconContainer, {backgroundColor: theme.colors.optCard || theme.colors.card}]}>
{item.icon} {item.icon}
</View> </View>
<Text style={styles.infoLabel} > <Text style={[styles.infoLabel,{color: theme.colors.textPrimary}]} >
{item.label} {item.label}
</Text> </Text>
</View> </View>
<Text style={[styles.infoValue, { color: getValueColor(item.value) }]} numberOfLines={1}> <Text style={[styles.infoValue, { color: theme.colors.textPrimary }]} numberOfLines={1}>
{item.value} {item.value}
</Text> </Text>
</View> </View>
@@ -296,16 +298,16 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
</View> </View>
)} )}
</View> </View>
<View style={styles.infoCardOne}> <View style={[styles.infoCardOne,{backgroundColor: theme.colors.background}]}>
<Text style={{ color: "#fff", fontSize: responsiveWidth(2.3)}}>Total Paid Amount <Text style={{ fontWeight: "900"}}>{userDashboard?.totalPaidAmountMonth}</Text></Text> <Text style={{ color: theme.colors.textPrimary, fontSize: responsiveWidth(3), fontFamily: FontsFamily.nunitoSemiBold,}}>Total Paid Amount</Text>
{/* <Text style={{ color: "#fff", fontWeight: "400"}}>Total Paid Amount {userDashboard?.totalPaidAmountMonth}</Text> */} <Text style={{ color: theme.colors.textPrimary, fontWeight: "400", fontFamily: FontsFamily.nunitoRegular}}>{userDashboard?.totalPaidAmountMonth}</Text>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
</View> </View>
{renderDueBar()} {renderDueBar()}
</LinearGradient> </View>
{renderPayDialog()} {renderPayDialog()}
@@ -377,8 +379,8 @@ const styles = StyleSheet.create({
marginRight: 8 marginRight: 8
}, },
header: { header: {
fontSize: responsiveWidth(3.5), fontSize: responsiveWidth(4.5),
fontWeight: "700", fontFamily: FontsFamily.nunitoBold,
color: "#fff" color: "#fff"
}, },
statusBadge: { statusBadge: {
@@ -411,14 +413,11 @@ const styles = StyleSheet.create({
gap: 10 gap: 10
}, },
infoCard: { infoCard: {
backgroundColor: 'rgba(255,255,255,0.15)',
borderRadius: 14,
padding: 12, padding: 12,
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.2)',
flex: 1, flex: 1,
minWidth: '45%', minWidth: '45%',
marginBottom: 8, marginBottom: 8,
borderRadius:12
}, },
infoCardOne: { infoCardOne: {
backgroundColor: 'rgba(255,255,255,0.15)', backgroundColor: 'rgba(255,255,255,0.15)',
@@ -436,25 +435,22 @@ const styles = StyleSheet.create({
marginBottom: 6 marginBottom: 6
}, },
iconContainer: { iconContainer: {
width: 28, width: responsiveWidth(8),
height: 28, height: responsiveHeight(3.5),
backgroundColor: '#fff', borderRadius: responsiveWidth(2),
borderRadius: 8,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
marginRight: 8, marginRight: 8,
}, },
infoLabel: { infoLabel: {
color: "rgba(255,255,255,0.9)", fontSize: responsiveWidth(3),
fontSize: responsiveWidth(2), fontFamily: FontsFamily.nunitoSemiBold,
fontWeight: '500',
flexShrink: 1, flexShrink: 1,
}, },
infoValue: { infoValue: {
fontWeight: "700", fontFamily: FontsFamily.nunitoBold,
fontSize: responsiveWidth(2.5), fontSize: responsiveWidth(3),
textAlign: 'left', textAlign: 'left',
color: "#fff"
}, },
profileSection: { profileSection: {
width: 90, width: 90,
@@ -498,16 +494,13 @@ const styles = StyleSheet.create({
alignItems: "center", alignItems: "center",
padding: 16, padding: 16,
borderTopWidth: 1, borderTopWidth: 1,
borderColor: "rgba(255,255,255,0.15)",
backgroundColor: "rgba(0,0,0,0.15)",
}, },
dueBarText: { dueBarText: {
color: "#fff", fontFamily: FontsFamily.nunitoExtraBold,
fontSize: responsiveWidth(2.5), fontSize: responsiveWidth(4),
fontWeight: "600"
}, },
payAllButton: { payAllButton: {
backgroundColor: "#ffffffff",
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
paddingHorizontal: 16, paddingHorizontal: 16,
@@ -516,8 +509,7 @@ const styles = StyleSheet.create({
gap: 6, gap: 6,
}, },
payAllButtonText: { payAllButtonText: {
color: "#000000ff", fontFamily: FontsFamily.nunitoBold,
fontWeight: "bold",
fontSize: responsiveWidth(2.5) fontSize: responsiveWidth(2.5)
}, },
payModalOverlay: { payModalOverlay: {
@@ -527,7 +519,7 @@ const styles = StyleSheet.create({
alignItems: "center", alignItems: "center",
}, },
payModalBox: { payModalBox: {
width: "92%", width: "90%",
borderRadius: 24, borderRadius: 24,
overflow: "hidden", overflow: "hidden",
alignItems: "center", alignItems: "center",
@@ -547,9 +539,10 @@ const styles = StyleSheet.create({
borderColor: "rgba(255,255,255,0.1)", borderColor: "rgba(255,255,255,0.1)",
}, },
payDialogTitle: { payDialogTitle: {
color: "#000000ff",
fontSize: responsiveWidth(1.8), fontSize: responsiveWidth(4),
fontWeight: "700" fontFamily: FontsFamily.nunitoBold,
}, },
payGroupOption: { payGroupOption: {
flexDirection: "row", flexDirection: "row",
@@ -557,24 +550,20 @@ const styles = StyleSheet.create({
gap: 16, gap: 16,
paddingVertical: 12, paddingVertical: 12,
paddingHorizontal: 14, paddingHorizontal: 14,
borderColor: colors.primary,
borderWidth: 1,
borderRadius: 12, borderRadius: 12,
marginBottom: 8, marginBottom: 8,
}, },
payOptionSelected: { payOptionSelected: {
backgroundColor: "rgba(166, 49, 255, 0.2)",
borderColor: colors.primary,
borderWidth: 1, borderWidth: 1,
}, },
payGroupName: { payGroupName: {
color: "#000000ff", fontFamily: FontsFamily.nunitoSemiBold,
fontWeight: "700", fontSize: responsiveWidth(3)
fontSize: responsiveWidth(2.5)
}, },
payDueText: { payDueText: {
color: "rgba(0, 0, 0, 0.8)", fontSize: responsiveWidth(3),
fontSize: responsiveWidth(1.3), fontFamily: FontsFamily.nunitoRegular,
marginTop: 2 marginTop: 2
}, },
payAmount: { payAmount: {
@@ -591,26 +580,23 @@ const styles = StyleSheet.create({
paddingHorizontal: 18, paddingHorizontal: 18,
}, },
payDialogTotal: { payDialogTotal: {
color: "#000000ff", fontFamily: FontsFamily.nunitoBold,
fontWeight: "600", fontSize: responsiveWidth(3.5)
fontSize: responsiveWidth(1.7)
}, },
payConfirmButton: { payConfirmButton: {
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
backgroundColor: colors.primary, paddingVertical: 8,
paddingVertical: 12, paddingHorizontal: 18,
paddingHorizontal: 24, borderRadius: 12,
borderRadius: 20,
elevation: 4, elevation: 4,
borderColor: colors.primary,
borderWidth: 1,
gap: 8, gap: 8,
}, },
payButtonText: { payButtonText: {
color: "#ffffffff", color: "#ffffffff",
fontWeight: "bold", fontWeight: "bold",
fontSize: responsiveWidth(2.5) fontSize: responsiveWidth(3),
}, },
modalOverlay: { modalOverlay: {
flex: 1, flex: 1,
+1 -1
View File
@@ -1,4 +1,4 @@
// src/screens/ManagementScreen.tsx // src/screens/ManagementScreen.tsx
import React, { useState, useCallback, FC, useEffect } from "react"; import React, { useState, useCallback, FC, useEffect } from "react";
import { import {
View, View,
+61
View File
@@ -0,0 +1,61 @@
// ThemeProvider.tsx
import React, { createContext, useContext, useEffect, useState } from 'react';
import { Appearance } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Theme, ThemeMode, light, dark } from './theme';
interface ThemeContextProps {
theme: Theme;
mode: ThemeMode;
setThemeMode: (mode: ThemeMode | 'system') => void;
}
const ThemeContext = createContext<ThemeContextProps | null>(null);
export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
const [mode, setMode] = useState<ThemeMode | 'system'>('system');
const [theme, setTheme] = useState<Theme>(light);
// Load saved theme mode
useEffect(() => {
(async () => {
const stored = await AsyncStorage.getItem('app_theme');
if (stored) setMode(stored as ThemeMode | 'system');
})();
}, []);
// Detect system theme + apply user choice
useEffect(() => {
const applyTheme = () => {
const system = Appearance.getColorScheme() === 'dark' ? 'dark' : 'light';
const finalMode = mode === 'system' ? system : mode;
setTheme(finalMode === 'dark' ? dark : light);
};
applyTheme();
const listener = Appearance.addChangeListener(applyTheme);
return () => listener.remove();
}, [mode]);
// Update & save theme mode
const setThemeMode = async (newMode: ThemeMode | 'system') => {
setMode(newMode);
await AsyncStorage.setItem('app_theme', newMode);
};
return (
<ThemeContext.Provider value={{ theme, mode: theme.mode, setThemeMode }}>
{children}
</ThemeContext.Provider>
);
};
// Custom hook
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) throw new Error("useTheme must be used inside ThemeProvider");
return context;
};
+75
View File
@@ -0,0 +1,75 @@
export type ThemeMode = 'light' | 'dark';
export interface ThemeColors {
primary: string;
secondary: string;
background: string;
card: string;
textPrimary: string;
textSecondary: string;
success: string;
border: string;
logincard?: string;
logincard1?: string;
reverseBackground: string;
reverseText?: string;
optCard?: string;
gray?: string;
black?: string;
white?: string;
red?: string;
green?: string;
}
export interface Theme {
mode: ThemeMode;
colors: ThemeColors;
}
export const light: Theme = {
mode: 'light',
colors: {
primary: '#F4721E',
secondary: '#FF9A62',
reverseBackground: '#000000',
background: '#FFFFFF',
card: '#F3F4F6',
textPrimary: '#000000',
reverseText: '#FFFFFF',
textSecondary: '#666666',
success: '#008e02',
border: '#E5E7EB',
logincard: '#e0e0e0',
logincard1: '#66666eb3',
optCard: '#66666eb3',
gray:'#e0e0e0ff',
black:'#000000',
white:'#FFFFFF',
red:'#FF0000',
green:'#00C853'
},
};
export const dark: Theme = {
mode: 'dark',
colors: {
primary: '#F4721E',
secondary: '#FF9A62',
reverseBackground: '#FFFFFF',
background: '#000000', // Fixed: Dark mode = Black bg
card: '#111214',
textPrimary: '#FFFFFF',
textSecondary: '#A1A1A1',
reverseText: '#000000',
success: '#00C853',
border: '#222427',
logincard: '#000000',
logincard1: '#8d8d8f',
optCard: '#e0e0e0',
gray:'#66666eb3',
black:'#000000',
white:'#FFFFFF',
red:'#FF0000',
green:'#00C853'
},
};