import React, { useContext, useEffect, useRef, useState } from 'react'; import { ScrollView, StyleSheet, Switch, Text, TextInput, TouchableOpacity, View, } from 'react-native'; import { RouteProp, useNavigation } from '@react-navigation/native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { Avatar, useChannelPreviewDisplayName, useOverlayContext, useTheme, } from 'stream-chat-react-native'; import { RoundButton } from '../components/RoundButton'; import { ScreenHeader } from '../components/ScreenHeader'; import { AppContext } from '../context/AppContext'; import { useAppOverlayContext } from '../context/AppOverlayContext'; import { useBottomSheetOverlayContext } from '../context/BottomSheetOverlayContext'; import { useUserInfoOverlayContext } from '../context/UserInfoOverlayContext'; import { useChannelMembersStatus } from '../hooks/useChannelMembersStatus'; import { AddUser } from '../icons/AddUser'; import { Check } from '../icons/Check'; import { CircleClose } from '../icons/CircleClose'; import { DownArrow } from '../icons/DownArrow'; import { File } from '../icons/File'; import { GoForward } from '../icons/GoForward'; import { Mute } from '../icons/Mute'; import { Picture } from '../icons/Picture'; import { RemoveUser } from '../icons/RemoveUser'; import { getUserActivityStatus } from '../utils/getUserActivityStatus'; import type { StackNavigationProp } from '@react-navigation/stack'; import type { Channel, UserResponse } from 'stream-chat'; import type { LocalAttachmentType, LocalChannelType, LocalCommandType, LocalEventType, LocalMessageType, LocalReactionType, LocalUserType, StackNavigatorParamList, } from '../types'; const styles = StyleSheet.create({ actionContainer: { alignItems: 'center', borderBottomWidth: 1, flexDirection: 'row', justifyContent: 'space-between', padding: 16, }, actionLabelContainer: { alignItems: 'center', flexDirection: 'row', }, changeNameContainer: { alignItems: 'center', borderBottomWidth: 1, flexDirection: 'row', justifyContent: 'space-between', paddingLeft: 8, paddingRight: 16, paddingVertical: 20, }, changeNameInputBox: { flex: 1, fontSize: 14, fontWeight: '700', includeFontPadding: false, // for android vertical text centering padding: 0, // removal of default text input padding on android paddingLeft: 14, paddingTop: 0, // removal of iOS top padding for weird centering textAlignVertical: 'center', // for android vertical text centering }, changeNameInputContainer: { alignItems: 'center', flex: 1, flexDirection: 'row', }, container: { flex: 1, }, itemText: { fontSize: 14, paddingLeft: 16, }, loadMoreButton: { alignItems: 'center', borderBottomWidth: 1, flexDirection: 'row', paddingHorizontal: 16, paddingVertical: 20, width: '100%', }, loadMoreText: { fontSize: 14, paddingLeft: 20, }, memberContainer: { alignItems: 'center', borderBottomWidth: 1, flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 8, paddingVertical: 12, width: '100%', }, memberDetails: { paddingLeft: 8, }, memberName: { fontSize: 14, fontWeight: '700', paddingBottom: 1, }, memberRow: { alignItems: 'center', flexDirection: 'row', }, row: { flexDirection: 'row' }, spacer: { height: 8, }, }); type GroupChannelDetailsRouteProp = RouteProp; type GroupChannelDetailsProps = { route: GroupChannelDetailsRouteProp; }; type GroupChannelDetailsScreenNavigationProp = StackNavigationProp< StackNavigatorParamList, 'GroupChannelDetailsScreen' >; const Spacer = () => { const { theme: { colors: { grey_gainsboro }, }, } = useTheme(); return ( ); }; export const GroupChannelDetailsScreen: React.FC = ({ route: { params: { channel }, }, }) => { const { chatClient } = useContext(AppContext); const { setOverlay: setAppOverlay } = useAppOverlayContext(); const { setData: setBottomSheetOverlayData } = useBottomSheetOverlayContext(); const { setData: setUserInfoOverlayData } = useUserInfoOverlayContext(); const navigation = useNavigation(); const { setBlurType, setOverlay } = useOverlayContext(); const { theme: { colors: { accent_blue, accent_green, black, border, grey, white, white_smoke }, }, } = useTheme(); const textInputRef = useRef(null); const [muted, setMuted] = useState( chatClient?.mutedChannels.some((mute) => mute.channel?.id === channel?.id), ); const [groupName, setGroupName] = useState(channel.data?.name); const allMembers = Object.values(channel.state.members); const [members, setMembers] = useState(allMembers.slice(0, 3)); const [textInputFocused, setTextInputFocused] = useState(false); const membersStatus = useChannelMembersStatus(channel); const displayName = useChannelPreviewDisplayName< LocalAttachmentType, LocalChannelType, LocalCommandType, LocalEventType, LocalMessageType, LocalReactionType, LocalUserType >(channel, 30); const allMembersLength = allMembers.length; useEffect(() => { setMembers(allMembers.slice(0, 3)); }, [allMembersLength]); if (!channel) return null; const channelCreatorId = channel.data && (channel.data.created_by_id || (channel.data.created_by as UserResponse)?.id); /** * Opens confirmation sheet for leaving the group */ const openLeaveGroupConfirmationSheet = () => { if (chatClient?.user?.id) { setBottomSheetOverlayData({ confirmText: 'LEAVE', onConfirm: leaveGroup, subtext: `Are you sure you want to leave the group ${groupName || ''}?`, title: 'Leave group', }); setAppOverlay('confirmation'); } }; /** * Cancels the confirmation sheet. */ const openAddMembersSheet = () => { if (chatClient?.user?.id) { setBottomSheetOverlayData({ channel, }); setAppOverlay('addMembers'); } }; /** * Leave the group/channel */ const leaveGroup = async () => { if (chatClient?.user?.id) { await channel.removeMembers([chatClient?.user?.id]); } setBlurType(undefined); setAppOverlay('none'); setOverlay('none'); navigation.reset({ index: 0, routes: [ { name: 'ChatScreen', }, ], }); }; return ( ( )} subtitleText={membersStatus} titleText={displayName} /> {members.map((member) => { if (!member.user?.id) return null; return ( { if (member.user?.id !== chatClient?.user?.id) { setUserInfoOverlayData({ channel, member, navigation, }); setAppOverlay('userInfo'); } }} style={[ styles.memberContainer, { borderBottomColor: border, }, ]} > {member.user?.name} {getUserActivityStatus(member.user)} {channelCreatorId === member.user?.id ? 'owner' : ''} ); })} {allMembersLength !== members.length && ( { setMembers(Object.values(channel.state.members)); }} style={[ styles.loadMoreButton, { borderBottomColor: border, }, ]} > {`${allMembersLength - members.length} more`} )} <> NAME { setTextInputFocused(false); }} onChangeText={setGroupName} onFocus={() => { setTextInputFocused(true); }} placeholder='Add a group name' placeholderTextColor={grey} ref={textInputRef} style={[{ color: black }, styles.changeNameInputBox]} value={groupName} /> { setGroupName(channel.data?.name); textInputRef.current && textInputRef.current.blur(); }} style={{ paddingRight: 8, }} > { await channel.update({ ...channel.data, name: groupName, } as Parameters['update']>[0]); if (textInputRef.current) { textInputRef.current.blur(); } }} > {!!groupName && } Mute group { if (muted) { await channel.unmute(); } else { await channel.mute(); } setMuted((previousState) => !previousState); }} trackColor={{ false: white_smoke, true: accent_green, }} value={muted} /> { navigation.navigate('ChannelImagesScreen', { channel, }); }} style={[ styles.actionContainer, { borderBottomColor: border, }, ]} > Photos and Videos { navigation.navigate('ChannelFilesScreen', { channel, }); }} style={[ styles.actionContainer, { borderBottomColor: border, }, ]} > Files Leave Group ); };