diff --git a/App.tsx b/App.tsx
index 30d539a..c9244e1 100644
--- a/App.tsx
+++ b/App.tsx
@@ -20,7 +20,7 @@ import { ThemeProvider } from './src/theme/ThemeProvider';
LogBox.ignoreAllLogs(true);
-const queryClient = new QueryClient({
+export const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 2,
@@ -29,8 +29,9 @@ const queryClient = new QueryClient({
},
});
-function App() {
+import { GestureHandlerRootView } from 'react-native-gesture-handler';
+function App() {
useEffect(() => {
requestPermission();
@@ -53,18 +54,18 @@ function App() {
};
}, []);
-
-
return (
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index e1a825a..b8852ba 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -19,6 +19,7 @@
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
+ android:screenOrientation="portrait"
android:exported="true">
diff --git a/package.json b/package.json
index 78755c5..b1d43e8 100644
--- a/package.json
+++ b/package.json
@@ -41,6 +41,7 @@
"react-native-dialog": "^9.3.0",
"react-native-elements": "^3.4.3",
"react-native-fast-confetti": "^1.1.1",
+ "react-native-gesture-handler": "^2.28.0",
"react-native-image-picker": "^8.2.1",
"react-native-inset-shadow": "^1.0.3",
"react-native-keychain": "^10.0.0",
diff --git a/src/screens/notification/Notification.tsx b/src/screens/notification/Notification.tsx
index 54f9c09..fcf81fc 100644
--- a/src/screens/notification/Notification.tsx
+++ b/src/screens/notification/Notification.tsx
@@ -1,14 +1,16 @@
-import React from 'react';
+import React, { useState } from 'react';
import {
View,
Text,
TouchableOpacity,
FlatList,
StyleSheet,
+ Alert,
+ Dimensions,
} from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
-import { removeNotification } from '../../redux/notificationSlice';
+import { removeNotification, clearAllNotifications, markAllAsRead } from '../../redux/notificationSlice';
import LinearGradient from 'react-native-linear-gradient';
import LucideIcon from '../../components/common/LucideIcon';
@@ -16,13 +18,18 @@ import AppView from '../../components/AppView';
import Header from '../../components/common/Header';
import { useNotificationHandler } from '../hook/useNotificationHandler';
import { useTheme } from '../../theme/ThemeProvider';
-
import Swipeable from 'react-native-gesture-handler/ReanimatedSwipeable';
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
+ interpolate,
+ Extrapolate,
+ SharedValue,
} from 'react-native-reanimated';
+import FontsFamily from '../../constents/FontsFamily';
+
+const { width } = Dimensions.get('window');
type FilterType =
| 'all'
@@ -52,6 +59,28 @@ const Notification = () => {
dispatch(removeNotification(messageId));
};
+ const handleClearAll = () => {
+ if (notifications.length === 0) return;
+
+ Alert.alert(
+ 'Clear All',
+ 'Are you sure you want to delete all notifications?',
+ [
+ { text: 'Cancel', style: 'cancel' },
+ {
+ text: 'Clear All',
+ style: 'destructive',
+ onPress: () => dispatch(clearAllNotifications())
+ },
+ ]
+ );
+ };
+
+ const handleMarkAllRead = () => {
+ if (notifications.length === 0) return;
+ dispatch(markAllAsRead());
+ };
+
const handleMarkAsRead = (
messageId: string,
screen: string,
@@ -72,36 +101,47 @@ const Notification = () => {
const formatTime = (timestamp: number) => {
const date = new Date(timestamp);
const now = new Date();
- const diff = (now.getTime() - date.getTime()) / (1000 * 60 * 60);
+ const diff = (now.getTime() - date.getTime()) / (1000 * 60);
if (diff < 1) return 'Just now';
- if (diff < 24) return `${Math.floor(diff)}h ago`;
- return date.toLocaleDateString();
+ if (diff < 60) return `${Math.floor(diff)}m ago`;
+ if (diff < 1440) return `${Math.floor(diff / 60)}h ago`;
+
+ const dayDiff = Math.floor(diff / 1440);
+ if (dayDiff === 1) return 'Yesterday';
+ if (dayDiff < 7) return `${dayDiff}d ago`;
+
+ return date.toLocaleDateString('en-IN', { day: '2-digit', month: 'short' });
};
const getNotificationIcon = (type: string) => {
const map: any = {
JOIN_REQUEST: 'person-add-outline',
CONTRIBUTION: 'cash-outline',
- CONTRIBUTION_ACK: 'checkmark-done-outline',
- CYCLE_COMPLETED: 'trophy-outline',
- WITHDRAWAL: 'card-outline',
+ CONTRIBUTION_ACK: 'checkmark-circle-outline',
+ CYCLE_COMPLETED: 'trophy',
+ WITHDRAWAL: 'wallet-outline',
WITHDRAWAL_NOTIFY: 'notifications-outline',
- GROUP_STARTED: 'rocket-outline',
+ GROUP_STARTED: 'sparkles-outline',
+ JOIN_APPROVED: 'checkmark-circle-outline',
+ JOIN_VIA_INVITE: 'mail-outline',
};
return map[type] || 'notifications-outline';
};
const getNotificationColor = (type: string) => {
const map: any = {
- JOIN_REQUEST: '#FFB6C1',
- CONTRIBUTION: '#98FB98',
- CONTRIBUTION_ACK: '#ADD8E6',
- CYCLE_COMPLETED: '#D8BFD8',
- WITHDRAWAL: '#FFD700',
- WITHDRAWAL_NOTIFY: '#FFA07A',
+ JOIN_REQUEST: '#FF6B6B', // Vibrant Red
+ CONTRIBUTION: '#4ECDC4', // Modern Teal
+ CONTRIBUTION_ACK: '#45B7D1', // Sky Blue
+ CYCLE_COMPLETED: '#FFD93D', // Golden Yellow
+ WITHDRAWAL: '#6C5CE7', // Deep Purple
+ WITHDRAWAL_NOTIFY: '#FF8066', // Coral
+ GROUP_STARTED: '#A8E6CF', // Mint Green
+ JOIN_APPROVED: '#32D74B', // iOS Green
+ JOIN_VIA_INVITE: '#007AFF', // iOS Blue
};
- return map[type] || '#D3D3D3';
+ return map[type] || '#8E8E93';
};
const getFilterDisplayName = (filter: FilterType) => {
@@ -110,135 +150,162 @@ const Notification = () => {
const unreadCount = notifications.filter((i: any) => !i.read).length;
- // 🔥 Swipe Delete UI
const renderRightActions = (
messageId: string,
onDeleteAction: (id: string) => void,
- swipeableMethods?: { close?: () => void }
+ swipeableMethods: { close: () => void },
+ drag: SharedValue
) => {
+ const styleAnimation = useAnimatedStyle(() => {
+ const translateX = interpolate(
+ drag.value,
+ [-100, 0],
+ [0, 100],
+ Extrapolate.CLAMP
+ );
+ return {
+ transform: [{ translateX }],
+ };
+ });
+
return (
{
onDeleteAction(messageId);
- swipeableMethods?.close?.();
+ swipeableMethods.close();
}}
- style={styles(theme).deleteSwipe}
+ activeOpacity={0.8}
+ style={styles(theme).deleteSwipeContainer}
>
-
- Delete
+
+
+ Delete
+
);
};
-const NotificationItem = ({
- item,
- onDelete,
- onPressNotification,
- theme,
-}: {
- item: any;
- onDelete: (id: string) => void;
- onPressNotification: (
- messageId: string,
- screen: string,
- groupId: string,
- cycleNumber: string,
- contributionId: string
- ) => void;
- theme: any;
-}) => {
- const scale = useSharedValue(1);
- const animatedStyle = useAnimatedStyle(() => ({
- transform: [{ scale: scale.value }],
- }));
+ const NotificationItem = ({
+ item,
+ onDelete,
+ onPressNotification,
+ theme,
+ }: {
+ item: any;
+ onDelete: (id: string) => void;
+ onPressNotification: (
+ messageId: string,
+ screen: string,
+ groupId: string,
+ cycleNumber: string,
+ contributionId: string
+ ) => void;
+ theme: any;
+ }) => {
+ const scale = useSharedValue(1);
+ const animatedStyle = useAnimatedStyle(() => ({
+ transform: [{ scale: scale.value }],
+ }));
- return (
-
- renderRightActions(item.messageId, onDelete, swipeableMethods)
- }
- >
-
- (scale.value = withSpring(0.96))}
- onPressOut={() => (scale.value = withSpring(1))}
- onPress={() =>
- onPressNotification(
- item.messageId,
- item?.data?.msgType,
- item?.data?.groupId,
- item?.data?.cycleNumber,
- item?.data?.contributionId
- )
- }
- >
-
+ renderRightActions(item.messageId, onDelete, swipeableMethods, drag)
+ }
+ >
+
+ (scale.value = withSpring(0.98))}
+ onPressOut={() => (scale.value = withSpring(1))}
+ onPress={() =>
+ onPressNotification(
+ item.messageId,
+ item?.data?.msgType,
+ item?.data?.groupId,
+ item?.data?.cycleNumber,
+ item?.data?.contributionId
+ )
}
- style={[
- styles(theme).card,
- !item.read && styles(theme).unread,
- {
- borderLeftColor: getNotificationColor(
- item.data.msgType
- ),
- },
- ]}
>
-
-
-
-
-
-
-
-
- {item.data.title}
-
-
- {formatTime(item.sentTime)}
-
+
+
+
+
- {item.data.body}
+
+
+
+ {item.data.title}
+
+
+ {formatTime(item.sentTime)}
+
+
-
-
- {getFilterDisplayName(item.data.msgType)}
+
+ {item.data.body}
+
+
+
+
+
+ {getFilterDisplayName(item.data.msgType)}
+
+
+
+ {!item.read && (
+
+ )}
+
-
- {!item.read && }
-
-
-
-
- );
-};
+
+
+
+ );
+ };
const renderItem = ({ item }: any) => (
+ {notifications.length > 0 && (
+
+
+
+ )}
+ {notifications.length > 0 && (
+
+
+
+ )}
+
}
/>
@@ -263,16 +349,19 @@ const NotificationItem = ({
keyExtractor={(item) => item.messageId}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
- contentContainerStyle={styles(theme).container}
+ contentContainerStyle={styles(theme).listContainer}
ListEmptyComponent={
-
-
-
- No notifications
+
+
+
+
+ Stay Tuned!
+
+ We'll notify you when something important happens.
}
@@ -285,117 +374,164 @@ export default Notification;
const styles = (theme: any) =>
StyleSheet.create({
- container: {
- padding: 12,
+ listContainer: {
+ padding: 16,
paddingBottom: 40,
},
-
card: {
- borderRadius: 16,
- padding: 16,
+ backgroundColor: theme.colors.white || '#FFFFFF',
+ borderRadius: 20,
+ padding: 14,
marginBottom: 12,
- backgroundColor: 'rgba(255,255,255,0.95)',
shadowColor: '#000',
- shadowOpacity: 0.08,
- shadowRadius: 10,
- elevation: 4,
- borderLeftWidth: 5,
- },
-
- unread: {
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.05,
+ shadowRadius: 8,
+ elevation: 2,
borderWidth: 1,
- borderColor: '#E0E7FF',
- shadowColor: '#6366F1',
- shadowOpacity: 0.15,
+ borderColor: '#F4F4F5',
},
-
- row: {
+ unreadCard: {
+ borderColor: (theme.colors.primary || '#6366F1') + '20',
+ backgroundColor: (theme.colors.primary || '#6366F1') + '05',
+ },
+ cardContent: {
flexDirection: 'row',
- gap: 12,
+ alignItems: 'flex-start',
},
-
- iconBox: {
- backgroundColor: '#F1F5F9',
- padding: 10,
- borderRadius: 12,
+ iconContainer: {
+ width: 48,
+ height: 48,
+ borderRadius: 14,
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginRight: 12,
},
-
- content: {
+ textContent: {
flex: 1,
},
-
- topRow: {
+ headerRow: {
flexDirection: 'row',
justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 4,
},
-
title: {
- fontWeight: '700',
+ fontFamily: FontsFamily.nunitoBold,
fontSize: 15,
- color: theme.colors.textPrimary,
+ color: theme.colors.textPrimary || '#18181B',
flex: 1,
+ marginRight: 8,
},
-
unreadTitle: {
- color: theme.colors.primary,
+ color: theme.colors.primary || '#6366F1',
},
-
- message: {
- fontSize: 13,
- color: theme.colors.textSecondary,
- marginTop: 4,
- },
-
time: {
+ fontFamily: FontsFamily.nunitoRegular,
fontSize: 11,
- color: '#6B7280',
+ color: '#71717A',
},
-
- tagWrap: {
- marginTop: 6,
+ bodyText: {
+ fontFamily: FontsFamily.nunitoRegular,
+ fontSize: 13,
+ color: theme.colors.textSecondary || '#52525B',
+ lineHeight: 18,
+ marginBottom: 8,
},
-
- tag: {
- fontSize: 10,
- color: '#fff',
- paddingHorizontal: 10,
+ footerRow: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ badge: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingHorizontal: 8,
paddingVertical: 4,
- borderRadius: 20,
- fontWeight: '700',
- alignSelf: 'flex-start',
+ borderRadius: 8,
},
-
- dot: {
+ badgeDot: {
+ width: 6,
+ height: 6,
+ borderRadius: 3,
+ marginRight: 6,
+ },
+ badgeText: {
+ fontFamily: FontsFamily.nunitoSemiBold,
+ fontSize: 10,
+ textTransform: 'capitalize',
+ },
+ unreadIndicator: {
width: 8,
height: 8,
borderRadius: 4,
- backgroundColor: theme.colors.primary,
- marginTop: 6,
+ backgroundColor: theme.colors.primary || '#6366F1',
+ },
+ deleteSwipeContainer: {
+ width: 90,
+ height: '100%',
+ justifyContent: 'center',
+ alignItems: 'flex-end',
+ marginBottom: 12, // Match card margin
},
-
deleteSwipe: {
backgroundColor: '#FF3B30',
+ width: 80,
+ height: '84%', // A bit shorter than the container for visual balance
+ borderRadius: 20,
justifyContent: 'center',
alignItems: 'center',
- width: 80,
- borderRadius: 16,
- marginBottom: 12,
+ shadowColor: '#FF3B30',
+ shadowOffset: { width: 0, height: 4 },
+ shadowOpacity: 0.3,
+ shadowRadius: 8,
+ elevation: 5,
},
-
deleteText: {
- color: '#fff',
+ color: '#FFFFFF',
+ fontFamily: FontsFamily.nunitoBold,
fontSize: 12,
marginTop: 4,
},
-
- empty: {
+ headerActions: {
+ flexDirection: 'row',
alignItems: 'center',
- marginTop: 120,
+ gap: 8,
},
-
- emptyText: {
- marginTop: 12,
- color: '#999',
- fontSize: 16,
+ headerIconButton: {
+ width: 36,
+ height: 36,
+ borderRadius: 10,
+ backgroundColor: '#F4F4F5',
+ justifyContent: 'center',
+ alignItems: 'center',
},
- });
\ No newline at end of file
+ emptyContainer: {
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginTop: width * 0.4,
+ paddingHorizontal: 40,
+ },
+ emptyCircle: {
+ width: 100,
+ height: 100,
+ borderRadius: 50,
+ backgroundColor: '#F4F4F5',
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginBottom: 24,
+ },
+ emptyTitle: {
+ fontFamily: FontsFamily.nunitoBold,
+ fontSize: 20,
+ color: theme.colors.textPrimary,
+ marginBottom: 8,
+ },
+ emptySubText: {
+ fontFamily: FontsFamily.nunitoRegular,
+ fontSize: 14,
+ color: '#71717A',
+ textAlign: 'center',
+ lineHeight: 20,
+ },
+ });
diff --git a/src/screens/payment/PaymentModeScreen.tsx b/src/screens/payment/PaymentModeScreen.tsx
index 5450ea7..2cac4c7 100644
--- a/src/screens/payment/PaymentModeScreen.tsx
+++ b/src/screens/payment/PaymentModeScreen.tsx
@@ -319,9 +319,9 @@ const PaymentModeScreen: React.FC = () => {
`${BASE_URL}payments/pg/entry-fee/${groupId}?groupId=${groupId}&paymentId=${selectedMode}&mobileNumber=`,
'POST',
payload
- ) as any;
+ ) as any;
- console.log("payment res",res)
+ console.log("payment res", res)
if (res?.status === 'success' || res?.statusCode === 200) {
const pgOrderNo = res?.data?.pgOrderNo;
@@ -341,7 +341,7 @@ const PaymentModeScreen: React.FC = () => {
setPaying(false);
}
}
- const handlePaymentForRouteFormGroupDetails = async () => {
+ const handlePaymentForRouteFormGroupDetails = async (PaymentMode: number) => {
try {
setPaying(true);
@@ -358,36 +358,63 @@ const PaymentModeScreen: React.FC = () => {
'POST',
payload
) as any;
-
- console.log("payment payload ",payload)
- console.log("payment res ",res)
- if (res?.status === 'success' || res?.statusCode === 200) {
-
- const pgOrderNo = res?.data?.pgOrderNo;
- if (pgOrderNo) {
- // Start polling for transaction status
- pollTransactionStatus(pgOrderNo);
- } else {
- ToastAndroid.show('Payment initiated but no order number received', ToastAndroid.SHORT);
- setPaying(false);
- }
- } else {
- ToastAndroid.show(res?.data?.message || res?.message || 'Payment failed', ToastAndroid.SHORT);
- setPaying(false);
+
+ console.log("payment payload ", payload)
+ console.log("payment res ", res)
+ switch (PaymentMode) {
+ case 6:
+ {
+ if (res?.status === 'success' || res?.statusCode === 200) {
+
+ const pgOrderNo = res?.data?.pgOrderNo;
+ if (pgOrderNo) {
+ // Start polling for transaction status
+ pollTransactionStatus(pgOrderNo);
+ } else {
+ ToastAndroid.show('Payment initiated but no order number received', ToastAndroid.SHORT);
+ setPaying(false);
+ }
+ } else {
+ ToastAndroid.show(res?.data?.message || res?.message || 'Payment failed', ToastAndroid.SHORT);
+ setPaying(false);
+ }
+ }
+ break;
+ default:
+ {
+ if (res?.status === 'success' || res?.statusCode === 200) {
+
+ const pgOrderNo = res?.data?.pgOrderNo;
+ if (pgOrderNo) {
+ // Start polling for transaction status
+ pollTransactionStatus(pgOrderNo);
+ } else {
+ ToastAndroid.show('Payment initiated but no order number received', ToastAndroid.SHORT);
+ setPaying(false);
+ }
+ } else {
+ ToastAndroid.show(res?.data?.message || res?.message || 'Payment failed', ToastAndroid.SHORT);
+ setPaying(false);
+ }
+ }
+ break;
}
+
} catch (error) {
ToastAndroid.show('An error occurred during payment', ToastAndroid.SHORT);
setPaying(false);
}
}
const handleConfirmPayment = async () => {
+ console.log("selectedMode", selectedMode)
if (!selectedMode) {
+
ToastAndroid.show('Please select a payment method', ToastAndroid.SHORT);
return;
}
switch (routeFrom) {
case 'GroupDetails':
- handlePaymentForRouteFormGroupDetails();
+ handlePaymentForRouteFormGroupDetails(selectedMode);
break;
case 'GroupScreen':
handlePaymentForRouteFormGroupScreen();
diff --git a/src/screens/profile/Profile.tsx b/src/screens/profile/Profile.tsx
index 9ac97a1..8a182a2 100644
--- a/src/screens/profile/Profile.tsx
+++ b/src/screens/profile/Profile.tsx
@@ -39,6 +39,9 @@ import {
USER_MOBILE_MONEY_URL,
} from "../../utils/ApiUrls";
import SwitchNavigator from "./SwitchNavigator";
+import AsyncStorage from "@react-native-async-storage/async-storage";
+import { QueryClient } from "@tanstack/react-query";
+import { queryClient } from "../../../App";
interface UserProfile {
id: string;
@@ -183,9 +186,12 @@ const Profile = () => {
refetchStatus();
}, [refetchProfile, refetchStatus]);
+
const handleLogout = async () => {
setIsLoggingOut(true);
+
try {
+ // 🔹 Remove FCM Token
const fcmToken = await getFcmToken(user?.userId?.toString());
if (fcmToken) {
await fetch(`${BASE_URL}token/remove?fcmToken=${fcmToken}`, {
@@ -194,15 +200,34 @@ const Profile = () => {
Authorization: `Bearer ${(await Keychain.getGenericPassword() as any).password}`,
},
});
+
await deleteFcmToken(user?.userId?.toString());
}
+ // 🔹 Clear Redux State
dispatch(clearAllNotifications());
- await Keychain.resetGenericPassword();
dispatch(clearUser());
- navigation.replace("Login");
+
+ // 🔹 Clear AsyncStorage (IMPORTANT 🚨)
+ await AsyncStorage.clear();
+
+ // 🔹 Clear Keychain (secure storage)
+ await Keychain.resetGenericPassword();
+
+ // 🔹 Clear React Query / API Cache (if using)
+ if (queryClient) {
+ queryClient.clear();
+ }
+
+ // 🔹 Reset Navigation (avoid back navigation)
+ navigation.reset({
+ index: 0,
+ routes: [{ name: 'Login' }],
+ });
+
} catch (error) {
console.error("Logout error:", error);
+ } finally {
setIsLoggingOut(false);
}
};