[IMPROVE] Add ignore user to user profile (#4600)
* button ignore and pased the param * load room from database and check is is ignored * move handleIgnore to lib/method/helpers * Ignore and Unignore, also reactivity * block and unblock user * pass fromRid from actionView to InfoView too * remove console.log * unsubscribe subscriptionFrom * block and unblock user from dm * test to block user and ignore user * minor tweak * tweak data * minor tweak * add test before tapBack * refactor names
This commit is contained in:
parent
300e5fc793
commit
531f3d0147
|
@ -0,0 +1,19 @@
|
|||
import { LISTENER } from '../../../containers/Toast';
|
||||
import I18n from '../../../i18n';
|
||||
import EventEmitter from './events';
|
||||
import log from './log';
|
||||
import { Services } from '../../services';
|
||||
|
||||
export const handleIgnore = async (userId: string, ignore: boolean, rid: string) => {
|
||||
try {
|
||||
await Services.ignoreUser({
|
||||
rid,
|
||||
userId,
|
||||
ignore
|
||||
});
|
||||
const message = I18n.t(ignore ? 'User_has_been_ignored' : 'User_has_been_unignored');
|
||||
EventEmitter.emit(LISTENER, { message });
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
|
@ -280,6 +280,7 @@ export default {
|
|||
RI_GO_RI_EDIT: 'ri_go_ri_edit',
|
||||
RI_GO_LIVECHAT_EDIT: 'ri_go_livechat_edit',
|
||||
RI_GO_ROOM_USER: 'ri_go_room_user',
|
||||
RI_TOGGLE_BLOCK_USER: 'ri_toggle_block_user',
|
||||
|
||||
// ROOM INFO EDIT VIEW
|
||||
RI_EDIT_TOGGLE_ROOM_TYPE: 'ri_edit_toggle_room_type',
|
||||
|
|
|
@ -68,6 +68,7 @@ export type ChatsStackParamList = {
|
|||
rid: string;
|
||||
t: SubscriptionType;
|
||||
showCloseModal?: boolean;
|
||||
fromRid?: string;
|
||||
};
|
||||
RoomInfoEditView: {
|
||||
rid: string;
|
||||
|
|
|
@ -760,7 +760,8 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
rid,
|
||||
t,
|
||||
room,
|
||||
member
|
||||
member,
|
||||
fromRid: room.rid
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ import { ILivechatVisitor } from '../../definitions/ILivechatVisitor';
|
|||
import { callJitsi } from '../../lib/methods';
|
||||
import { getRoomTitle, getUidDirectMessage, hasPermission } from '../../lib/methods/helpers';
|
||||
import { Services } from '../../lib/services';
|
||||
import { getSubscriptionByRoomId } from '../../lib/database/services/Subscription';
|
||||
import { handleIgnore } from '../../lib/methods/helpers/handleIgnore';
|
||||
|
||||
interface IGetRoomTitle {
|
||||
room: ISubscription;
|
||||
|
@ -108,6 +110,7 @@ interface IRoomInfoViewState {
|
|||
room: ISubscription;
|
||||
roomUser: IUserParsed | ILivechatVisitorModified;
|
||||
showEdit: boolean;
|
||||
roomFromRid?: TSubscriptionModel;
|
||||
}
|
||||
|
||||
class RoomInfoView extends React.Component<IRoomInfoViewProps, IRoomInfoViewState> {
|
||||
|
@ -121,22 +124,29 @@ class RoomInfoView extends React.Component<IRoomInfoViewProps, IRoomInfoViewStat
|
|||
|
||||
private roomObservable?: Observable<TSubscriptionModel>;
|
||||
|
||||
private fromRid?: string;
|
||||
|
||||
private subscriptionRoomFromRid?: Subscription;
|
||||
|
||||
constructor(props: IRoomInfoViewProps) {
|
||||
super(props);
|
||||
const room = props.route.params?.room;
|
||||
const roomUser = props.route.params?.member;
|
||||
this.rid = props.route.params?.rid;
|
||||
this.t = props.route.params?.t;
|
||||
this.fromRid = props.route.params?.fromRid;
|
||||
this.state = {
|
||||
room: (room || { rid: this.rid, t: this.t }) as any,
|
||||
roomUser: roomUser || {},
|
||||
showEdit: false
|
||||
showEdit: false,
|
||||
roomFromRid: undefined
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.isDirect) {
|
||||
this.loadUser();
|
||||
this.loadRoomFromRid();
|
||||
} else {
|
||||
this.loadRoom();
|
||||
}
|
||||
|
@ -154,6 +164,9 @@ class RoomInfoView extends React.Component<IRoomInfoViewProps, IRoomInfoViewStat
|
|||
if (this.subscription && this.subscription.unsubscribe) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
if (this.subscriptionRoomFromRid && this.subscriptionRoomFromRid.unsubscribe) {
|
||||
this.subscriptionRoomFromRid.unsubscribe();
|
||||
}
|
||||
if (this.unsubscribeFocus) {
|
||||
this.unsubscribeFocus();
|
||||
}
|
||||
|
@ -266,6 +279,19 @@ class RoomInfoView extends React.Component<IRoomInfoViewProps, IRoomInfoViewStat
|
|||
}
|
||||
};
|
||||
|
||||
loadRoomFromRid = async () => {
|
||||
if (this.fromRid) {
|
||||
try {
|
||||
const sub = await getSubscriptionByRoomId(this.fromRid);
|
||||
this.subscriptionRoomFromRid = sub?.observe().subscribe(roomFromRid => {
|
||||
this.setState({ roomFromRid });
|
||||
});
|
||||
} catch (e) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
loadRoom = async () => {
|
||||
const { room: roomState } = this.state;
|
||||
const { route, editRoomPermission, editOmnichannelContact, editLivechatRoomCustomfields } = this.props;
|
||||
|
@ -351,11 +377,32 @@ class RoomInfoView extends React.Component<IRoomInfoViewProps, IRoomInfoViewStat
|
|||
}
|
||||
};
|
||||
|
||||
handleCreateDirectMessage = async (onPress: () => void) => {
|
||||
try {
|
||||
if (this.isDirect) {
|
||||
await this.createDirect();
|
||||
}
|
||||
onPress();
|
||||
} catch {
|
||||
EventEmitter.emit(LISTENER, {
|
||||
message: I18n.t('error-action-not-allowed', { action: I18n.t('Create_Direct_Messages') })
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
videoCall = () => {
|
||||
const { room } = this.state;
|
||||
callJitsi(room);
|
||||
};
|
||||
|
||||
handleBlockUser = async (rid: string, blocked: string, block: boolean) => {
|
||||
logEvent(events.RI_TOGGLE_BLOCK_USER);
|
||||
try {
|
||||
await Services.toggleBlockUser(rid, blocked, block);
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
renderAvatar = (room: ISubscription, roomUser: IUserParsed) => {
|
||||
const { theme } = this.props;
|
||||
|
||||
|
@ -370,36 +417,54 @@ class RoomInfoView extends React.Component<IRoomInfoViewProps, IRoomInfoViewStat
|
|||
);
|
||||
};
|
||||
|
||||
renderButton = (onPress: () => void, iconName: TIconsName, text: string) => {
|
||||
renderButton = (onPress: () => void, iconName: TIconsName, text: string, danger?: boolean) => {
|
||||
const { theme } = this.props;
|
||||
|
||||
const onActionPress = async () => {
|
||||
try {
|
||||
if (this.isDirect) {
|
||||
await this.createDirect();
|
||||
}
|
||||
onPress();
|
||||
} catch {
|
||||
EventEmitter.emit(LISTENER, {
|
||||
message: I18n.t('error-action-not-allowed', { action: I18n.t('Create_Direct_Messages') })
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const color = danger ? themes[theme].dangerColor : themes[theme].actionTintColor;
|
||||
return (
|
||||
<BorderlessButton onPress={onActionPress} style={styles.roomButton}>
|
||||
<CustomIcon name={iconName} size={30} color={themes[theme].actionTintColor} />
|
||||
<Text style={[styles.roomButtonText, { color: themes[theme].actionTintColor }]}>{text}</Text>
|
||||
<BorderlessButton testID={`room-info-view-${iconName}`} onPress={onPress} style={styles.roomButton}>
|
||||
<CustomIcon name={iconName} size={30} color={color} />
|
||||
<Text style={[styles.roomButtonText, { color }]}>{text}</Text>
|
||||
</BorderlessButton>
|
||||
);
|
||||
};
|
||||
|
||||
renderButtons = () => {
|
||||
const { roomFromRid, roomUser } = this.state;
|
||||
const { jitsiEnabled } = this.props;
|
||||
|
||||
const isFromDm = roomFromRid?.rid ? new RegExp(roomUser._id).test(roomFromRid.rid) : false;
|
||||
const isDirectFromSaved = this.isDirect && this.fromRid && roomFromRid;
|
||||
|
||||
// Following the web behavior, when is a DM with myself, shouldn't appear block or ignore option
|
||||
const isDmWithMyself = roomFromRid?.uids && roomFromRid.uids?.filter(uid => uid !== roomUser._id).length === 0;
|
||||
|
||||
const ignored = roomFromRid?.ignored;
|
||||
const isIgnored = ignored?.includes?.(roomUser._id);
|
||||
|
||||
const blocker = roomFromRid?.blocker;
|
||||
|
||||
return (
|
||||
<View style={styles.roomButtonsContainer}>
|
||||
{this.renderButton(this.goRoom, 'message', I18n.t('Message'))}
|
||||
{jitsiEnabled && this.isDirect ? this.renderButton(this.videoCall, 'camera', I18n.t('Video_call')) : null}
|
||||
{this.renderButton(() => this.handleCreateDirectMessage(this.goRoom), 'message', I18n.t('Message'))}
|
||||
{jitsiEnabled && this.isDirect
|
||||
? this.renderButton(() => this.handleCreateDirectMessage(this.videoCall), 'camera', I18n.t('Video_call'))
|
||||
: null}
|
||||
{isDirectFromSaved && !isFromDm && !isDmWithMyself
|
||||
? this.renderButton(
|
||||
() => handleIgnore(roomUser._id, !isIgnored, roomFromRid.rid),
|
||||
'ignore',
|
||||
I18n.t(isIgnored ? 'Unignore' : 'Ignore'),
|
||||
true
|
||||
)
|
||||
: null}
|
||||
{isDirectFromSaved && isFromDm
|
||||
? this.renderButton(
|
||||
() => this.handleBlockUser(roomFromRid.rid, roomUser._id, !blocker),
|
||||
'ignore',
|
||||
I18n.t(`${blocker ? 'Unblock' : 'Block'}_user`),
|
||||
true
|
||||
)
|
||||
: null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -217,20 +217,6 @@ export const handleRemoveUserFromRoom = async (
|
|||
}
|
||||
};
|
||||
|
||||
export const handleIgnore = async (selectedUser: TUserModel, ignore: boolean, rid: string) => {
|
||||
try {
|
||||
await Services.ignoreUser({
|
||||
rid,
|
||||
userId: selectedUser._id,
|
||||
ignore
|
||||
});
|
||||
const message = I18n.t(ignore ? 'User_has_been_ignored' : 'User_has_been_unignored');
|
||||
EventEmitter.emit(LISTENER, { message });
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
export const handleOwner = async (
|
||||
selectedUser: TUserModel,
|
||||
isOwner: boolean,
|
||||
|
|
|
@ -16,6 +16,7 @@ import { TSubscriptionModel, TUserModel } from '../../definitions';
|
|||
import I18n from '../../i18n';
|
||||
import { useAppSelector, usePermissions } from '../../lib/hooks';
|
||||
import { getRoomTitle, isGroupChat } from '../../lib/methods/helpers';
|
||||
import { handleIgnore } from '../../lib/methods/helpers/handleIgnore';
|
||||
import { showConfirmationAlert } from '../../lib/methods/helpers/info';
|
||||
import log from '../../lib/methods/helpers/log';
|
||||
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
|
||||
|
@ -28,7 +29,6 @@ import ActionsSection from './components/ActionsSection';
|
|||
import {
|
||||
fetchRole,
|
||||
fetchRoomMembersRoles,
|
||||
handleIgnore,
|
||||
handleLeader,
|
||||
handleModerator,
|
||||
handleMute,
|
||||
|
@ -207,7 +207,7 @@ const RoomMembersView = (): React.ReactElement => {
|
|||
options.push({
|
||||
icon: 'ignore',
|
||||
title: I18n.t(isIgnored ? 'Unignore' : 'Ignore'),
|
||||
onPress: () => handleIgnore(selectedUser, !isIgnored, room.rid),
|
||||
onPress: () => handleIgnore(selectedUser._id, !isIgnored, room.rid),
|
||||
testID: 'action-sheet-ignore-user'
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1109,10 +1109,13 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
|||
|
||||
navToRoomInfo = (navParam: any) => {
|
||||
const { navigation, user, isMasterDetail } = this.props;
|
||||
const { room } = this.state;
|
||||
|
||||
logEvent(events[`ROOM_GO_${navParam.t === 'd' ? 'USER' : 'ROOM'}_INFO`]);
|
||||
if (navParam.rid === user.id) {
|
||||
return;
|
||||
}
|
||||
navParam.fromRid = room.rid;
|
||||
if (isMasterDetail) {
|
||||
navParam.showCloseModal = true;
|
||||
// @ts-ignore
|
||||
|
|
|
@ -51,6 +51,9 @@ const data = {
|
|||
detoxpublicprotected: {
|
||||
name: 'detox-public-protected',
|
||||
joinCode: '123'
|
||||
},
|
||||
detoxpublicignore: {
|
||||
name: `detox-public-ignore-${value}`
|
||||
}
|
||||
},
|
||||
groups: {
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
import { expect } from 'detox';
|
||||
|
||||
import data from '../../data';
|
||||
import { navigateToLogin, login, searchRoom, sleep, platformTypes, TTextMatcher, tapBack } from '../../helpers/app';
|
||||
import { sendMessage } from '../../helpers/data_setup';
|
||||
|
||||
async function navigateToRoom(user: string) {
|
||||
await searchRoom(`${user}`);
|
||||
await element(by.id(`rooms-list-view-item-${user}`)).tap();
|
||||
await waitFor(element(by.id('room-view')))
|
||||
.toBeVisible()
|
||||
.withTimeout(5000);
|
||||
}
|
||||
|
||||
async function navigateToInfoView() {
|
||||
await element(by.id('room-header')).tap();
|
||||
await waitFor(element(by.id('room-actions-view')))
|
||||
.toExist()
|
||||
.withTimeout(5000);
|
||||
await element(by.id('room-actions-info')).tap();
|
||||
await waitFor(element(by.id('room-info-view')))
|
||||
.toExist()
|
||||
.withTimeout(2000);
|
||||
}
|
||||
|
||||
describe('Ignore/Block User', () => {
|
||||
const user = data.users.alternate.username;
|
||||
let textMatcher: TTextMatcher;
|
||||
|
||||
before(async () => {
|
||||
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
|
||||
({ textMatcher } = platformTypes[device.getPlatform()]);
|
||||
await navigateToLogin();
|
||||
await login(data.users.regular.username, data.users.regular.password);
|
||||
});
|
||||
|
||||
describe('Usage', () => {
|
||||
describe('Block user from DM', () => {
|
||||
it('should go to user info view', async () => {
|
||||
await navigateToRoom(user);
|
||||
await navigateToInfoView();
|
||||
});
|
||||
it('should block user', async () => {
|
||||
await expect(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Block user')))).toExist();
|
||||
await element(by.id('room-info-view-ignore')).tap();
|
||||
await waitFor(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Unblock user'))))
|
||||
.toExist()
|
||||
.withTimeout(2000);
|
||||
await tapBack();
|
||||
await waitFor(element(by.id('room-actions-view')))
|
||||
.toBeVisible()
|
||||
.withTimeout(5000);
|
||||
await tapBack();
|
||||
await expect(element(by[textMatcher]('This room is blocked'))).toExist();
|
||||
});
|
||||
|
||||
it('should unblock user', async () => {
|
||||
await navigateToInfoView();
|
||||
await element(by.id('room-info-view-ignore')).tap();
|
||||
await expect(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Block user')))).toExist();
|
||||
await tapBack();
|
||||
await waitFor(element(by.id('room-actions-view')))
|
||||
.toBeVisible()
|
||||
.withTimeout(5000);
|
||||
await tapBack();
|
||||
await expect(element(by.id('messagebox'))).toBeVisible();
|
||||
await tapBack();
|
||||
});
|
||||
});
|
||||
describe('Ignore user from Message', () => {
|
||||
it('should ignore user from message', async () => {
|
||||
const channelName = data.channels.detoxpublicignore.name;
|
||||
await navigateToRoom(channelName);
|
||||
await element(by.id('room-view-join-button')).tap();
|
||||
await sleep(300);
|
||||
await sendMessage(data.users.alternate, channelName, 'message-01');
|
||||
await sendMessage(data.users.alternate, channelName, 'message-02');
|
||||
await waitFor(element(by[textMatcher](user)).atIndex(0))
|
||||
.toExist()
|
||||
.withTimeout(30000);
|
||||
await sleep(300);
|
||||
await element(by[textMatcher](user)).atIndex(0).tap();
|
||||
await expect(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Ignore')))).toExist();
|
||||
await element(by.id('room-info-view-ignore')).tap();
|
||||
await expect(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Unignore')))).toExist();
|
||||
await tapBack();
|
||||
});
|
||||
it('should tap to display message', async () => {
|
||||
await expect(element(by[textMatcher]('Message ignored. Tap to display it.')).atIndex(0)).toExist();
|
||||
await element(by[textMatcher]('Message ignored. Tap to display it.')).atIndex(0).tap();
|
||||
await waitFor(element(by[textMatcher](user)))
|
||||
.toBeVisible()
|
||||
.withTimeout(1000);
|
||||
await element(by[textMatcher](user)).atIndex(0).tap();
|
||||
await expect(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Unignore')))).toExist();
|
||||
await element(by.id('room-info-view-ignore')).tap();
|
||||
await expect(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Ignore')))).toExist();
|
||||
await tapBack();
|
||||
await expect(element(by[textMatcher]('message-02')).atIndex(0)).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue