[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:
parent
4f960e9fb4
commit
3a9a98bdfe
|
@ -17,7 +17,7 @@ import Header, { HEADER_HEIGHT, IHeader } from './Header';
|
||||||
import events from '../../lib/methods/helpers/log/events';
|
import events from '../../lib/methods/helpers/log/events';
|
||||||
import { IApplicationState, IEmoji, ILoggedUser, TAnyMessageModel, TSubscriptionModel } from '../../definitions';
|
import { IApplicationState, IEmoji, ILoggedUser, TAnyMessageModel, TSubscriptionModel } from '../../definitions';
|
||||||
import { getPermalinkMessage } from '../../lib/methods';
|
import { getPermalinkMessage } from '../../lib/methods';
|
||||||
import { hasPermission } from '../../lib/methods/helpers';
|
import { getRoomTitle, getUidDirectMessage, hasPermission } from '../../lib/methods/helpers';
|
||||||
import { Services } from '../../lib/services';
|
import { Services } from '../../lib/services';
|
||||||
|
|
||||||
export interface IMessageActionsProps {
|
export interface IMessageActionsProps {
|
||||||
|
@ -41,6 +41,7 @@ export interface IMessageActionsProps {
|
||||||
deleteMessagePermission?: string[];
|
deleteMessagePermission?: string[];
|
||||||
forceDeleteMessagePermission?: string[];
|
forceDeleteMessagePermission?: string[];
|
||||||
pinMessagePermission?: string[];
|
pinMessagePermission?: string[];
|
||||||
|
createDirectMessagePermission?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMessageActions {
|
export interface IMessageActions {
|
||||||
|
@ -70,7 +71,8 @@ const MessageActions = React.memo(
|
||||||
editMessagePermission,
|
editMessagePermission,
|
||||||
deleteMessagePermission,
|
deleteMessagePermission,
|
||||||
forceDeleteMessagePermission,
|
forceDeleteMessagePermission,
|
||||||
pinMessagePermission
|
pinMessagePermission,
|
||||||
|
createDirectMessagePermission
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
|
@ -235,6 +237,23 @@ const MessageActions = React.memo(
|
||||||
replyInit(message, false);
|
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) => {
|
const handleStar = async (message: TAnyMessageModel) => {
|
||||||
logEvent(message.starred ? events.ROOM_MSG_ACTION_UNSTAR : events.ROOM_MSG_ACTION_STAR);
|
logEvent(message.starred ? events.ROOM_MSG_ACTION_UNSTAR : events.ROOM_MSG_ACTION_STAR);
|
||||||
try {
|
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
|
// Edit
|
||||||
if (allowEdit(message)) {
|
if (allowEdit(message)) {
|
||||||
options.push({
|
options.push({
|
||||||
|
@ -480,7 +508,8 @@ const mapStateToProps = (state: IApplicationState) => ({
|
||||||
editMessagePermission: state.permissions['edit-message'],
|
editMessagePermission: state.permissions['edit-message'],
|
||||||
deleteMessagePermission: state.permissions['delete-message'],
|
deleteMessagePermission: state.permissions['delete-message'],
|
||||||
forceDeleteMessagePermission: state.permissions['force-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);
|
export default connect(mapStateToProps, null, null, { forwardRef: true })(MessageActions);
|
||||||
|
|
|
@ -862,6 +862,7 @@
|
||||||
"Select_Members": "Select Members",
|
"Select_Members": "Select Members",
|
||||||
"Also_send_thread_message_to_channel_behavior": "Also send thread message to channel",
|
"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",
|
"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_archived": "archived room",
|
||||||
"room_unarchived": "unarchived room"
|
"room_unarchived": "unarchived room"
|
||||||
}
|
}
|
|
@ -817,6 +817,7 @@
|
||||||
"Select_Members": "Selecionar Membros",
|
"Select_Members": "Selecionar Membros",
|
||||||
"Also_send_thread_message_to_channel_behavior": "Também enviar mensagem do tópico para o canal",
|
"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",
|
"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_archived": "{{username}} arquivou a sala",
|
||||||
"room_unarchived": "{{username}} desarquivou a sala"
|
"room_unarchived": "{{username}} desarquivou a sala"
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@ import { IItem } from '../views/TeamChannelsView';
|
||||||
import { IOptionsField } from '../views/NotificationPreferencesView/options';
|
import { IOptionsField } from '../views/NotificationPreferencesView/options';
|
||||||
import { IServer } from '../definitions/IServer';
|
import { IServer } from '../definitions/IServer';
|
||||||
import { IAttachment } from '../definitions/IAttachment';
|
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 { ISubscription, SubscriptionType, TSubscriptionModel } from '../definitions/ISubscription';
|
||||||
import { ICannedResponse } from '../definitions/ICannedResponse';
|
import { ICannedResponse } from '../definitions/ICannedResponse';
|
||||||
import { TDataSelect } from '../definitions/IDataSelect';
|
import { TDataSelect } from '../definitions/IDataSelect';
|
||||||
|
@ -37,6 +37,7 @@ export type ChatsStackParamList = {
|
||||||
roomUserId?: string | null;
|
roomUserId?: string | null;
|
||||||
usedCannedResponse?: string;
|
usedCannedResponse?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
|
replyInDM?: TAnyMessageModel;
|
||||||
}
|
}
|
||||||
| undefined; // Navigates back to RoomView already on stack
|
| undefined; // Navigates back to RoomView already on stack
|
||||||
RoomActionsView: {
|
RoomActionsView: {
|
||||||
|
|
|
@ -225,6 +225,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
private retryFindTimeout?: ReturnType<typeof setTimeout>;
|
private retryFindTimeout?: ReturnType<typeof setTimeout>;
|
||||||
private messageErrorActions?: IMessageErrorActions | null;
|
private messageErrorActions?: IMessageErrorActions | null;
|
||||||
private messageActions?: IMessageActions | null;
|
private messageActions?: IMessageActions | null;
|
||||||
|
private replyInDM?: TAnyMessageModel;
|
||||||
// Type of InteractionManager.runAfterInteractions
|
// Type of InteractionManager.runAfterInteractions
|
||||||
private didMountInteraction?: {
|
private didMountInteraction?: {
|
||||||
then: (onfulfilled?: (() => any) | undefined, onrejected?: (() => any) | undefined) => Promise<any>;
|
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.jumpToMessageId = props.route.params?.jumpToMessageId;
|
||||||
this.jumpToThreadId = props.route.params?.jumpToThreadId;
|
this.jumpToThreadId = props.route.params?.jumpToThreadId;
|
||||||
const roomUserId = props.route.params?.roomUserId ?? getUidDirectMessage(room);
|
const roomUserId = props.route.params?.roomUserId ?? getUidDirectMessage(room);
|
||||||
|
this.replyInDM = props.route.params?.replyInDM;
|
||||||
this.state = {
|
this.state = {
|
||||||
joined: true,
|
joined: true,
|
||||||
room,
|
room,
|
||||||
|
@ -332,6 +334,9 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
if (isIOS && this.rid) {
|
if (isIOS && this.rid) {
|
||||||
this.updateUnreadCount();
|
this.updateUnreadCount();
|
||||||
}
|
}
|
||||||
|
if (this.replyInDM) {
|
||||||
|
this.onReplyInit(this.replyInDM, false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if (isTablet) {
|
if (isTablet) {
|
||||||
EventEmitter.addEventListener(KEY_COMMAND, this.handleCommands);
|
EventEmitter.addEventListener(KEY_COMMAND, this.handleCommands);
|
||||||
|
|
|
@ -51,9 +51,11 @@ const data = {
|
||||||
detoxpublicprotected: {
|
detoxpublicprotected: {
|
||||||
name: 'detox-public-protected',
|
name: 'detox-public-protected',
|
||||||
joinCode: '123'
|
joinCode: '123'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
detoxpublicignore: {
|
userRegularChannels: {
|
||||||
name: `detox-public-ignore-${value}`
|
detoxpublic: {
|
||||||
|
name: `detox-public-${value}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
groups: {
|
groups: {
|
||||||
|
|
|
@ -54,6 +54,11 @@ const data = {
|
||||||
joinCode: '123'
|
joinCode: '123'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
userRegularChannels: {
|
||||||
|
detoxpublic: {
|
||||||
|
name: `detox-public-${value}`
|
||||||
|
}
|
||||||
|
},
|
||||||
groups: {
|
groups: {
|
||||||
private: {
|
private: {
|
||||||
name: `detox-private-${value}`
|
name: `detox-private-${value}`
|
||||||
|
|
|
@ -53,6 +53,11 @@ const data = {
|
||||||
joinCode: '123'
|
joinCode: '123'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
userRegularChannels: {
|
||||||
|
detoxpublic: {
|
||||||
|
name: `detox-public-${value}`
|
||||||
|
}
|
||||||
|
},
|
||||||
groups: {
|
groups: {
|
||||||
private: {
|
private: {
|
||||||
name: `detox-private-${value}`
|
name: `detox-private-${value}`
|
||||||
|
|
|
@ -158,6 +158,21 @@ const setup = async () => {
|
||||||
|
|
||||||
await login(data.users.regular.username, data.users.regular.password);
|
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) {
|
for (const groupKey in data.groups) {
|
||||||
if (Object.prototype.hasOwnProperty.call(data.groups, groupKey)) {
|
if (Object.prototype.hasOwnProperty.call(data.groups, groupKey)) {
|
||||||
const group = data.groups[groupKey as TDataGroups];
|
const group = data.groups[groupKey as TDataGroups];
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
platformTypes,
|
platformTypes,
|
||||||
TTextMatcher
|
TTextMatcher
|
||||||
} from '../../helpers/app';
|
} from '../../helpers/app';
|
||||||
|
import { sendMessage } from '../../helpers/data_setup';
|
||||||
|
|
||||||
async function navigateToRoom(roomName: string) {
|
async function navigateToRoom(roomName: string) {
|
||||||
await searchRoom(`${roomName}`);
|
await searchRoom(`${roomName}`);
|
||||||
|
@ -496,6 +497,37 @@ describe('Room screen', () => {
|
||||||
await waitFor(element(by[textMatcher](`${data.random}delete`)).atIndex(0))
|
await waitFor(element(by[textMatcher](`${data.random}delete`)).atIndex(0))
|
||||||
.toNotExist()
|
.toNotExist()
|
||||||
.withTimeout(2000);
|
.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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -69,9 +69,8 @@ describe('Ignore/Block User', () => {
|
||||||
});
|
});
|
||||||
describe('Ignore user from Message', () => {
|
describe('Ignore user from Message', () => {
|
||||||
it('should ignore user from message', async () => {
|
it('should ignore user from message', async () => {
|
||||||
const channelName = data.channels.detoxpublicignore.name;
|
const channelName = data.userRegularChannels.detoxpublic.name;
|
||||||
await navigateToRoom(channelName);
|
await navigateToRoom(channelName);
|
||||||
await element(by.id('room-view-join-button')).tap();
|
|
||||||
await sleep(300);
|
await sleep(300);
|
||||||
await sendMessage(data.users.alternate, channelName, 'message-01');
|
await sendMessage(data.users.alternate, channelName, 'message-01');
|
||||||
await sendMessage(data.users.alternate, channelName, 'message-02');
|
await sendMessage(data.users.alternate, channelName, 'message-02');
|
||||||
|
|
Loading…
Reference in New Issue