260 lines
8.9 KiB
TypeScript
260 lines
8.9 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { SafeAreaView, ScrollView, StyleSheet, Switch, TouchableOpacity } from 'react-native';
|
|
import { useNavigation, NavigationProp } from '@react-navigation/native';
|
|
import { DateTimePickerAndroid, DateTimePickerEvent } from '@react-native-community/datetimepicker';
|
|
import { useSelector } from 'react-redux';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
import { Colors } from '@/src/constants/Colors';
|
|
import { useColorScheme } from '@/src/hooks/useColorScheme';
|
|
import { ThemedView } from '@/src/components/themed/ThemedView';
|
|
import { ThemedText } from '@/src/components/themed/ThemedText';
|
|
import { Request } from '@/src/services/request';
|
|
import { useToken } from '@/src/context/AppProvider';
|
|
import { Message } from '@/src/services/message';
|
|
import { store } from '@/src/store/store';
|
|
import { setSession } from '@/src/store/dataStore';
|
|
import { ThemedIcon } from '@/src/components/themed/ThemedIcon';
|
|
|
|
interface NotificationType {
|
|
key: 'dayBefore' | 'sameDay' | 'pickup';
|
|
label: string;
|
|
defaultTime: string;
|
|
minTime?: string;
|
|
maxTime?: string;
|
|
}
|
|
|
|
interface NotificationsState {
|
|
[ key: string ]: {
|
|
enabled: boolean;
|
|
time: string;
|
|
};
|
|
}
|
|
|
|
export default function CategoryScreen() {
|
|
const colorScheme = useColorScheme() ?? 'light';
|
|
const navigation = useNavigation<NavigationProp<any>>();
|
|
const { token } = useToken();
|
|
const { t } = useTranslation();
|
|
const session = useSelector( (state: any) => state.data.session );
|
|
|
|
const [ sessionSet, setSessionSet ] = useState( false );
|
|
|
|
const notificationTypes: NotificationType[] = [
|
|
{
|
|
key: 'dayBefore',
|
|
label: t( "day-before" ),
|
|
defaultTime: '19:30',
|
|
minTime: '16:00',
|
|
},
|
|
{
|
|
key: 'sameDay',
|
|
label: t( "same-day" ),
|
|
defaultTime: '08:00',
|
|
maxTime: '09:00',
|
|
},
|
|
{
|
|
key: 'pickup',
|
|
label: t( "pickup" ),
|
|
defaultTime: '17:30',
|
|
},
|
|
];
|
|
|
|
const [ notifications, setNotifications ] = useState<NotificationsState>(
|
|
notificationTypes.reduce( (acc, type) => {
|
|
acc[ type.key ] = { enabled: true, time: type.defaultTime };
|
|
return acc;
|
|
}, {} as NotificationsState )
|
|
);
|
|
|
|
let currentEdit: keyof NotificationsState = 'dayBefore';
|
|
|
|
// Set page title
|
|
useEffect( () => {
|
|
navigation.setOptions( { title: t( "notifications" ) } );
|
|
}, [] );
|
|
|
|
// Set session data
|
|
useEffect( () => {
|
|
if (!sessionSet) {
|
|
const newNotifications = { ...notifications };
|
|
|
|
notificationTypes.forEach( (type) => {
|
|
const sessionTime = session.notifications[ type.key ];
|
|
if (sessionTime === 'off') {
|
|
newNotifications[ type.key ].enabled = false;
|
|
} else {
|
|
newNotifications[ type.key ].enabled = true;
|
|
newNotifications[ type.key ].time = sessionTime;
|
|
}
|
|
} );
|
|
|
|
setNotifications( newNotifications );
|
|
setSessionSet( true );
|
|
}
|
|
}, [ session ] );
|
|
|
|
// Update session when something changes
|
|
useEffect( () => {
|
|
updateSession();
|
|
}, [ notifications ] );
|
|
|
|
// Open time picker
|
|
const selectTime = (typeKey: keyof NotificationsState) => {
|
|
currentEdit = typeKey;
|
|
const time = new Date( `1970-01-01T${notifications[ typeKey ].time}` );
|
|
|
|
if (time) {
|
|
DateTimePickerAndroid.open( {
|
|
value: time,
|
|
onChange,
|
|
mode: 'time',
|
|
is24Hour: true,
|
|
positiveButton: { label: t( "choose" ) },
|
|
negativeButton: { label: t( "close" ) },
|
|
} );
|
|
}
|
|
};
|
|
|
|
// Set selected time
|
|
const onChange = (event: DateTimePickerEvent, selectedDate?: Date) => {
|
|
if (selectedDate) {
|
|
const hours = selectedDate.getHours().toString().padStart( 2, '0' );
|
|
const minutes = selectedDate.getMinutes().toString().padStart( 2, '0' );
|
|
const timeString = `${hours}:${minutes}`;
|
|
|
|
const notification = notificationTypes.find( nt => nt.key === currentEdit )!;
|
|
|
|
if (notification.minTime) {
|
|
const minDate = new Date( `1970-01-01T${notification.minTime}` );
|
|
if (minDate > selectedDate) {
|
|
Message.error( t( "notifications-before-16-00" ) );
|
|
return;
|
|
}
|
|
} else if (notification.minTime) {
|
|
const maxDate = new Date( `1970-01-01T${notification.minTime}` );
|
|
if (maxDate < selectedDate) {
|
|
Message.error( t( "notifications-after-09-00" ) );
|
|
return;
|
|
}
|
|
}
|
|
|
|
setNotifications( (prev) => ( {
|
|
...prev,
|
|
[ currentEdit ]: { ...prev[ currentEdit ], time: timeString }
|
|
} ) );
|
|
}
|
|
};
|
|
|
|
// Enable/disable date
|
|
const toggleDate = (typeKey: keyof NotificationsState) => {
|
|
setNotifications( (prev) => ( {
|
|
...prev,
|
|
[ typeKey ]: { ...prev[ typeKey ], enabled: !prev[ typeKey ].enabled }
|
|
} ) );
|
|
};
|
|
|
|
// Update session data in API
|
|
const updateSession = () => {
|
|
if (!sessionSet) return;
|
|
|
|
const postData: { token: string; [ key: string ]: string } = {
|
|
token: token!,
|
|
};
|
|
|
|
notificationTypes.forEach( (type) => {
|
|
postData[ `notification_${convertCase( type.key )}` ] = notifications[ type.key ].enabled ? notifications[ type.key ].time : 'off';
|
|
} );
|
|
|
|
Request.post( 'sessions/update', postData ).then( (response) => {
|
|
if (response.success) {
|
|
store.dispatch( setSession( response.session ) );
|
|
} else {
|
|
Message.error( t( "something-went-wrong" ) );
|
|
}
|
|
} );
|
|
};
|
|
|
|
// Convert camel case to snake case
|
|
const convertCase = (camelCaseString: string) => {
|
|
return camelCaseString
|
|
.replace( /([A-Z])/g, '_$1' ) // Insert an underscore before each uppercase letter
|
|
.toLowerCase();
|
|
}
|
|
|
|
return (
|
|
<SafeAreaView style={{ flex: 1, backgroundColor: Colors[ colorScheme ].background }}>
|
|
<ScrollView style={styles.container}>
|
|
<ThemedView style={styles.listContainer}>
|
|
{notificationTypes.map( (type) => (
|
|
<ThemedView key={type.key} style={styles.listItem}>
|
|
<ThemedView style={styles.listTitle}>
|
|
<Switch
|
|
trackColor={{ false: '#767577', true: Colors[ colorScheme ].tint }}
|
|
thumbColor={'#fff'}
|
|
value={notifications[ type.key ].enabled}
|
|
style={styles.listSwitch}
|
|
onValueChange={() => toggleDate( type.key )}
|
|
/>
|
|
<ThemedText type="defaultSemiBold">{type.label}</ThemedText>
|
|
</ThemedView>
|
|
|
|
{notifications[ type.key ].enabled ? (
|
|
<TouchableOpacity style={styles.listEdit} onPress={() => selectTime( type.key )}>
|
|
<ThemedText style={styles.listEditText}>{t( "at" )} {notifications[ type.key ].time}</ThemedText>
|
|
<ThemedIcon size={18} name="chevron-forward" style={styles.listEditIcon}/>
|
|
</TouchableOpacity>
|
|
) : (
|
|
<ThemedView style={styles.listEdit}>
|
|
<ThemedText style={styles.listEditText}>{t( "off" )}</ThemedText>
|
|
</ThemedView>
|
|
)}
|
|
</ThemedView>
|
|
) )}
|
|
</ThemedView>
|
|
</ScrollView>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create( {
|
|
container: {
|
|
padding: 25,
|
|
},
|
|
listContainer: {
|
|
paddingBottom: 10
|
|
},
|
|
listItem: {
|
|
flex: 1,
|
|
display: 'flex',
|
|
gap: 8,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
paddingBottom: 20,
|
|
marginBottom: 20,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: '#f2f2f2',
|
|
},
|
|
listTitle: {
|
|
flex: 1,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
},
|
|
listSwitch: {
|
|
marginRight: 5,
|
|
},
|
|
listEdit: {
|
|
flex: 1,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'flex-end',
|
|
textAlign: 'right',
|
|
},
|
|
listEditText: {
|
|
fontWeight: '300',
|
|
},
|
|
listEditIcon: {
|
|
marginLeft: 10,
|
|
},
|
|
} );
|