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
+22 -15
View File
@@ -1,36 +1,39 @@
import React, { useEffect } from 'react';
import { StyleSheet, useColorScheme } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { PaperProvider } from 'react-native-paper';
import AppNavigator from './src/navigation/AppNavigator';
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() {
const isDarkMode = useColorScheme() === 'dark';
useEffect(() => {
// 🔹 Ask for permission
requestPermission();
// 🔹 Handle background + quit state
backgroundHandler();
setupFirebaseHandlers()
setupNotificationPressHandlers()
setupFirebaseHandlers();
setupNotificationPressHandlers();
// 🔹 Listen for foreground messages
const unsubscribe = foregroundListener();
// 🔹 Listen for token refresh
const unsubscribeRefresh = tokenRefreshHandler();
// 🔹 Create default notification channel (Android)
notifee.createChannel({
id: 'default',
name: 'Default Channel',
importance: 4, // HIGH
importance: 4,
});
return () => {
@@ -39,19 +42,23 @@ function App() {
};
}, []);
return (
<SafeAreaProvider>
<ThemeProvider>
<PaperProvider >
<AppNavigator />
</PaperProvider>
</ThemeProvider>
</SafeAreaProvider>
);
}
export default App;
const styles = StyleSheet.create({
container: {
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 */; };
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
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 */
/* Begin PBXFileReference section */
@@ -24,6 +40,22 @@
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>"; };
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 */
/* Begin PBXFrameworksBuildPhase section */
@@ -74,6 +106,7 @@
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
BBD78D7AC51CEA395F1C20DB /* Pods */,
20F28563BF834255BD7383AF /* Resources */,
);
indentWidth = 2;
sourceTree = "<group>";
@@ -97,6 +130,30 @@
path = Pods;
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 */
/* Begin PBXNativeTarget section */
@@ -159,6 +216,22 @@
files = (
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard 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;
};
+20 -2
View File
@@ -26,14 +26,13 @@
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<!-- Do not change NSAllowsArbitraryLoads to true, or you will risk app rejection! -->
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<string/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
@@ -48,5 +47,24 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<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>
</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-safe-area-context": "^5.6.1",
"react-native-screens": "^4.16.0",
"react-native-size-matters": "^0.0.3",
"react-native-svg": "^15.13.0",
"react-native-vector-icons": "^10.3.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 colors from "./colors";
import { responsiveFontSize } from "react-native-responsive-dimensions";
import { useTheme } from "../theme/ThemeProvider";
import FontsFamily from "../constents/FontsFamily";
export interface AlertButton {
text: string;
@@ -29,7 +31,6 @@ export interface CustomAlertProps {
onClose?: () => void;
}
const BORDER_WIDTH = 6;
const RADIUS = 24;
const CustomAlert: React.FC<CustomAlertProps> = ({
@@ -40,6 +41,8 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
alertType = 'info',
onClose,
}) => {
const { theme } = useTheme();
const getIcon = () => {
switch (alertType) {
case 'error':
@@ -47,7 +50,7 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
case 'success':
return { name: "checkmark-circle", color: colors.success };
default:
return { name: "information-circle", color: colors.primary };
return { name: "information-circle", color: theme.colors.primary };
}
};
const icon = getIcon();
@@ -64,34 +67,30 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
const getButtonStyle = (styleType: string | undefined) => {
switch (styleType) {
case "primary":
return [styles.button, styles.primaryButton];
return [styles.primaryButton,{ backgroundColor: theme.colors.textPrimary }];
case "secondary":
return [styles.button, styles.secondaryButton];
return [styles.primaryButton,{ backgroundColor: theme.colors.textPrimary }];
case "outline":
return [styles.button, styles.outlineButton];
return [styles.primaryButton,{ backgroundColor: theme.colors.textPrimary }];
case "destructive":
return [styles.button, styles.destructiveButton];
return [styles.primaryButton,{ backgroundColor: theme.colors.textPrimary }];
case "cancel":
return [styles.button, styles.cancelButton];
return [styles.primaryButton,{ backgroundColor: theme.colors.textPrimary }];
default:
return [styles.button, styles.defaultButton];
return [styles.primaryButton,{ backgroundColor: theme.colors.textPrimary }];
}
};
const getButtonTextStyle = (styleType: string | undefined) => {
switch (styleType) {
case "primary":
return [styles.buttonText, styles.primaryButtonText];
case "secondary":
return [styles.buttonText, styles.secondaryButtonText];
case "outline":
return [styles.buttonText, styles.outlineButtonText];
case "destructive":
return [styles.buttonText, styles.destructiveButtonText];
return { color: theme.colors.reverseText };
case "cancel":
return [styles.buttonText, styles.cancelButtonText];
return { color: colors.error };
case "secondary":
return { color: theme.colors.reverseText };
default:
return [styles.buttonText, styles.defaultButtonText];
return { color: theme.colors.reverseText };
}
};
@@ -103,35 +102,49 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
onRequestClose={handleRequestClose}
>
<View style={styles.centeredView}>
{/* Background Overlay */}
{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}>
<View style={styles.faintOverlay} />
{/* Alert Box */}
<View style={[
styles.box,
{
backgroundColor: theme.colors.card,
borderColor: theme.colors.border,
borderWidth: 1,
}
]}>
<View style={styles.contentContainer}>
<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>
<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 && (
<View style={styles.buttonContainer}>
{buttons.map((button, index) => (
<TouchableOpacity
key={index}
style={[
...getButtonStyle(button.style),
styles.button,
getButtonStyle(button.style),
// Custom overrides
button.style !== "cancel" && button.buttonColor ? { backgroundColor: button.buttonColor } : null,
button.style === "outline" && button.buttonColor ? { borderColor: button.buttonColor } : null,
]}
onPress={button.onPress}
>
<Text style={[
...getButtonTextStyle(button.style),
button.style !== "cancel" && button.textColor ? { color: button.textColor } : null,
styles.buttonText,{color: theme.colors.reverseText},
getButtonTextStyle(button.style),
]}>
{button.text}
</Text>
@@ -153,112 +166,83 @@ const styles = StyleSheet.create({
alignItems: "center",
},
absolute: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
...StyleSheet.absoluteFillObject,
},
box: {
paddingHorizontal: 2, // reduce horizontal padding
padding: BORDER_WIDTH + 2, // reduce overall padding
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,
width: '85%',
maxWidth: 340,
borderRadius: RADIUS,
alignSelf: 'center',
overflow: 'hidden',
shadowColor: "#000",
shadowOffset: { width: 0, height: 10 },
shadowOpacity: 0.25,
shadowRadius: 10,
elevation: 10,
},
contentContainer: {
zIndex: 2,
padding: 12, // reduce container padding
padding: 20,
alignItems: "center",
},
iconContainer: {
marginBottom: 8, // reduce vertical space below icon
marginBottom: 12,
},
alertTitle: {
fontSize: responsiveFontSize(1.8), // smaller font size
fontWeight: "bold",
color: colors.textDark,
marginBottom: 4, // less space below title
fontSize: responsiveFontSize(2),
fontFamily: FontsFamily.nunitoExtraBold,
marginBottom: 8,
textAlign: "center",
},
alertMessage: {
fontSize: responsiveFontSize(1.4), // smaller font size
color: colors.textLight,
fontFamily: FontsFamily.nunitoRegular,
fontSize: responsiveFontSize(1.6),
textAlign: "center",
marginBottom: 12, // tighter spacing
lineHeight: 18, // reduced line height
marginBottom: 20,
lineHeight: 20,
},
buttonContainer: {
flexDirection: "row",
justifyContent: 'flex-end',
width: "100%",
gap: 8, // reduce gap between buttons
gap: 10,
flexWrap: 'wrap', // Allow wrapping if many buttons
},
button: {
paddingVertical: 8, // less vertical padding
paddingHorizontal: 16, // less horizontal padding
borderRadius: 16,
paddingVertical: 10,
paddingHorizontal: 20,
borderRadius: 12,
justifyContent: 'center',
alignItems: 'center',
minWidth: 72, // slightly smaller minimum width
borderWidth: 0,
minWidth: 80,
},
// === BUTTON STYLE VARIANTS ===
buttonText: {
fontSize: responsiveFontSize(1.6),
fontFamily: FontsFamily.nunitoBold,
},
// === BUTTON STYLES ===
defaultButton: {
backgroundColor: colors.pastelPink,
},
destructiveButton: {
backgroundColor: colors.error,
backgroundColor: colors.pastelPink, // Or theme.colors.primary
},
primaryButton: {
backgroundColor: colors.primary,
},
secondaryButton: {
backgroundColor: colors.pastelBlue,
backgroundColor: '#E0E0E0', // Neutral gray
},
destructiveButton: {
backgroundColor: colors.error,
},
outlineButton: {
borderColor: colors.primary,
borderWidth: 2,
backgroundColor: "transparent",
borderWidth: 1.5,
borderColor: colors.primary, // Will be overridden by theme in render if needed
backgroundColor: 'transparent',
},
cancelButton: {
borderColor: colors.error,
borderWidth: 1,
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,
borderColor: colors.error,
backgroundColor: 'transparent',
},
});
+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 LinearGradient from "react-native-linear-gradient";
import { Ionicons } from "@react-native-vector-icons/ionicons";
import colors from "../colors";
import { responsiveFontSize } from "react-native-responsive-dimensions";
import { useTheme } from "../../theme/ThemeProvider";
import FontsFamily from "../../constents/FontsFamily";
type CardProps = {
title: string;
@@ -23,7 +25,7 @@ const AnimatedCard: React.FC<CardProps> = ({
backTwo = colors.pastelPink,
}) => {
const scale = useRef(new Animated.Value(1)).current;
const{ theme } = useTheme()
const handlePress = () => {
Animated.sequence([
Animated.spring(scale, { toValue: 0.95, useNativeDriver: true }),
@@ -33,11 +35,11 @@ const AnimatedCard: React.FC<CardProps> = ({
return (
<TouchableWithoutFeedback onPress={handlePress}>
<Animated.View style={[styles.card, { transform: [{ scale }] }]}>
<LinearGradient colors={[backOne, backTwo]} style={styles.gradient}>
<Ionicons name={iconName} size={38} color="#fff" style={styles.icon} />
<Text style={styles.title}>{title}</Text>
{subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
</LinearGradient>
<View style={[styles.gradient, { backgroundColor: theme.colors.reverseBackground }]}>
<Ionicons name={iconName} size={38} color={theme.colors.reverseText} style={styles.icon} />
<Text style={[styles.title, { color: theme.colors.reverseText }]}>{title}</Text>
{subtitle && <Text style={[styles.subtitle, { color: theme.colors.reverseText }]}>{subtitle}</Text>}
</View>
</Animated.View>
</TouchableWithoutFeedback>
);
@@ -58,8 +60,8 @@ const styles = StyleSheet.create({
alignItems: "center",
},
icon: { marginBottom: 10 },
title: { fontSize: responsiveFontSize(1.8), fontWeight: "bold", color: "#fff" },
subtitle: { fontSize: responsiveFontSize(1.3), color: "#fff", opacity: 0.9 },
title: { fontSize: responsiveFontSize(2.2), fontFamily: FontsFamily.nunitoExtraBold, color: "#fff" },
subtitle: { fontSize: responsiveFontSize(1.5), fontFamily: FontsFamily.nunitoRegular, opacity: 0.9 },
});
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 { BottomTabNavigationProp, createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { View, Text, StyleSheet, Platform, AccessibilityInfo } from "react-native";
import React, { useState, useEffect } from "react";
import {
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 GroupScreen from "../screens/GroupScreen";
import Profile from "../screens/profile/Profile";
@@ -8,8 +17,12 @@ import Home from "../screens/home/Home";
import colors from "../components/colors";
import Setting from "../screens/setting/Setting";
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 = {
activeColor: "#000",
@@ -19,60 +32,72 @@ const TAB_CONFIG = {
iconSize: 28,
};
// Background (tab highlight) colors
const activeTabColors: { [key: string]: string } = {
Home: colors.pastelPink, // Soft pink
Profile: colors.pastelBlue, // Light sky blue
Group: colors.pastelGreen, // Gentle mint green
Management: colors.pastelOrange, // Warm pastel yellow
Home: colors.pastelPink,
Profile: colors.pastelBlue,
Group: colors.pastelGreen,
Management: colors.pastelOrange,
};
// Icon/Text colors on active tabs
const activeIconColors: { [key: string]: string } = {
Home: "#333",
Profile: "#333",
Group: "#333",
Management: "#333",
}
};
export type RootTabParamList = {
Home: undefined;
Profile: 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>;
const Tab = createBottomTabNavigator<RootTabParamList>();
const BottomTabs: React.FC = () => {
const { theme } = useTheme();
const insets = useSafeAreaInsets();
const [isReduceMotionEnabled, setIsReduceMotionEnabled] = React.useState<boolean>(false);
const [isScreenReaderEnabled, setIsScreenReaderEnabled] = React.useState<boolean>(false);
const [isReduceMotionEnabled, setIsReduceMotionEnabled] =
useState<boolean>(false);
const [isScreenReaderEnabled, setIsScreenReaderEnabled] =
useState<boolean>(false);
React.useEffect(() => {
// Check for accessibility settings
useEffect(() => {
AccessibilityInfo.isScreenReaderEnabled().then(setIsScreenReaderEnabled);
AccessibilityInfo.isReduceMotionEnabled().then(setIsReduceMotionEnabled);
const screenReaderSubscription = AccessibilityInfo.addEventListener(
'screenReaderChanged',
"screenReaderChanged",
setIsScreenReaderEnabled
);
return () => screenReaderSubscription.remove();
}, []);
// console.log("isReduceMotionEnabled : ", isReduceMotionEnabled)
// console.log("isScreenReaderEnabled : ", isScreenReaderEnabled)
return (
<Tab.Navigator
screenOptions={({ route }) => ({
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 }) =>
!focused ? (
@@ -82,32 +107,33 @@ const BottomTabs: React.FC = () => {
tabBarIcon: ({ focused }) => {
let iconName: IoniconsIconName = "home-outline";
// Determine icon based on route
if (route.name === "Home") iconName = focused ? "home" : "home-outline";
else if (route.name === "Profile") iconName = focused ? "person" : "person-outline";
else if (route.name === "Management") iconName = focused ? "settings" : "settings-outline";
else if (route.name === "Group") iconName = focused ? "people" : "people-outline";
if (route.name === "Home")
iconName = focused ? "home" : "home-outline";
else if (route.name === "Profile")
iconName = focused ? "person" : "person-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 = activeTabColors[route.name];
const activeBackgroundColor = theme.colors.background;
const activeIconColor = activeIconColors[route.name];
return (
<View
style={[
styles.iconContainer,
// 4. Apply the dynamic background color when focused
focused && [
styles.activeIconContainer,
{ backgroundColor: activeBackgroundColor },
],
]}
>
(route.name === "Management"{<Ionicons
<Ionicons
name={iconName}
size={TAB_CONFIG.iconSize}
color={focused ? activeIconColor : TAB_CONFIG.inactiveColor}
/>}:null)
/>
</View>
);
},
@@ -119,7 +145,7 @@ const BottomTabs: React.FC = () => {
<Tab.Screen name="Management" component={ManagementScreen} />
</Tab.Navigator>
);
}
};
const styles = StyleSheet.create({
screen: {
@@ -135,19 +161,16 @@ const styles = StyleSheet.create({
},
tabBar: {
position: "absolute",
bottom: 20,
left: 20,
right: 20,
height: TAB_CONFIG.height,
// backgroundColor: TAB_CONFIG.backgroundColor,
borderRadius: 20,
shadowColor: "#000",
shadowOpacity: 0.1,
shadowOffset: { width: 0, height: 5 },
shadowRadius: 8,
elevation: 10,
paddingBottom: Platform.OS === "ios" ? 25 : 10,
paddingTop: 10,
paddingTop: 0,
marginHorizontal: 10,
},
label: {
@@ -155,19 +178,18 @@ const styles = StyleSheet.create({
fontSize: responsiveFontSize(1.2),
},
iconContainer: {
width: 60,
height: 50,
justifyContent: "center",
alignItems: "center",
width: 80, // Reduced size for a sleeker look
height: 60, // Reduced size
marginTop: 10,
borderRadius: 15,
},
activeIconContainer: {
// The default active style, background color is now applied dynamically
elevation: 2,
shadowColor: '#000',
shadowColor: "#000",
shadowOpacity: 0.1,
shadowRadius: 5,
},
});
export default BottomTabs
export default BottomTabs;
+72 -59
View File
@@ -22,14 +22,24 @@ import Ionicons from '@react-native-vector-icons/ionicons';
import MaterialRipple from 'react-native-material-ripple';
import CustomAlert from '../components/CostomAlart';
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 BLOCK_SIZE = width / 3;
// Note: You will need to install react-native-material-ripple
// npm install react-native-material-ripple
export default function LoginScreen() {
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();
const [mobile, setMobile] = useState('');
@@ -42,9 +52,12 @@ export default function LoginScreen() {
alertType: 'info' as 'info' | 'success' | 'error',
});
const fade = useRef(new Animated.Value(0)).current;
const slide = useRef(new Animated.Value(30)).current;
const ctaScale = useRef(new Animated.Value(1)).current;
const { theme } = useTheme();
useEffect(() => {
Animated.parallel([
@@ -53,6 +66,7 @@ export default function LoginScreen() {
]).start();
}, []);
const showAlert = (
title: string,
message: string,
@@ -62,10 +76,12 @@ export default function LoginScreen() {
setAlertInfo({ visible: true, title, message, buttons, alertType });
};
const hideAlert = () => {
setAlertInfo({ ...alertInfo, visible: false });
};
const handleContinue = async () => {
if (mobile.length === 10) {
setLoading(true);
@@ -87,21 +103,24 @@ export default function LoginScreen() {
}
};
const colorsBlocks = [
colors.pastelPink,
colors.pastelBlue,
colors.pastelGreen,
colors.pastelOrange,
colors.pastelBlue,
colors.pastelPink,
colors.pastelGreen,
colors.pastelOrange,
colors.pastelPink,
theme.colors.logincard,
theme.colors.logincard1,
theme.colors.logincard1,
theme.colors.logincard,
theme.colors.logincard,
theme.colors.logincard1,
theme.colors.logincard1,
theme.colors.logincard,
theme.colors.logincard,
];
return (
<View style={styles.container}>
<StatusBar barStyle="dark-content" backgroundColor={colors.pastelOrange} />
<View style={[ styles.container, {backgroundColor: theme.colors.background }]}>
<StatusBar barStyle={theme.mode === "dark" ? "light-content" : "dark-content"} backgroundColor={colors.pastelOrange} />
{/* Funky grid blocks background (Unchanged) */}
<View style={styles.gridContainer}>
@@ -110,35 +129,38 @@ export default function LoginScreen() {
key={index}
style={[
styles.gridBlock,
{ backgroundColor: bgColor },
{ backgroundColor: bgColor ,borderRadius:16 },
index % 2 === 0 ? styles.elevatedBlock : {},
]}
/>
))}
</View>
<KeyboardAvoidingView
style={styles.keyboardAvoidingContainer}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<View style={styles.contentContainer}>
<View style={styles.topSection}>
<View style={[ styles.topSection, {marginTop:60}]}>
<Animated.View style={{ opacity: fade, transform: [{ translateY: slide }] }}>
<Text style={styles.brand}>CircleCollect</Text>
<Text style={styles.title}>Welcome Back!</Text>
<Text style={styles.subtitle}>
<Text style={[styles.brand, { color: theme.colors.textPrimary }]}>CircleCollect</Text>
<Text style={[styles.title, { color: theme.colors.textPrimary }]}>Welcome Back!</Text>
<Text style={[styles.subtitle, { color: theme.colors.textSecondary }]}>
Let's get you signed in to your community.
</Text>
</Animated.View>
</View>
<Animated.View style={{ opacity: fade, transform: [{ translateY: slide }] }}>
<View style={styles.inputWrapper}>
<Ionicons name="call-outline" size={24} color={colors.textLight} style={styles.inputIcon} />
<Animated.View style={{ opacity: fade, transform: [{ translateY: slide }] ,marginTop: 150}}>
<View style={[styles.inputWrapper, { backgroundColor: theme.colors.reverseBackground }]}>
<Ionicons name="call-outline" size={24} color={theme.colors.reverseText} style={styles.inputIcon} />
<TextInput
style={styles.input}
style={[ styles.input, { color: theme.colors.reverseText, fontFamily: FontsFamily.nunitoRegular }]}
placeholder="Enter your mobile number"
placeholderTextColor={colors.textLight}
placeholderTextColor={theme.colors.reverseText}
value={mobile}
onChangeText={setMobile}
keyboardType="phone-pad"
@@ -148,6 +170,7 @@ export default function LoginScreen() {
</View>
</Animated.View>
<Animated.View style={[styles.bottomSection, { transform: [{ scale: ctaScale }] }]}>
<MaterialRipple
onPress={handleContinue}
@@ -157,21 +180,16 @@ export default function LoginScreen() {
rippleCentered
rippleContainerBorderRadius={30}
>
<LinearGradient
colors={[colors.pastelPink, colors.pastelOrange]}
style={styles.buttonGradient}
start={{x: 0, y: 0}}
end={{x: 1, y: 1}}
>
<View style={[styles.buttonGradient, { backgroundColor: theme.colors.reverseBackground }]} >
{loading ? (
<ActivityIndicator color={colors.white} />
<ActivityIndicator color={theme.colors.reverseText} />
) : (
<View style={styles.buttonContent}>
<Text style={styles.buttonText}>Continue</Text>
<Ionicons name="arrow-forward-circle-outline" size={22} color={colors.white} style={{marginLeft: 8}} />
<Text style={[styles.buttonText, { color: theme.colors.reverseText }]}>Continue</Text>
<Ionicons name="arrow-forward-circle-outline" size={22} color={theme.colors.reverseText} style={{marginLeft: 8}} />
</View>
)}
</LinearGradient>
</View>
</MaterialRipple>
<Text style={styles.footerText}>
Your privacy is our priority. Your information is always secure.
@@ -180,6 +198,7 @@ export default function LoginScreen() {
</View>
</KeyboardAvoidingView>
<CustomAlert
visible={alertInfo.visible}
title={alertInfo.title}
@@ -191,6 +210,7 @@ export default function LoginScreen() {
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
@@ -207,11 +227,11 @@ const styles = StyleSheet.create({
borderRadius: 16,
},
elevatedBlock: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 5 },
shadowOpacity: 0.15,
shadowRadius: 10,
elevation: 7,
shadowColor: '#ffffffff',
shadowOffset: { width: 1, height: 5 },
shadowOpacity: 1,
shadowRadius: 15,
elevation: 10,
},
keyboardAvoidingContainer: {
flex: 1,
@@ -227,8 +247,7 @@ const styles = StyleSheet.create({
alignItems: 'center',
},
brand: {
fontSize: responsiveFontSize(2.8),
fontWeight: '700',
fontSize: responsiveFontSize(3),
color: colors.primary,
textAlign: 'center',
letterSpacing: 2,
@@ -236,33 +255,29 @@ const styles = StyleSheet.create({
textShadowColor: 'rgba(0, 0, 0, 0.1)',
textShadowOffset: { width: 1, height: 1 },
textShadowRadius: 2,
fontFamily: FontsFamily.nunitoExtraBold,
},
title: {
fontSize: responsiveFontSize(4),
fontWeight: 'bold',
color: colors.textDark,
fontSize: responsiveFontSize(5),
fontFamily: FontsFamily.nunitoBold,
textAlign: 'center',
marginBottom: 14,
},
subtitle: {
fontSize: responsiveFontSize(1.6),
color: colors.textDark,
fontFamily: FontsFamily.nunitoRegular,
textAlign: 'center',
lineHeight: 26,
maxWidth: 320,
},
inputWrapper: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'rgba(255, 255, 255, 0.85)',
borderRadius: 36,
borderWidth: 2,
borderColor: colors.pastelPink,
paddingHorizontal: 20,
shadowColor: colors.primary,
shadowOffset: { width: 0, height: 6 },
shadowOpacity: 0.2,
shadowRadius: 14,
paddingHorizontal: responsiveHeight(2),
elevation: 8,
},
inputIcon: {
@@ -271,9 +286,9 @@ const styles = StyleSheet.create({
input: {
flex: 1,
fontSize: responsiveFontSize(1.8),
fontWeight: '600',
fontFamily: FontsFamily.nunitoSemiBold,
color: colors.textLight,
height: 58,
height: responsiveHeight(5),
},
bottomSection: {
alignItems: 'center',
@@ -282,16 +297,13 @@ const styles = StyleSheet.create({
button: {
width: '100%',
borderRadius: 30,
shadowColor: colors.primary,
shadowOffset: { width: 0, height: 10 },
shadowOpacity: 0.4,
shadowRadius: 20,
elevation: 14,
marginTop: 20,
},
buttonGradient: {
borderRadius: 30,
paddingVertical: 18,
borderRadius:42,
paddingVertical: 14,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
@@ -302,8 +314,8 @@ const styles = StyleSheet.create({
},
buttonText: {
color: colors.white,
fontWeight: '800',
fontSize: responsiveFontSize(1.8),
fontFamily: FontsFamily.nunitoBold,
},
footerText: {
fontSize: responsiveFontSize(1.4),
@@ -311,5 +323,6 @@ const styles = StyleSheet.create({
marginTop: 28,
textAlign: 'center',
maxWidth: 300,
fontFamily: FontsFamily.nunitoRegular,
},
});
+67 -37
View File
@@ -21,6 +21,8 @@ import { LOGIN_URL, VARIFY_OTP_URL } from "../utils/ApiUrls";
import colors from "../components/colors";
import CustomAlert from "../components/CostomAlart";
import { getFcmToken } from "../utils/firebase/firebaseServices";
import { useTheme } from "../theme/ThemeProvider";
import FontsFamily from "../constents/FontsFamily";
const CELL_COUNT = 6;
const { width } = Dimensions.get('window');
@@ -50,6 +52,7 @@ export default function OtpVerificationScreen() {
buttons: [],
alertType: "info",
});
const { theme } = useTheme();
const inputs = useRef<Array<TextInput | null>>([]);
const route = useRoute<OtpVerifyRouteProp>();
@@ -239,20 +242,20 @@ export default function OtpVerificationScreen() {
};
const colorsBlocks = [
colors.pastelPink,
colors.pastelBlue,
colors.pastelGreen,
colors.pastelOrange,
colors.pastelBlue,
colors.pastelPink,
colors.pastelGreen,
colors.pastelOrange,
colors.pastelPink,
theme.colors.logincard,
theme.colors.logincard1,
theme.colors.logincard1,
theme.colors.logincard,
theme.colors.logincard,
theme.colors.logincard1,
theme.colors.logincard1,
theme.colors.logincard,
theme.colors.logincard,
];
return (
<View style={styles.container}>
<StatusBar barStyle="dark-content" backgroundColor={colors.pastelOrange} />
<View style={[styles.container, {backgroundColor: theme.colors.background }]}>
<StatusBar barStyle={theme.mode === "dark" ? "light-content" : "dark-content"} backgroundColor={colors.pastelOrange} />
<View style={styles.gridContainer}>
{colorsBlocks.map((bgColor, index) => (
@@ -260,7 +263,7 @@ export default function OtpVerificationScreen() {
key={index}
style={[
styles.gridBlock,
{ backgroundColor: bgColor },
{ backgroundColor: bgColor, borderRadius: 16 },
index % 2 === 0 ? styles.elevatedBlock : {},
]}
/>
@@ -268,26 +271,34 @@ export default function OtpVerificationScreen() {
</View>
<KeyboardAvoidingView style={styles.keyboardAvoidingContainer} behavior={Platform.OS === "ios" ? "padding" : undefined}>
<CustomAlert {...alertConfig} />
<View style={styles.contentPanel}>
<View style={[styles.contentPanel, { backgroundColor: theme.colors.reverseBackground }]}>
<View style={styles.topSection}>
<Text style={styles.title}>Verify Your Code</Text>
<Text style={styles.subtitle}>We’ve sent a 6-digit verification code to +91 {mobile}.</Text>
<Text style={[styles.title, { color: theme.colors.reverseText }]}>Verify Your Code</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) => (
<TextInput
key={`otp-${i}`}
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"
maxLength={i === 0 ? CELL_COUNT : 1}
value={code[i]}
onChangeText={(t) => (i === 0 ? handleFirstCellChange(t) : updateIndex(i, t))}
onKeyPress={(e) => handleKeyPress(i, e)}
placeholder="•"
placeholder="*"
placeholderTextColor="#BDBDBD"
textContentType="oneTimeCode"
/>
@@ -300,16 +311,16 @@ export default function OtpVerificationScreen() {
onPressIn={() => Animated.spring(ctaScale, { toValue: 0.97, useNativeDriver: true }).start()}
onPressOut={() => Animated.spring(ctaScale, { toValue: 1, useNativeDriver: true }).start()}
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>
{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}>
<Text style={[styles.footerText, { color: colors.pastelPink, fontWeight: '600' }]}>
<Text style={[styles.footerText, { color: theme.colors.reverseText, fontWeight: '600' }]}>
{isResending ? "Resending..." : "Resend Code"}
</Text>
</Pressable>
@@ -335,16 +346,15 @@ const styles = StyleSheet.create({
borderRadius: 16,
},
elevatedBlock: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 5 },
shadowOpacity: 0.15,
shadowRadius: 10,
elevation: 7,
shadowColor: '#ffffffff',
shadowOffset: { width: 1, height: 5 },
shadowOpacity: 1,
shadowRadius: 15,
elevation: 10,
},
keyboardAvoidingContainer: { flex: 1, justifyContent: 'center' },
contentPanel: {
marginHorizontal: 24,
backgroundColor: 'rgba(255, 255, 255, 0.95)',
borderRadius: 32,
padding: 28,
shadowColor: '#000',
@@ -354,13 +364,33 @@ const styles = StyleSheet.create({
elevation: 12,
},
topSection: { alignItems: 'center', marginBottom: 24 },
title: { fontSize: 28, fontWeight: "bold", color: colors.textDark, textAlign: 'center', marginBottom: 12 },
subtitle: { fontSize: 16, color: colors.textLight, textAlign: 'center', marginBottom: 32, lineHeight: 24, maxWidth: 300 },
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 },
cellFilled: { borderColor: colors.primary, backgroundColor: '#F7F2FF' },
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 ,fontFamily: FontsFamily.nunitoMedium},
otpRow: {
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 },
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},
buttonText: { color: colors.white, fontWeight: "700", fontSize: 16 },
footerText: { textAlign: "center", color: colors.textLight, fontSize: 14, marginTop: 20 },
button: { backgroundColor: colors.pastelPink, paddingVertical: 18, borderRadius: 30, alignItems: "center", justifyContent: "center",},
buttonText: { color: colors.white, fontSize: 16, fontFamily: FontsFamily.nunitoBold },
footerText: { textAlign: "center", fontFamily: FontsFamily.nunitoBold, fontSize: 14, marginTop: 20 },
});
+19 -11
View File
@@ -10,6 +10,7 @@ import {
ListRenderItem,
Alert,
RefreshControl,
StatusBar,
} from "react-native";
import AppView from "../../components/AppView";
import colors from "../../components/colors";
@@ -23,6 +24,10 @@ import AnimatedCard from "../../components/common/AnimatedCard";
import { Ionicons } from "@react-native-vector-icons/ionicons";
import { apiCall } from "../../utils/ApiHelper";
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) {
UIManager.setLayoutAnimationEnabledExperimental(true);
@@ -80,6 +85,8 @@ const Home: React.FC = () => {
const [userDashboard, setUserDashboard] = useState<any>(null);
const [refreshing, setRefreshing] = useState(false);
const [statusData, setStatusData] = useState<any>(null);
const insets = useSafeAreaInsets();
const {theme} = useTheme();
const getDashboardData = async () => {
try {
const result = (await apiCall(DASHBOARD_URL, "GET")) as any;
@@ -186,7 +193,8 @@ const Home: React.FC = () => {
};
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
data={actions}
numColumns={2}
@@ -195,18 +203,19 @@ const Home: React.FC = () => {
ListHeaderComponent={
<>
<View style={styles.header}>
<View>
<Text style={styles.greeting}>
Hello, <Text style={{ fontWeight: 'bold' }}>{user.firstName || apiUser?.firstName || "User"}</Text>
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 ,marginTop: responsiveHeight(2) }} >
<Text style={[styles.greeting, { color: theme.colors.textPrimary }]}>
Hello,
</Text>
<Text style={{ fontFamily:FontsFamily.nunitoExtraBold,color: theme.colors.textPrimary,fontSize:responsiveFontSize(2.5) }}>{user.firstName || apiUser?.firstName || "User"}</Text>
</View>
<TouchableOpacity onPress={() => navigation.navigate("Notification" as never)}>
<TouchableOpacity onPress={() => navigation.navigate("Notification" as never)} style={{ marginTop: responsiveHeight(2) ,}}>
{unreadCount > 0 && (
<View style={styles.badge}>
<Text style={styles.badgeText}>{unreadCount}</Text>
</View>
)}
<Ionicons name="notifications-outline" size={30} color={colors.textDark} />
<Ionicons name="notifications-outline" size={30} color={theme.colors.textPrimary} />
</TouchableOpacity>
</View>
@@ -217,7 +226,7 @@ const Home: React.FC = () => {
/>
<View style={styles.actionHeader}>
<Text style={styles.sectionTitle}>Quick Actions</Text>
<Text style={[styles.sectionTitle,{ color: theme.colors.textPrimary }]}>Quick Actions </Text>
</View>
</>
@@ -243,7 +252,7 @@ const Home: React.FC = () => {
onClose={() => setAlertVisible(false)}
/>
)}
</AppView>
</View>
);
};
@@ -252,7 +261,6 @@ export default Home;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.white,
},
header: {
flexDirection: "row",
@@ -262,8 +270,8 @@ const styles = StyleSheet.create({
paddingTop: 24,
paddingBottom: 16,
},
greeting: { fontSize: 22, color: colors.textDark },
sectionTitle: { fontSize: 20, fontWeight: "600", color: colors.textDark, flex: 1 },
greeting: { fontSize: 22,fontFamily: FontsFamily.nunitoLight },
sectionTitle: { fontSize: 20, fontWeight: "600", color: colors.textDark, flex: 1 ,fontFamily: FontsFamily.nunitoSemiBold},
actionHeader: {
flexDirection: "row",
justifyContent: "space-between",
+84 -98
View File
@@ -21,6 +21,8 @@ import {
responsiveWidth,
responsiveFontSize
} from "react-native-responsive-dimensions";
import { useTheme } from "../../theme/ThemeProvider";
import FontsFamily from "../../constents/FontsFamily";
type StatusData = {
role?: string;
@@ -73,7 +75,7 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
const [showPayDialog, setShowPayDialog] = useState(false);
const [selectedGroupIds, setSelectedGroupIds] = useState<string[]>([]);
const [submitting, setSubmitting] = useState(false);
const {theme} = useTheme();
// console.log("user me : ", userDashboard)
// Preselect all due groups at mount and on change
useEffect(() => {
@@ -92,12 +94,12 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
// Top grid info items
const infoItems = useMemo(
() => [
{ icon: <Ionicons name="person-outline" size={20} color={colors.primary} />, 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="book-outline" size={20} color={colors.primary} />, 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="document-text-outline" size={20} color={colors.primary} />, 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="person-outline" size={20} color={theme.colors.black} />, label: "Role", value: statusData?.role || "-" },
{ 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={theme.colors.black} />, label: "Joined Groups", value: userDashboard?.myJoinedGroups?.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={theme.colors.black} />, label: "Total Due", value: `₹${userDashboard?.multiplePayAmount || 0}` },
{ icon: <Ionicons name="checkmark-done-outline" size={20} color={theme.colors.black} />, label: "Completed", value: userDashboard?.completeStatusMyGroupCount?.toString() || "0" },
],
[userDashboard, statusData]
);
@@ -153,18 +155,18 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
// Bottom bar
const renderDueBar = () => (
<View style={styles.dueBar}>
<Text style={styles.dueBarText}>
Total Due: <Text style={{ color: "#FFDE03", fontWeight: "bold" }}>₹{totalDue}</Text>
<View style={[ styles.dueBar, { borderTopColor: theme.colors.border, backgroundColor: theme.colors.gray }]}>
<Text style={[styles.dueBarText, { color: theme.colors.black }]}>
Total Due: <Text style={{ color: theme.colors.black, fontWeight: "bold" }}>₹{totalDue}</Text>
</Text>
<TouchableOpacity
style={styles.payAllButton}
style={[styles.payAllButton, { backgroundColor: theme.colors.black }]}
onPress={() => setShowPayDialog(true)}
disabled={dueGroups.length === 0}
activeOpacity={0.8}
>
<Ionicons name="wallet-outline" size={20} color="#000000ff" />
<Text style={styles.payAllButtonText}>Pay All</Text>
<Ionicons name="wallet-outline" size={20} color={theme.colors.white} />
<Text style={[styles.payAllButtonText, { color: theme.colors.white }]}>Pay All</Text>
</TouchableOpacity>
</View>
);
@@ -173,17 +175,14 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
const renderPayDialog = () => (
<Modal visible={showPayDialog} transparent animationType="fade" onRequestClose={() => setShowPayDialog(false)}>
<View style={styles.payModalOverlay}>
<LinearGradient
colors={[colors.white, colors.white]}
start={{ x: 0.2, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.payModalBox}
<View
style={[styles.payModalBox, { backgroundColor: theme.colors.reverseBackground }]}
>
<View style={styles.payDialogHeader}>
<Ionicons name="card-outline" color="#000000ff" size={22} />
<Text style={styles.payDialogTitle}>Select Groups to Pay</Text>
<Ionicons name="card-outline" color={theme.colors.reverseText} size={22} />
<Text style={[styles.payDialogTitle, { color: theme.colors.reverseText }]}>Select Groups to Pay</Text>
<TouchableOpacity onPress={() => setShowPayDialog(false)}>
<Ionicons name="close-circle" color="#000000ff" size={24} />
<Ionicons name="close-circle" color={theme.colors.reverseText} size={24} />
</TouchableOpacity>
</View>
<ScrollView
@@ -195,68 +194,71 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
key={group.groupId}
style={[
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)}
activeOpacity={0.85}
>
<Ionicons
name={selectedGroupIds.includes(group.groupId) ? "checkbox" : "square-outline"}
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 }}>
<Text style={[styles.payGroupName, { color: "#000000ff" }]}>{group.groupName}</Text>
<Text style={styles.payDueText}>
<Text style={[styles.payGroupName, {color: theme.colors.reverseText}]}>{group.groupName}</Text>
<Text style={[styles.payDueText, {color: theme.colors.reverseText}]}>
₹{group.baseAmount}
{group.lateFee > 0 ? ` (Late: ₹${group.lateFee})` : ""}
</Text>
</View>
<Text style={[styles.payAmount, { color: "#4CAF50" }]}>₹{group.totalDue}</Text>
{/* <Text style={[styles.payAmount, { color: "#4CAF50" }]}>₹{group.totalDue}</Text> */}
</TouchableOpacity>
))}
</ScrollView>
<View style={styles.payDialogFooter}>
<Text style={styles.payDialogTotal}>
Total: <Text style={{ fontWeight: "bold", color: "#ff5f03ff" }}>₹{totalDueSelected}</Text>
<Text style={[styles.payDialogTotal, { color: theme.colors.reverseText }]}>
Total : <Text style={{ fontFamily: FontsFamily.nunitoBold, color: theme.colors.red }}>₹{totalDueSelected}</Text>
</Text>
<TouchableOpacity
style={[styles.payConfirmButton, { opacity: submitting ? 0.7 : 1 }]}
style={[styles.payConfirmButton, { opacity: submitting ? 0.7 : 1 , backgroundColor: theme.colors.background}]}
onPress={handlePayAll}
disabled={submitting || selectedGroupIds.length === 0}
activeOpacity={0.8}
>
{submitting ? (
<ActivityIndicator color="#fff" />
<ActivityIndicator color={theme.colors.textPrimary} />
) : (
<>
<Ionicons name="checkmark-circle" size={22} color="#ffffffff" />
<Text style={styles.payButtonText}>Pay Selected</Text>
<Ionicons name="checkmark-circle" size={22} color={theme.colors.textPrimary} />
<Text style={[styles.payButtonText, { fontFamily: FontsFamily.nunitoSemiBold ,color: theme.colors.textPrimary}]}>Pay Selected</Text>
</>
)}
</TouchableOpacity>
</View>
</LinearGradient>
</View>
</View>
</Modal>
);
return (
<View style={styles.container}>
<LinearGradient
colors={[colors.primary, colors.pastelPink]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.card}
<View
style={[styles.card, { backgroundColor: theme.colors.reverseBackground }]}
>
<View style={styles.headerSection}>
<View style={styles.titleWithIcon}>
<Ionicons name="stats-chart" size={24} color="#fff" style={styles.headerIcon} />
<Text style={styles.header}>Profile Overview</Text>
<Ionicons name="stats-chart" size={24} color={theme.colors.reverseText} style={styles.headerIcon} />
<Text style={[styles.header, { color: theme.colors.reverseText} ]}>Profile Overview</Text>
</View>
<View style={styles.statusBadge}>
<Ionicons name="shield-checkmark" size={16} color="#fff" />
<Text style={styles.statusBadgeText}>
<View style={[styles.statusBadge, { backgroundColor: theme.colors.background}]}>
<Ionicons name="shield-checkmark" size={16} color={theme.colors.textPrimary} />
<Text style={[styles.statusBadgeText, { color: theme.colors.textPrimary }]}>
{statusData?.profileStatus ? "Verified" : "Pending"}
</Text>
</View>
@@ -265,16 +267,16 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
<View style={styles.leftSection}>
<View style={styles.infoGrid}>
{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.iconContainer}>
<View style={[styles.iconContainer, {backgroundColor: theme.colors.optCard || theme.colors.card}]}>
{item.icon}
</View>
<Text style={styles.infoLabel} >
<Text style={[styles.infoLabel,{color: theme.colors.textPrimary}]} >
{item.label}
</Text>
</View>
<Text style={[styles.infoValue, { color: getValueColor(item.value) }]} numberOfLines={1}>
<Text style={[styles.infoValue, { color: theme.colors.textPrimary }]} numberOfLines={1}>
{item.value}
</Text>
</View>
@@ -296,16 +298,16 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
</View>
)}
</View>
<View style={styles.infoCardOne}>
<Text style={{ color: "#fff", fontSize: responsiveWidth(2.3)}}>Total Paid Amount <Text style={{ fontWeight: "900"}}>{userDashboard?.totalPaidAmountMonth}</Text></Text>
{/* <Text style={{ color: "#fff", fontWeight: "400"}}>Total Paid Amount {userDashboard?.totalPaidAmountMonth}</Text> */}
<View style={[styles.infoCardOne,{backgroundColor: theme.colors.background}]}>
<Text style={{ color: theme.colors.textPrimary, fontSize: responsiveWidth(3), fontFamily: FontsFamily.nunitoSemiBold,}}>Total Paid Amount</Text>
<Text style={{ color: theme.colors.textPrimary, fontWeight: "400", fontFamily: FontsFamily.nunitoRegular}}>{userDashboard?.totalPaidAmountMonth}</Text>
</View>
</TouchableOpacity>
</View>
</View>
{renderDueBar()}
</LinearGradient>
</View>
{renderPayDialog()}
@@ -377,8 +379,8 @@ const styles = StyleSheet.create({
marginRight: 8
},
header: {
fontSize: responsiveWidth(3.5),
fontWeight: "700",
fontSize: responsiveWidth(4.5),
fontFamily: FontsFamily.nunitoBold,
color: "#fff"
},
statusBadge: {
@@ -411,14 +413,11 @@ const styles = StyleSheet.create({
gap: 10
},
infoCard: {
backgroundColor: 'rgba(255,255,255,0.15)',
borderRadius: 14,
padding: 12,
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.2)',
flex: 1,
minWidth: '45%',
marginBottom: 8,
borderRadius:12
},
infoCardOne: {
backgroundColor: 'rgba(255,255,255,0.15)',
@@ -436,25 +435,22 @@ const styles = StyleSheet.create({
marginBottom: 6
},
iconContainer: {
width: 28,
height: 28,
backgroundColor: '#fff',
borderRadius: 8,
width: responsiveWidth(8),
height: responsiveHeight(3.5),
borderRadius: responsiveWidth(2),
justifyContent: 'center',
alignItems: 'center',
marginRight: 8,
},
infoLabel: {
color: "rgba(255,255,255,0.9)",
fontSize: responsiveWidth(2),
fontWeight: '500',
fontSize: responsiveWidth(3),
fontFamily: FontsFamily.nunitoSemiBold,
flexShrink: 1,
},
infoValue: {
fontWeight: "700",
fontSize: responsiveWidth(2.5),
fontFamily: FontsFamily.nunitoBold,
fontSize: responsiveWidth(3),
textAlign: 'left',
color: "#fff"
},
profileSection: {
width: 90,
@@ -498,16 +494,13 @@ const styles = StyleSheet.create({
alignItems: "center",
padding: 16,
borderTopWidth: 1,
borderColor: "rgba(255,255,255,0.15)",
backgroundColor: "rgba(0,0,0,0.15)",
},
dueBarText: {
color: "#fff",
fontSize: responsiveWidth(2.5),
fontWeight: "600"
fontFamily: FontsFamily.nunitoExtraBold,
fontSize: responsiveWidth(4),
},
payAllButton: {
backgroundColor: "#ffffffff",
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 16,
@@ -516,8 +509,7 @@ const styles = StyleSheet.create({
gap: 6,
},
payAllButtonText: {
color: "#000000ff",
fontWeight: "bold",
fontFamily: FontsFamily.nunitoBold,
fontSize: responsiveWidth(2.5)
},
payModalOverlay: {
@@ -527,7 +519,7 @@ const styles = StyleSheet.create({
alignItems: "center",
},
payModalBox: {
width: "92%",
width: "90%",
borderRadius: 24,
overflow: "hidden",
alignItems: "center",
@@ -547,9 +539,10 @@ const styles = StyleSheet.create({
borderColor: "rgba(255,255,255,0.1)",
},
payDialogTitle: {
color: "#000000ff",
fontSize: responsiveWidth(1.8),
fontWeight: "700"
fontSize: responsiveWidth(4),
fontFamily: FontsFamily.nunitoBold,
},
payGroupOption: {
flexDirection: "row",
@@ -557,24 +550,20 @@ const styles = StyleSheet.create({
gap: 16,
paddingVertical: 12,
paddingHorizontal: 14,
borderColor: colors.primary,
borderWidth: 1,
borderRadius: 12,
marginBottom: 8,
},
payOptionSelected: {
backgroundColor: "rgba(166, 49, 255, 0.2)",
borderColor: colors.primary,
borderWidth: 1,
},
payGroupName: {
color: "#000000ff",
fontWeight: "700",
fontSize: responsiveWidth(2.5)
fontFamily: FontsFamily.nunitoSemiBold,
fontSize: responsiveWidth(3)
},
payDueText: {
color: "rgba(0, 0, 0, 0.8)",
fontSize: responsiveWidth(1.3),
fontSize: responsiveWidth(3),
fontFamily: FontsFamily.nunitoRegular,
marginTop: 2
},
payAmount: {
@@ -591,26 +580,23 @@ const styles = StyleSheet.create({
paddingHorizontal: 18,
},
payDialogTotal: {
color: "#000000ff",
fontWeight: "600",
fontSize: responsiveWidth(1.7)
fontFamily: FontsFamily.nunitoBold,
fontSize: responsiveWidth(3.5)
},
payConfirmButton: {
flexDirection: "row",
alignItems: "center",
backgroundColor: colors.primary,
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 20,
paddingVertical: 8,
paddingHorizontal: 18,
borderRadius: 12,
elevation: 4,
borderColor: colors.primary,
borderWidth: 1,
gap: 8,
},
payButtonText: {
color: "#ffffffff",
fontWeight: "bold",
fontSize: responsiveWidth(2.5)
fontSize: responsiveWidth(3),
},
modalOverlay: {
flex: 1,
+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'
},
};