react-native-chating-ui-kit
Version:
CometChat React Native UI Kit is a collection of custom UI Components designed to build text , chat and calling features in your application. The UI Kit is developed to keep developers in mind and aims to reduce development efforts significantly
396 lines • 17.3 kB
JavaScript
import React, { useContext, useEffect, useImperativeHandle, useState, } from 'react';
import { View, Text, FlatList, Image,
//@ts-ignore
} from 'react-native';
import { CometChatContext } from '../../CometChatContext';
import { localize } from "../../resources/CometChatLocalize";
import { LOADING, NO_DATA_FOUND, SOMETHING_WRONG, } from '../../constants/UIKitConstants';
import { ICONS } from './resources';
import styles from './styles';
import { CometChatListItem } from "../../views/CometChatListItem";
import Header from './Header';
let lastCall;
let lastReject;
/**
* @class Users is a component useful for displaying the header and users in a list
* @description This component displays a header and list of users with subtitle,avatar,status
* @Version 1.0.0
* @author CometChat
*
*/
export const CometChatList = React.forwardRef((props, ref) => {
const { theme } = useContext(CometChatContext);
const { SubtitleView, TailView, disableUsersPresence, ListItemView, AppBarOptions, options, hideSeparator, searchPlaceholderText, backButtonIcon, showBackButton, selectionMode, onSelection, searchBoxIcon, hideSearch, title, EmptyStateView, emptyStateText, errorStateText, ErrorStateView, LoadingStateView, requestBuilder, searchRequestBuilder, hideError, onItemPress, onItemLongPress, onError, onBack, selectionIcon, listItemKey, statusIndicatorStyle, avatarStyle, listItemStyle, headViewContainerStyle, bodyViewContainerStyle, tailViewContainerStyle, listStyle, } = props;
// functions which can be access by parents
useImperativeHandle(ref, () => {
return {
updateList,
addItemToList,
removeItemFromList,
getListItem,
updateAndMoveToFirst
};
});
const [searchInput, setSearchInput] = React.useState(requestBuilder && searchRequestBuilder
? searchRequestBuilder.searchKeyword
? searchRequestBuilder.searchKeyword
: ''
: requestBuilder
? requestBuilder.searchKeyword
? requestBuilder.searchKeyword
: ''
: searchRequestBuilder
? searchRequestBuilder.searchKeyword
? searchRequestBuilder.searchKeyword
: ''
: '');
const [shouldSelect, setShouldSelect] = React.useState(selectionMode !== 'none' ? true : false);
const [selectedItems, setSelectedItems] = useState({});
const listHandlerRef = React.useRef(null);
const initialRunRef = React.useRef(true);
const [list, setList] = React.useState([]);
const [decoratorMessage, setDecoratorMessage] = React.useState(LOADING);
const searchHandler = (searchText) => {
setSearchInput(searchText);
if (searchRequestBuilder) {
listHandlerRef.current = searchRequestBuilder
.setSearchKeyword(searchText ? searchText : '')
.build();
}
else if (requestBuilder) {
listHandlerRef.current = requestBuilder
.setSearchKeyword(searchText ? searchText : '')
.build();
}
getSearch();
};
const getSearch = () => {
getList(listHandlerRef.current)
.then((newlist) => {
setDecoratorMessage(NO_DATA_FOUND);
setList(newlist);
})
.catch((error) => {
if (error && error['message'] == 'Promise cancelled') {
}
else {
setDecoratorMessage(SOMETHING_WRONG);
errorHandler(error);
}
});
};
useEffect(() => {
if (initialRunRef.current === true) {
if (searchRequestBuilder) {
if (searchInput)
listHandlerRef.current = searchRequestBuilder
.setSearchKeyword(searchInput)
.build();
else
listHandlerRef.current = searchRequestBuilder.build();
}
else if (requestBuilder) {
if (searchInput)
listHandlerRef.current = requestBuilder
.setSearchKeyword(searchInput)
.build();
else
listHandlerRef.current = requestBuilder.build();
}
initialRunRef.current = false;
handleList(false);
}
}, []);
useEffect(() => {
setShouldSelect(selectionMode !== 'none' ? true : false);
}, [selectionMode]);
/**
* Updates the list of users to be displayed
* @param
*/
const updateList = (item) => {
let newList = [...list];
let itemKey = newList.findIndex((u) => u[listItemKey] === item[listItemKey]);
if (itemKey > -1) {
newList.splice(itemKey, 1, item);
if (newList?.length === 0)
setDecoratorMessage(NO_DATA_FOUND);
setList(newList);
}
};
/**
* This will move item to first location if item doesn't exits then add it to first location.
* @param item
*/
const updateAndMoveToFirst = (item) => {
let newList = [...list];
let itemKey = newList.findIndex((u) => u[listItemKey] === item[listItemKey]);
if (itemKey > -1) {
newList.splice(itemKey, 1);
}
setList([item, ...newList]);
};
const addItemToList = (item, position) => {
setList((prev) => {
if (position != undefined) {
if (position == 0)
return [item, ...prev];
if (position >= prev.length)
return [...prev, item];
else
return [...prev.slice(0, position - 1), item, prev.slice(position)];
}
return [...prev, item];
});
};
const removeItemFromList = (uid) => {
setList((prev) => {
let newList = prev.filter((item) => item[listItemKey] !== uid);
if (newList?.length === 0)
setDecoratorMessage(NO_DATA_FOUND);
return newList;
});
};
const getListItem = (itemId) => {
return list.find((item) => item[listItemKey] == itemId);
};
/**
* update list
* @param {boolean} thrughKeyword - pass true if wants to set only new users.
*/
const handleList = (thrughKeyword) => {
getList(listHandlerRef.current)
.then((newlist) => {
if ((thrughKeyword || list.length === 0) && newlist.length === 0) {
setDecoratorMessage(NO_DATA_FOUND);
}
else {
setDecoratorMessage('');
}
if (thrughKeyword && thrughKeyword === true) {
setList(newlist);
}
else {
setList([].concat(list, newlist));
}
})
.catch((error) => {
if (error && error['message'] == 'Promise cancelled') {
}
else {
setDecoratorMessage(SOMETHING_WRONG);
errorHandler(error);
}
});
};
/**
* Handle on end reached of the list
*/
const endReached = (prop) => {
handleList(false);
};
const errorHandler = (errorCode) => {
onError && onError(errorCode);
// CometChatUserEvents.emit(CometChatUserEvents.onUserError, errorCode);
};
const handleSelection = (listItem) => {
if (shouldSelect || selectionMode === 'none')
return;
setShouldSelect(true);
setSelectedItems(() => {
return { [listItem.value.uid || listItem.value.guid]: true };
});
};
const onSelectionHandler = () => {
onSelection(Object.values(selectedItems));
setShouldSelect(false);
setSelectedItems({});
};
const onListItemPress = (item) => {
if (shouldSelect) {
if (selectionMode === 'multiple')
setSelectedItems((prev) => {
let newState = { ...prev };
if (Object.keys(prev).includes(item.value[listItemKey])) {
delete newState[item.value[listItemKey]];
return newState;
}
else {
newState[item.value[listItemKey]] = item.value;
return newState;
}
});
else if (selectionMode === 'single')
setSelectedItems({ [item.value[listItemKey]]: item.value });
}
onItemPress(item.value);
// CometChatUserEvents.emit(CometChatUserEvents.onUserItemClick, item);
};
const onListItemLongPress = (item) => {
handleSelection(item);
onItemLongPress(item.value);
// CometChatUserEvents.emit(CometChatUserEvents.onUserItemClick, item);
};
const renderListItemView = ({ item, index }) => {
if (item.header) {
const headerLetter = item.value;
return (<View key={index}>
<View style={[
styles.dividerStyle,
{
backgroundColor: listStyle?.separatorColor ?? theme.palette.getAccent100(),
},
]}></View>
<Text style={[
styles.headerLetterStyle,
{
color: listStyle.sectionHeaderTextColor ??
theme.palette.getAccent500(),
},
listStyle.sectionHeaderTextFont ?? theme.typography.text2,
]}>
{headerLetter}
</Text>
</View>);
}
return (<CometChatListItem id={item.value[listItemKey]} avatarName={item.value.name} title={item.value.name} listItemStyle={listItemStyle ? listItemStyle : { height: 55 }} headViewContainerStyle={headViewContainerStyle
? headViewContainerStyle
: { marginHorizontal: 9 }} bodyViewContainerStyle={bodyViewContainerStyle ? bodyViewContainerStyle : {}} tailViewContainerStyle={tailViewContainerStyle ? tailViewContainerStyle : {}} avatarURL={item.value.avatar || undefined} statusIndicatorColor={Object.keys(selectedItems).includes(item.value[listItemKey])
? theme.palette.getBackgroundColor()
: !disableUsersPresence && item.value.status === 'online'
? listStyle?.onlineStatusColor
? listStyle?.onlineStatusColor
: theme.palette.getSuccess()
: ''} statusIndicatorIcon={Object.keys(selectedItems).includes(item.value[listItemKey]) && ICONS.CHECK} SubtitleView={SubtitleView ? () => <SubtitleView {...item.value}/> : null} TailView={TailView ? () => <TailView {...item.value}/> : null} statusIndicatorStyle={selectedItems[item.value[listItemKey]] === true
? {
...statusIndicatorStyle,
borderRadius: 10,
height: 20,
width: 20,
}
: statusIndicatorStyle} avatarStyle={avatarStyle} options={() => options && options(item.value)} onPress={() => {
onListItemPress(item);
}} onLongPress={() => {
onListItemLongPress(item);
}}/>);
};
/**
* Gets the list of users
*/
const getList = (props) => {
const promise = new Promise((resolve, reject) => {
const cancel = () => {
clearTimeout(lastCall);
lastReject(new Error('Promise cancelled'));
};
if (lastCall) {
cancel();
}
lastCall = setTimeout(() => {
props?.fetchNext().then((listItems) => {
resolve(listItems);
});
}, 500);
lastReject = reject;
});
return promise;
};
/**
* Returns a container of users if exists else returns the corresponding decorator message
*/
const getMessageContainer = () => {
let messageContainer = null;
decoratorMessage === LOADING;
if (list.length === 0 && decoratorMessage.toLowerCase() === LOADING) {
if (LoadingStateView)
return <LoadingStateView />;
messageContainer = (<View style={styles.msgContainerStyle}>
<Image style={{
tintColor: listStyle?.loadingIconTint ?? theme.palette.getAccent600(),
}} source={ICONS.SPINNER}/>
</View>);
}
else if (list.length === 0 &&
decoratorMessage.toLowerCase() === NO_DATA_FOUND) {
messageContainer = EmptyStateView ? (<EmptyStateView />) : (<View style={styles.msgContainerStyle}>
<Text style={[
styles.msgTxtStyle,
listStyle.emptyTextFont ?? theme.typography.body,
{
color: listStyle?.emptyTextColor ?? theme.palette.getAccent400(),
},
]}>
{emptyStateText}
</Text>
</View>);
}
else if (!hideError &&
decoratorMessage.toLowerCase() === SOMETHING_WRONG) {
messageContainer = ErrorStateView ? (<ErrorStateView />) : (<View style={styles.msgContainerStyle}>
<Text style={[
styles.msgTxtStyle,
listStyle?.errorTextFont ?? theme.typography.body,
{
color: listStyle?.errorTextColor ?? theme.palette.getAccent400(),
},
]}>
{errorStateText}
</Text>
</View>);
}
else {
let currentLetter = '';
const listWithHeaders = [];
if (list.length) {
list.forEach((listItem) => {
const chr = listItem?.name && listItem.name[0].toUpperCase();
if (chr !== currentLetter && !hideSeparator && !ListItemView) {
currentLetter = chr;
listWithHeaders.push({
value: currentLetter,
header: true,
});
}
listWithHeaders.push({ value: listItem, header: false });
});
messageContainer = (<View style={styles.listContainerStyle}>
<FlatList data={listWithHeaders} extraData={listWithHeaders} renderItem={ListItemView
? ({ item, index, separators }) => (<ListItemView index={index} separators={separators} item={item.value}/>)
: renderListItemView} keyExtractor={(item, index) => (item[listItemKey]
? item[listItemKey] + '_' + index
: index)} onEndReached={endReached} onEndReachedThreshold={0.3} showsVerticalScrollIndicator={false}/>
</View>);
}
}
return messageContainer;
};
return (<View style={[
styles.containerStyle,
{
height: listStyle.height ?? '100%',
width: listStyle.width ?? '100%',
backgroundColor: listStyle.background ?? theme.palette.getBackgroundColor(),
borderRadius: listStyle.borderRadius ?? 0,
},
listStyle.border ?? {},
]}>
<Header backButtonIcon={backButtonIcon} showBackButton={showBackButton} onBack={onBack} title={title} AppBarOptions={AppBarOptions} shouldSelect={shouldSelect} onSelectionHandler={onSelectionHandler} hideSearch={hideSearch} searchBoxIcon={searchBoxIcon} searchPlaceholderText={searchPlaceholderText} searchHandler={searchHandler} searchInput={searchInput} onSubmitEditing={() => searchHandler(searchInput)} selectionIcon={selectionIcon} titleFontStyle={listStyle.titleFont ?? theme.typography.heading} titleColor={listStyle.titleColor ?? theme.palette.getAccent()} backIconTint={listStyle.backIconTint ?? theme.palette.getPrimary()} selectionIconTint={theme.palette.getPrimary()} searchBorderStyle={listStyle.searchBorder} searchBorderRadius={listStyle.searchBorderRadius} searchTextFontStyle={listStyle.searchTextFont ?? theme.typography.body} searchTextColor={listStyle.searchTextColor ?? theme.palette.getAccent()} searchPlaceholderTextColor={theme.palette.getAccent600()} searchIconTint={listStyle.searchIconTint ?? theme.palette.getAccent400()} searchBackground={listStyle.searchBackground ?? theme.palette.getAccent50()}/>
<View style={styles.container}>{getMessageContainer()}</View>
</View>);
});
CometChatList.defaultProps = {
title: 'Title',
hideSearch: false,
requestBuilder: undefined,
searchRequestBuilder: undefined,
emptyStateText: localize('NO_USERS_FOUND'),
errorStateText: localize('SOMETHING_WRONG'),
hideError: false,
onItemPress: () => { },
onItemLongPress: () => { },
onSelection: () => { },
selectionMode: 'none',
listItemKey: 'uid',
listStyle: {},
};
//# sourceMappingURL=CometChatList.js.map