123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- import React, { useEffect, useState } from 'react';
- import {
- View,
- Text,
- TouchableOpacity,
- StyleSheet,
- Image,
- ActivityIndicator,
- ScrollView
- } from 'react-native';
- import { useNavigation } from '@react-navigation/native';
- import { Colors } from 'src/theme';
- import { Input } from 'src/components';
- import { storage, StoreType } from 'src/storage';
- import { PostCreateGroup, usePostCreateGroupMutation } from '@api/chat';
- import { API_HOST } from 'src/constants';
- import { AvatarWithInitials } from 'src/components';
- import * as ImagePicker from 'expo-image-picker';
- import * as yup from 'yup';
- import { FlashList } from '@shopify/flash-list';
- import { useSheetRouter } from 'react-native-actions-sheet';
- import { getFontSize } from 'src/utils';
- import CloseIcon from 'assets/icons/close.svg';
- import CameraIcon from 'assets/icons/messages/camera.svg';
- import { Formik } from 'formik';
- import { useGroupChatStore } from 'src/stores/groupChatStore';
- import { NAVIGATION_PAGES } from 'src/types';
- const ProfileSchema = yup.object({
- name: yup
- .string()
- .required('name is required')
- .min(3, 'group name should be at least 3 characters'),
- description: yup.string().optional().max(8000, 'description should not exceed 8000 characters'),
- users: yup.array().min(2, 'select at least 2 members').required('members are required')
- });
- const RouteAddGroup = () => {
- const router = useSheetRouter('search-modal');
- const token = storage.get('token', StoreType.STRING) as string;
- const navigation = useNavigation();
- const {
- selectedUsers,
- removeUser,
- image,
- setImage,
- groupName,
- setGroupName,
- description,
- setDescription,
- clearStore
- } = useGroupChatStore();
- const [isSubmitting, setIsSubmitting] = useState(false);
- const { mutate: createGroup } = usePostCreateGroupMutation();
- const pickImage = async () => {
- let result = await ImagePicker.launchImageLibraryAsync({
- mediaTypes: ImagePicker.MediaTypeOptions.Images,
- allowsEditing: true,
- aspect: [4, 3],
- quality: 1
- });
- if (!result.canceled) {
- setImage(result.assets[0]);
- }
- };
- const renderUserItem = ({ item }: { item: any }) => {
- return (
- <View style={styles.selectedUserContainer}>
- <View style={styles.userContainer}>
- <TouchableOpacity onPress={() => removeUser(item.user_id)} style={styles.removeIcon}>
- <CloseIcon width={10} height={10} fill={Colors.WHITE} />
- </TouchableOpacity>
- {item.avatar ? (
- <Image source={{ uri: API_HOST + item.avatar }} style={styles.selectedAvatar} />
- ) : (
- <AvatarWithInitials
- text={`${item.first_name[0] ?? ''}${item.last_name[0] ?? ''}`}
- flag={API_HOST + item.flag1}
- size={60}
- fontSize={21}
- borderColor={Colors.LIGHT_GRAY}
- borderWidth={1}
- />
- )}
- </View>
- </View>
- );
- };
- return (
- <Formik
- validationSchema={ProfileSchema}
- initialValues={{
- name: groupName,
- description,
- users: selectedUsers
- }}
- onSubmit={async (values) => {
- setIsSubmitting(true);
- const groupData: PostCreateGroup = {
- token,
- name: values.name,
- description: values.description,
- users: selectedUsers.map((user) => +user.user_id),
- admins: []
- };
- if (image && image.uri) {
- groupData.group_avatar = {
- type: image.type || 'image',
- uri: image.uri,
- name: image.uri.split('/').pop()!
- };
- }
- await createGroup(groupData, {
- onSuccess: (res) => {
- console.log('res', res);
- setIsSubmitting(false);
- navigation.navigate(
- ...([
- NAVIGATION_PAGES.CHAT,
- {
- id: 8948,
- name: 'Test Name',
- avatar: null,
- userType: 'normal'
- }
- ] as never)
- );
- router?.close();
- },
- onError: (err) => {
- console.log('err', err);
- setIsSubmitting(false);
- }
- });
- }}
- >
- {(props) => {
- useEffect(() => {
- props.setFieldValue('users', selectedUsers);
- }, [selectedUsers]);
- return (
- <View style={styles.container}>
- <View style={styles.header}>
- <TouchableOpacity
- onPress={() => {
- router?.goBack();
- }}
- style={{
- paddingTop: 16,
- paddingBottom: 6,
- paddingHorizontal: 6
- }}
- >
- <Text style={styles.headerText}>Back</Text>
- </TouchableOpacity>
- {isSubmitting ? (
- <View
- style={{
- paddingTop: 10,
- paddingHorizontal: 10
- }}
- >
- <ActivityIndicator size="small" color={Colors.DARK_BLUE} />
- </View>
- ) : (
- <TouchableOpacity
- style={{
- paddingTop: 16,
- paddingBottom: 6,
- paddingHorizontal: 6
- }}
- onPress={() => props.handleSubmit()}
- >
- <Text style={styles.headerText}>Save</Text>
- </TouchableOpacity>
- )}
- </View>
- <ScrollView
- showsVerticalScrollIndicator={false}
- style={{ flex: 1 }}
- contentContainerStyle={{ gap: 16 }}
- >
- <View style={styles.photoContainer}>
- <TouchableOpacity style={styles.photoContainer} onPress={pickImage}>
- {!image && (
- <>
- <View style={[styles.groupPhoto, { backgroundColor: Colors.FILL_LIGHT }]}>
- <CameraIcon width={36} height={36} fill={Colors.LIGHT_GRAY} />
- </View>
- <Text style={styles.photoText}>Add photo</Text>
- </>
- )}
- {image && (
- <>
- <Image
- source={{ uri: image.uri }}
- style={{
- width: 80,
- height: 80,
- borderRadius: 40,
- borderWidth: 1,
- borderColor: Colors.FILL_LIGHT
- }}
- />
- <Text style={styles.photoText}>Change photo</Text>
- </>
- )}
- </TouchableOpacity>
- </View>
- <Input
- placeholder="Add group name"
- value={props.values.name}
- inputMode={'text'}
- onChange={(text) => {
- props.handleChange('name')(text);
- setGroupName(text);
- }}
- onBlur={props.handleBlur('name')}
- header="Group name"
- formikError={props.touched.name && props.errors.name}
- />
- <Input
- placeholder="Add group description"
- value={props.values.description}
- onChange={(text) => {
- props.handleChange('description')(text);
- setDescription(text);
- }}
- onBlur={props.handleBlur('description')}
- header="Description"
- multiline
- height={58}
- formikError={props.touched.description && props.errors.description}
- />
- {selectedUsers.length > 0 ? (
- <View
- style={[
- styles.usersRow,
- props.errors.users ? { borderColor: Colors.RED, borderWidth: 1 } : {}
- ]}
- >
- <FlashList
- viewabilityConfig={{
- waitForInteraction: true,
- itemVisiblePercentThreshold: 50,
- minimumViewTime: 1000
- }}
- scrollEnabled={false}
- data={selectedUsers}
- renderItem={renderUserItem}
- keyExtractor={(item) => item.user_id.toString()}
- estimatedItemSize={100}
- extraData={selectedUsers}
- showsVerticalScrollIndicator={false}
- contentContainerStyle={styles.selectedUsersList}
- numColumns={4}
- />
- </View>
- ) : null}
- {props.errors.users && (
- <Text
- style={[styles.textError, selectedUsers.length > 0 ? { marginTop: -32 } : {}]}
- >
- select at least 2 members
- </Text>
- )}
- </ScrollView>
- </View>
- );
- }}
- </Formik>
- );
- };
- const styles = StyleSheet.create({
- container: {
- gap: 16,
- height: '100%',
- backgroundColor: Colors.WHITE
- },
- header: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center'
- },
- headerText: {
- color: Colors.DARK_BLUE,
- fontSize: getFontSize(14),
- fontWeight: '700'
- },
- photoContainer: {
- alignItems: 'center',
- gap: 8
- },
- groupPhoto: {
- width: 80,
- height: 80,
- borderRadius: 40,
- alignItems: 'center',
- justifyContent: 'center'
- },
- photoText: {
- color: Colors.DARK_BLUE,
- fontSize: 12,
- fontWeight: '700'
- },
- input: {
- marginBottom: 12
- },
- userItem: {
- flexDirection: 'row',
- alignItems: 'center',
- paddingVertical: 8,
- paddingHorizontal: 12,
- backgroundColor: Colors.FILL_LIGHT,
- gap: 8,
- borderRadius: 8,
- marginBottom: 6
- },
- avatar: {
- width: 36,
- height: 36,
- borderRadius: 18,
- borderWidth: 1,
- borderColor: Colors.LIGHT_GRAY
- },
- userName: {
- color: Colors.DARK_BLUE,
- fontSize: getFontSize(14),
- fontFamily: 'montserrat-700'
- },
- userSubtitle: {
- color: Colors.DARK_BLUE,
- fontSize: 14,
- fontFamily: 'montserrat-500'
- },
- userNM: {
- color: Colors.DARK_BLUE,
- fontSize: 14,
- fontFamily: 'montserrat-700',
- marginRight: 12
- },
- unselectedCircle: {
- width: 20,
- height: 20,
- borderRadius: 10,
- borderWidth: 1,
- borderColor: Colors.LIGHT_GRAY,
- justifyContent: 'center',
- alignItems: 'center'
- },
- selectedUsersList: {
- paddingTop: 12
- },
- selectedUserContainer: {
- position: 'relative',
- width: '100%',
- alignItems: 'center',
- paddingBottom: 12
- },
- userContainer: {},
- selectedAvatar: {
- width: 60,
- height: 60,
- borderRadius: 30,
- borderWidth: 1,
- borderColor: Colors.LIGHT_GRAY
- },
- removeIcon: {
- position: 'absolute',
- top: -4,
- right: -4,
- width: 22,
- height: 22,
- borderRadius: 11,
- borderWidth: 1,
- borderColor: Colors.WHITE,
- backgroundColor: Colors.RED,
- justifyContent: 'center',
- alignItems: 'center',
- zIndex: 1
- },
- usersRow: {
- flex: 1,
- backgroundColor: Colors.FILL_LIGHT,
- borderRadius: 8,
- marginBottom: 24
- },
- textError: {
- color: Colors.RED,
- fontSize: getFontSize(12),
- fontFamily: 'redhat-600',
- marginTop: 5
- }
- });
- export default RouteAddGroup;
|