181 lines
6.1 KiB
TypeScript
181 lines
6.1 KiB
TypeScript
import { Q } from '@nozbe/watermelondb';
|
|
import orderBy from 'lodash/orderBy';
|
|
import React, { useCallback, useEffect, useLayoutEffect, useState } from 'react';
|
|
import { FlatList } from 'react-native';
|
|
import { shallowEqual, useDispatch } from 'react-redux';
|
|
import { Subscription } from 'rxjs';
|
|
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
|
|
import { StackNavigationProp } from '@react-navigation/stack';
|
|
|
|
import { addUser, removeUser, reset } from '../../actions/selectedUsers';
|
|
import * as HeaderButton from '../../containers/HeaderButton';
|
|
import * as List from '../../containers/List';
|
|
import { sendLoadingEvent } from '../../containers/Loading';
|
|
import SafeAreaView from '../../containers/SafeAreaView';
|
|
import StatusBar from '../../containers/StatusBar';
|
|
import { ISearch, ISearchLocal } from '../../definitions';
|
|
import I18n from '../../i18n';
|
|
import database from '../../lib/database';
|
|
import UserItem from '../../containers/UserItem';
|
|
import { ISelectedUser } from '../../reducers/selectedUsers';
|
|
import { getUserSelector } from '../../selectors/login';
|
|
import { ChatsStackParamList } from '../../stacks/types';
|
|
import { useTheme } from '../../theme';
|
|
import { showErrorAlert } from '../../lib/methods/helpers/info';
|
|
import log, { events, logEvent } from '../../lib/methods/helpers/log';
|
|
import { search as searchMethod } from '../../lib/methods';
|
|
import { isGroupChat as isGroupChatMethod } from '../../lib/methods/helpers';
|
|
import { useAppSelector } from '../../lib/hooks';
|
|
import Header from './Header';
|
|
|
|
type TRoute = RouteProp<ChatsStackParamList, 'SelectedUsersView'>;
|
|
type TNavigation = StackNavigationProp<ChatsStackParamList, 'SelectedUsersView'>;
|
|
|
|
type TSearchItem = ISearch | ISearchLocal;
|
|
|
|
const SelectedUsersView = () => {
|
|
const [chats, setChats] = useState<ISelectedUser[]>([]);
|
|
const [search, setSearch] = useState<TSearchItem[]>([]);
|
|
|
|
const { maxUsers, showButton, title, buttonText, nextAction } = useRoute<TRoute>().params;
|
|
const navigation = useNavigation<TNavigation>();
|
|
|
|
const { colors } = useTheme();
|
|
const dispatch = useDispatch();
|
|
|
|
const { users, loading, useRealName, user } = useAppSelector(
|
|
state => ({
|
|
users: state.selectedUsers.users,
|
|
loading: state.selectedUsers.loading,
|
|
useRealName: state.settings.UI_Use_Real_Name as boolean,
|
|
user: getUserSelector(state)
|
|
}),
|
|
shallowEqual
|
|
);
|
|
|
|
useEffect(() => {
|
|
sendLoadingEvent({ visible: loading });
|
|
}, [loading]);
|
|
|
|
const isChecked = (username: string) => users.findIndex(el => el.name === username) !== -1;
|
|
|
|
const isGroupChat = () => maxUsers && maxUsers > 2;
|
|
|
|
useLayoutEffect(() => {
|
|
const titleHeader = title ?? I18n.t('Select_Members');
|
|
const buttonTextHeader = buttonText ?? I18n.t('Next');
|
|
const nextActionHeader = nextAction ?? (() => {});
|
|
const options = {
|
|
title: titleHeader,
|
|
headerRight: () =>
|
|
(!maxUsers || showButton || (isGroupChat() && users.length > 1)) && (
|
|
<HeaderButton.Container>
|
|
<HeaderButton.Item
|
|
title={users.length > 0 ? buttonTextHeader : I18n.t('Skip')}
|
|
onPress={nextActionHeader}
|
|
testID='selected-users-view-submit'
|
|
/>
|
|
</HeaderButton.Container>
|
|
)
|
|
};
|
|
navigation.setOptions(options);
|
|
}, [navigation, users.length, maxUsers]);
|
|
|
|
useEffect(() => {
|
|
if (isGroupChat()) {
|
|
dispatch(addUser({ _id: user.id, name: user.username, fname: user.name as string }));
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
let querySubscription: Subscription;
|
|
const init = async () => {
|
|
try {
|
|
const db = database.active;
|
|
const observable = await db.get('subscriptions').query(Q.where('t', 'd')).observeWithColumns(['room_updated_at']);
|
|
|
|
querySubscription = observable.subscribe(data => {
|
|
const chats = orderBy(data, ['roomUpdatedAt'], ['desc']) as ISelectedUser[];
|
|
setChats(chats);
|
|
});
|
|
} catch (e) {
|
|
log(e);
|
|
}
|
|
};
|
|
init();
|
|
|
|
return () => {
|
|
dispatch(reset());
|
|
if (querySubscription && querySubscription.unsubscribe) {
|
|
querySubscription.unsubscribe();
|
|
}
|
|
};
|
|
}, [dispatch]);
|
|
|
|
const handleSearch = useCallback(async (text: string) => {
|
|
const result = await searchMethod({ text, filterRooms: false });
|
|
setSearch(result);
|
|
}, []);
|
|
|
|
const toggleUser = (userItem: ISelectedUser) => {
|
|
// Disallow removing self user from the direct message group
|
|
if (isGroupChat() && user.username === userItem.name) {
|
|
return;
|
|
}
|
|
|
|
if (!isChecked(userItem.name)) {
|
|
if (isGroupChat() && users.length === maxUsers) {
|
|
return showErrorAlert(I18n.t('Max_number_of_users_allowed_is_number', { maxUsers }), I18n.t('Oops'));
|
|
}
|
|
logEvent(events.SELECTED_USERS_ADD_USER);
|
|
dispatch(addUser(userItem));
|
|
} else {
|
|
logEvent(events.SELECTED_USERS_REMOVE_USER);
|
|
dispatch(removeUser(userItem));
|
|
}
|
|
};
|
|
|
|
const _onPressItem = (item = {} as ISelectedUser) => {
|
|
if (item.search) {
|
|
toggleUser({ _id: item._id, name: item.username as string, fname: item.name });
|
|
} else {
|
|
toggleUser({ _id: item._id, name: item.name, fname: item.fname });
|
|
}
|
|
};
|
|
|
|
const searchOrChats = (search.length > 0 ? search : chats) as ISelectedUser[];
|
|
// filter DM between multiple users
|
|
const data = searchOrChats.filter(sub => !isGroupChatMethod(sub));
|
|
|
|
return (
|
|
<SafeAreaView testID='select-users-view'>
|
|
<StatusBar />
|
|
<FlatList
|
|
data={data}
|
|
keyExtractor={item => item._id}
|
|
renderItem={({ item }) => {
|
|
const name = useRealName && item.fname ? item.fname : item.name;
|
|
const username = item.search ? (item.username as string) : item.name;
|
|
return (
|
|
<UserItem
|
|
name={name}
|
|
username={username}
|
|
onPress={() => _onPressItem(item)}
|
|
testID={`select-users-view-item-${item.name}`}
|
|
icon={isChecked(username) ? 'checkbox-checked' : 'checkbox-unchecked'}
|
|
iconColor={isChecked(username) ? colors.actionTintColor : colors.separatorColor}
|
|
/>
|
|
);
|
|
}}
|
|
ItemSeparatorComponent={List.Separator}
|
|
ListFooterComponent={<List.Separator />}
|
|
ListHeaderComponent={<Header useRealName={useRealName} onChangeText={handleSearch} onPressItem={toggleUser} />}
|
|
contentContainerStyle={{ backgroundColor: colors.backgroundColor }}
|
|
keyboardShouldPersistTaps='always'
|
|
/>
|
|
</SafeAreaView>
|
|
);
|
|
};
|
|
|
|
export default SelectedUsersView;
|