[NEW] Reply in direct (#4582)

* add translations

* implements reply in DM

* refactor data e2e

* fix ignoreuser e2e

* create test for reply in dm

* minor tweak

Co-authored-by: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com>
Co-authored-by: Reinaldo Neto <reinaldonetof@hotmail.com>
This commit is contained in:
Gleidson Daniel Silva 2022-11-04 14:09:58 -03:00 committed by GitHub
parent 4f960e9fb4
commit 3a9a98bdfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 104 additions and 9 deletions

View File

@ -17,7 +17,7 @@ import Header, { HEADER_HEIGHT, IHeader } from './Header';
import events from '../../lib/methods/helpers/log/events';
import { IApplicationState, IEmoji, ILoggedUser, TAnyMessageModel, TSubscriptionModel } from '../../definitions';
import { getPermalinkMessage } from '../../lib/methods';
import { hasPermission } from '../../lib/methods/helpers';
import { getRoomTitle, getUidDirectMessage, hasPermission } from '../../lib/methods/helpers';
import { Services } from '../../lib/services';
export interface IMessageActionsProps {
@ -41,6 +41,7 @@ export interface IMessageActionsProps {
deleteMessagePermission?: string[];
forceDeleteMessagePermission?: string[];
pinMessagePermission?: string[];
createDirectMessagePermission?: string[];
}
export interface IMessageActions {
@ -70,7 +71,8 @@ const MessageActions = React.memo(
editMessagePermission,
deleteMessagePermission,
forceDeleteMessagePermission,
pinMessagePermission
pinMessagePermission,
createDirectMessagePermission
},
ref
) => {
@ -235,6 +237,23 @@ const MessageActions = React.memo(
replyInit(message, false);
};
const handleReplyInDM = async (message: TAnyMessageModel) => {
if (message?.u?.username) {
const result = await Services.createDirectMessage(message.u.username);
if (result.success) {
const { room } = result;
const params = {
rid: room.rid,
name: getRoomTitle(room),
t: room.t,
roomUserId: getUidDirectMessage(room),
replyInDM: message
};
Navigation.replace('RoomView', params);
}
}
};
const handleStar = async (message: TAnyMessageModel) => {
logEvent(message.starred ? events.ROOM_MSG_ACTION_UNSTAR : events.ROOM_MSG_ACTION_STAR);
try {
@ -345,6 +364,15 @@ const MessageActions = React.memo(
});
}
// Reply in DM
if (room.t !== 'd' && room.t !== 'l' && createDirectMessagePermission) {
options.push({
title: I18n.t('Reply_in_direct_message'),
icon: 'arrow-back',
onPress: () => handleReplyInDM(message)
});
}
// Edit
if (allowEdit(message)) {
options.push({
@ -480,7 +508,8 @@ const mapStateToProps = (state: IApplicationState) => ({
editMessagePermission: state.permissions['edit-message'],
deleteMessagePermission: state.permissions['delete-message'],
forceDeleteMessagePermission: state.permissions['force-delete-message'],
pinMessagePermission: state.permissions['pin-message']
pinMessagePermission: state.permissions['pin-message'],
createDirectMessagePermission: state.permissions['create-d']
});
export default connect(mapStateToProps, null, null, { forwardRef: true })(MessageActions);

View File

@ -862,6 +862,7 @@
"Select_Members": "Select Members",
"Also_send_thread_message_to_channel_behavior": "Also send thread message to channel",
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Allow users to select the Also send to channel behavior",
"Reply_in_direct_message": "Reply in Direct Message",
"room_archived": "archived room",
"room_unarchived": "unarchived room"
}

View File

@ -817,6 +817,7 @@
"Select_Members": "Selecionar Membros",
"Also_send_thread_message_to_channel_behavior": "Também enviar mensagem do tópico para o canal",
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Permitir que os usuários selecionem o comportamento Também enviar para o canal",
"Reply_in_direct_message": "Responder por mensagem direta",
"room_archived": "{{username}} arquivou a sala",
"room_unarchived": "{{username}} desarquivou a sala"
}

View File

@ -5,7 +5,7 @@ import { IItem } from '../views/TeamChannelsView';
import { IOptionsField } from '../views/NotificationPreferencesView/options';
import { IServer } from '../definitions/IServer';
import { IAttachment } from '../definitions/IAttachment';
import { IMessage, TMessageModel } from '../definitions/IMessage';
import { IMessage, TAnyMessageModel, TMessageModel } from '../definitions/IMessage';
import { ISubscription, SubscriptionType, TSubscriptionModel } from '../definitions/ISubscription';
import { ICannedResponse } from '../definitions/ICannedResponse';
import { TDataSelect } from '../definitions/IDataSelect';
@ -37,6 +37,7 @@ export type ChatsStackParamList = {
roomUserId?: string | null;
usedCannedResponse?: string;
status?: string;
replyInDM?: TAnyMessageModel;
}
| undefined; // Navigates back to RoomView already on stack
RoomActionsView: {

View File

@ -225,6 +225,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
private retryFindTimeout?: ReturnType<typeof setTimeout>;
private messageErrorActions?: IMessageErrorActions | null;
private messageActions?: IMessageActions | null;
private replyInDM?: TAnyMessageModel;
// Type of InteractionManager.runAfterInteractions
private didMountInteraction?: {
then: (onfulfilled?: (() => any) | undefined, onrejected?: (() => any) | undefined) => Promise<any>;
@ -259,6 +260,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
this.jumpToMessageId = props.route.params?.jumpToMessageId;
this.jumpToThreadId = props.route.params?.jumpToThreadId;
const roomUserId = props.route.params?.roomUserId ?? getUidDirectMessage(room);
this.replyInDM = props.route.params?.replyInDM;
this.state = {
joined: true,
room,
@ -332,6 +334,9 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
if (isIOS && this.rid) {
this.updateUnreadCount();
}
if (this.replyInDM) {
this.onReplyInit(this.replyInDM, false);
}
});
if (isTablet) {
EventEmitter.addEventListener(KEY_COMMAND, this.handleCommands);

View File

@ -51,9 +51,11 @@ const data = {
detoxpublicprotected: {
name: 'detox-public-protected',
joinCode: '123'
},
detoxpublicignore: {
name: `detox-public-ignore-${value}`
}
},
userRegularChannels: {
detoxpublic: {
name: `detox-public-${value}`
}
},
groups: {

View File

@ -54,6 +54,11 @@ const data = {
joinCode: '123'
}
},
userRegularChannels: {
detoxpublic: {
name: `detox-public-${value}`
}
},
groups: {
private: {
name: `detox-private-${value}`

View File

@ -53,6 +53,11 @@ const data = {
joinCode: '123'
}
},
userRegularChannels: {
detoxpublic: {
name: `detox-public-${value}`
}
},
groups: {
private: {
name: `detox-private-${value}`

View File

@ -158,6 +158,21 @@ const setup = async () => {
await login(data.users.regular.username, data.users.regular.password);
for (const channelKey in data.userRegularChannels) {
if (Object.prototype.hasOwnProperty.call(data.userRegularChannels, channelKey)) {
const channel = data.userRegularChannels[channelKey as TDataChannels];
const {
data: {
channel: { _id }
}
} = await createChannelIfNotExists(channel.name);
if ('joinCode' in channel) {
await changeChannelJoinCode(_id, channel.joinCode);
}
}
}
for (const groupKey in data.groups) {
if (Object.prototype.hasOwnProperty.call(data.groups, groupKey)) {
const group = data.groups[groupKey as TDataGroups];

View File

@ -13,6 +13,7 @@ import {
platformTypes,
TTextMatcher
} from '../../helpers/app';
import { sendMessage } from '../../helpers/data_setup';
async function navigateToRoom(roomName: string) {
await searchRoom(`${roomName}`);
@ -496,6 +497,37 @@ describe('Room screen', () => {
await waitFor(element(by[textMatcher](`${data.random}delete`)).atIndex(0))
.toNotExist()
.withTimeout(2000);
await tapBack();
});
it('should reply in DM to another user', async () => {
const channelName = data.userRegularChannels.detoxpublic.name;
const stringToReply = 'Message to reply in DM';
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
await navigateToRoom(channelName);
await sendMessage(data.users.alternate, channelName, stringToReply);
await waitFor(element(by[textMatcher](stringToReply)).atIndex(0))
.toBeVisible()
.withTimeout(3000);
await element(by[textMatcher](stringToReply)).atIndex(0).longPress();
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await waitFor(element(by[textMatcher]('Reply in Direct Message')).atIndex(0))
.toExist()
.withTimeout(6000);
await element(by[textMatcher]('Reply in Direct Message')).atIndex(0).tap();
await waitFor(element(by.id(`room-view-title-${data.users.alternate.username}`)))
.toExist()
.withTimeout(6000);
await element(by.id('messagebox-input')).replaceText(`${data.random} replied in dm`);
await waitFor(element(by.id('messagebox-send-message')))
.toExist()
.withTimeout(2000);
await element(by.id('messagebox-send-message')).tap();
});
});
});

View File

@ -69,9 +69,8 @@ describe('Ignore/Block User', () => {
});
describe('Ignore user from Message', () => {
it('should ignore user from message', async () => {
const channelName = data.channels.detoxpublicignore.name;
const channelName = data.userRegularChannels.detoxpublic.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');