Tweaks on sequential threads messages layout (#858)
* Tweaks on sequential threads messages * Update tests * Fix quote * Prevent from deleting thread start message when positioned inside the thread * Remove thread listener from RightButtons * Fix error on thread start parse * Stop parsing threads on render * Check replied thread only if necessary * Fix messages don't displaying * Fix threads e2e * RoomsListView.updateState slice * Stop fetching hidden messages on threads * Set initialNumToRender to 5
This commit is contained in:
parent
61fcadc879
commit
a243b1ccd7
File diff suppressed because it is too large
Load Diff
|
@ -198,6 +198,11 @@ export default class MessageActions extends React.Component {
|
||||||
if (this.isRoomReadOnly()) {
|
if (this.isRoomReadOnly()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevent from deleting thread start message when positioned inside the thread
|
||||||
|
if (props.tmid && props.tmid === props.actionMessage._id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const deleteOwn = this.isOwn(props);
|
const deleteOwn = this.isOwn(props);
|
||||||
const { Message_AllowDeleting: isDeleteAllowed, Message_AllowDeleting_BlockDeleteInMinutes } = this.props;
|
const { Message_AllowDeleting: isDeleteAllowed, Message_AllowDeleting_BlockDeleteInMinutes } = this.props;
|
||||||
if (!(this.hasDeletePermission || (isDeleteAllowed && deleteOwn) || this.hasForceDeletePermission)) {
|
if (!(this.hasDeletePermission || (isDeleteAllowed && deleteOwn) || this.hasForceDeletePermission)) {
|
||||||
|
|
|
@ -606,10 +606,10 @@ class MessageBox extends Component {
|
||||||
const { replyMessage, closeReply, threadsEnabled } = this.props;
|
const { replyMessage, closeReply, threadsEnabled } = this.props;
|
||||||
|
|
||||||
// Thread
|
// Thread
|
||||||
if (threadsEnabled) {
|
if (threadsEnabled && replyMessage.mention) {
|
||||||
onSubmit(message, replyMessage._id);
|
onSubmit(message, replyMessage._id);
|
||||||
|
|
||||||
// Legacy reply
|
// Legacy reply or quote (quote is a reply without mention)
|
||||||
} else {
|
} else {
|
||||||
const { user, roomType } = this.props;
|
const { user, roomType } = this.props;
|
||||||
const permalink = await this.getPermalink(replyMessage);
|
const permalink = await this.getPermalink(replyMessage);
|
||||||
|
|
|
@ -25,6 +25,8 @@ import messagesStatus from '../../constants/messagesStatus';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { COLOR_DANGER } from '../../constants/colors';
|
import { COLOR_DANGER } from '../../constants/colors';
|
||||||
import debounce from '../../utils/debounce';
|
import debounce from '../../utils/debounce';
|
||||||
|
import DisclosureIndicator from '../DisclosureIndicator';
|
||||||
|
import sharedStyles from '../../views/Styles';
|
||||||
|
|
||||||
const SYSTEM_MESSAGES = [
|
const SYSTEM_MESSAGES = [
|
||||||
'r',
|
'r',
|
||||||
|
@ -118,6 +120,8 @@ export default class Message extends PureComponent {
|
||||||
reactionsModal: PropTypes.bool,
|
reactionsModal: PropTypes.bool,
|
||||||
type: PropTypes.string,
|
type: PropTypes.string,
|
||||||
header: PropTypes.bool,
|
header: PropTypes.bool,
|
||||||
|
isThreadReply: PropTypes.bool,
|
||||||
|
isThreadSequential: PropTypes.bool,
|
||||||
avatar: PropTypes.string,
|
avatar: PropTypes.string,
|
||||||
alias: PropTypes.string,
|
alias: PropTypes.string,
|
||||||
ts: PropTypes.oneOfType([
|
ts: PropTypes.oneOfType([
|
||||||
|
@ -230,17 +234,17 @@ export default class Message extends PureComponent {
|
||||||
return status === messagesStatus.ERROR;
|
return status === messagesStatus.ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderAvatar = () => {
|
renderAvatar = (small = false) => {
|
||||||
const {
|
const {
|
||||||
header, avatar, author, baseUrl, user
|
header, avatar, author, baseUrl, user
|
||||||
} = this.props;
|
} = this.props;
|
||||||
if (header) {
|
if (header) {
|
||||||
return (
|
return (
|
||||||
<Avatar
|
<Avatar
|
||||||
style={styles.avatar}
|
style={small ? styles.avatarSmall : styles.avatar}
|
||||||
text={avatar ? '' : author.username}
|
text={avatar ? '' : author.username}
|
||||||
size={36}
|
size={small ? 20 : 36}
|
||||||
borderRadius={4}
|
borderRadius={small ? 2 : 4}
|
||||||
avatar={avatar}
|
avatar={avatar}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
userId={user.id}
|
userId={user.id}
|
||||||
|
@ -493,14 +497,15 @@ export default class Message extends PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.repliedThread} testID={`message-thread-replied-on-${ msg }`}>
|
<View style={styles.repliedThread} testID={`message-thread-replied-on-${ msg }`}>
|
||||||
<CustomIcon name='thread' size={16} style={styles.repliedThreadIcon} />
|
<CustomIcon name='thread' size={20} style={styles.repliedThreadIcon} />
|
||||||
<Text style={styles.repliedThreadName} numberOfLines={1}>{msg}</Text>
|
<Text style={styles.repliedThreadName} numberOfLines={1}>{msg}</Text>
|
||||||
|
<DisclosureIndicator />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInner = () => {
|
renderInner = () => {
|
||||||
const { type, tmid } = this.props;
|
const { type } = this.props;
|
||||||
if (type === 'discussion-created') {
|
if (type === 'discussion-created') {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
@ -509,15 +514,6 @@ export default class Message extends PureComponent {
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (tmid) {
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
{this.renderUsername()}
|
|
||||||
{this.renderRepliedThread()}
|
|
||||||
{this.renderContent()}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{this.renderUsername()}
|
{this.renderUsername()}
|
||||||
|
@ -531,23 +527,32 @@ export default class Message extends PureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
renderMessage = () => {
|
||||||
const {
|
const { header, isThreadReply, isThreadSequential } = this.props;
|
||||||
editing, style, header, reactionsModal, closeReactions, msg, ts, reactions, author, user, timeFormat, customEmojis, baseUrl
|
|
||||||
} = this.props;
|
|
||||||
const accessibilityLabel = I18n.t('Message_accessibility', { user: author.username, time: moment(ts).format(timeFormat), message: msg });
|
|
||||||
|
|
||||||
|
if (isThreadReply || isThreadSequential || this.isInfoMessage()) {
|
||||||
|
const thread = isThreadReply ? this.renderRepliedThread() : null;
|
||||||
return (
|
return (
|
||||||
<View style={styles.root}>
|
<React.Fragment>
|
||||||
{this.renderError()}
|
{thread}
|
||||||
<TouchableWithoutFeedback
|
<View style={[styles.flex, sharedStyles.alignItemsCenter]}>
|
||||||
onLongPress={this.onLongPress}
|
{this.renderAvatar(true)}
|
||||||
onPress={this.onPress}
|
|
||||||
>
|
|
||||||
<View
|
<View
|
||||||
style={[styles.container, header && styles.marginTop, editing && styles.editing, style]}
|
style={[
|
||||||
accessibilityLabel={accessibilityLabel}
|
styles.messageContent,
|
||||||
|
header && styles.messageContentWithHeader,
|
||||||
|
this.hasError() && header && styles.messageContentWithHeader,
|
||||||
|
this.hasError() && !header && styles.messageContentWithError,
|
||||||
|
this.isTemp() && styles.temp
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
|
{this.renderContent()}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
<View style={styles.flex}>
|
<View style={styles.flex}>
|
||||||
{this.renderAvatar()}
|
{this.renderAvatar()}
|
||||||
<View
|
<View
|
||||||
|
@ -562,6 +567,27 @@ export default class Message extends PureComponent {
|
||||||
{this.renderInner()}
|
{this.renderInner()}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
editing, style, reactionsModal, closeReactions, msg, ts, reactions, author, user, timeFormat, customEmojis, baseUrl
|
||||||
|
} = this.props;
|
||||||
|
const accessibilityLabel = I18n.t('Message_accessibility', { user: author.username, time: moment(ts).format(timeFormat), message: msg });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.root}>
|
||||||
|
{this.renderError()}
|
||||||
|
<TouchableWithoutFeedback
|
||||||
|
onLongPress={this.onLongPress}
|
||||||
|
onPress={this.onPress}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={[styles.container, editing && styles.editing, style]}
|
||||||
|
accessibilityLabel={accessibilityLabel}
|
||||||
|
>
|
||||||
|
{this.renderMessage()}
|
||||||
{reactionsModal
|
{reactionsModal
|
||||||
? (
|
? (
|
||||||
<ReactionsModal
|
<ReactionsModal
|
||||||
|
|
|
@ -163,6 +163,26 @@ export default class MessageContainer extends React.Component {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isThreadReply = () => {
|
||||||
|
const {
|
||||||
|
item, previousItem
|
||||||
|
} = this.props;
|
||||||
|
if (previousItem && item.tmid && (previousItem.tmid !== item.tmid) && (previousItem._id !== item.tmid)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isThreadSequential = () => {
|
||||||
|
const {
|
||||||
|
item, previousItem
|
||||||
|
} = this.props;
|
||||||
|
if (previousItem && item.tmid && ((previousItem.tmid === item.tmid) || (previousItem._id === item.tmid))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
parseMessage = () => {
|
parseMessage = () => {
|
||||||
const { item } = this.props;
|
const { item } = this.props;
|
||||||
return JSON.parse(JSON.stringify(item));
|
return JSON.parse(JSON.stringify(item));
|
||||||
|
@ -201,6 +221,8 @@ export default class MessageContainer extends React.Component {
|
||||||
alias={alias}
|
alias={alias}
|
||||||
editing={isEditing}
|
editing={isEditing}
|
||||||
header={this.isHeader()}
|
header={this.isHeader()}
|
||||||
|
isThreadReply={this.isThreadReply()}
|
||||||
|
isThreadSequential={this.isThreadSequential()}
|
||||||
avatar={avatar}
|
avatar={avatar}
|
||||||
user={user}
|
user={user}
|
||||||
edited={editedBy && !!editedBy.username}
|
edited={editedBy && !!editedBy.username}
|
||||||
|
|
|
@ -102,6 +102,9 @@ export default StyleSheet.create({
|
||||||
avatar: {
|
avatar: {
|
||||||
marginTop: 4
|
marginTop: 4
|
||||||
},
|
},
|
||||||
|
avatarSmall: {
|
||||||
|
marginLeft: 16
|
||||||
|
},
|
||||||
addReaction: {
|
addReaction: {
|
||||||
color: COLOR_PRIMARY
|
color: COLOR_PRIMARY
|
||||||
},
|
},
|
||||||
|
@ -217,16 +220,18 @@ export default StyleSheet.create({
|
||||||
},
|
},
|
||||||
repliedThread: {
|
repliedThread: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
flex: 1
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: 6,
|
||||||
|
marginBottom: 12
|
||||||
},
|
},
|
||||||
repliedThreadIcon: {
|
repliedThreadIcon: {
|
||||||
color: COLOR_PRIMARY,
|
color: COLOR_PRIMARY,
|
||||||
marginRight: 2
|
marginRight: 10,
|
||||||
|
marginLeft: 16
|
||||||
},
|
},
|
||||||
repliedThreadName: {
|
repliedThreadName: {
|
||||||
fontSize: 14,
|
fontSize: 16,
|
||||||
lineHeight: 16,
|
|
||||||
fontStyle: 'normal',
|
|
||||||
flex: 1,
|
flex: 1,
|
||||||
color: COLOR_PRIMARY,
|
color: COLOR_PRIMARY,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular
|
||||||
|
|
|
@ -9,7 +9,7 @@ async function load({ tmid, offset }) {
|
||||||
try {
|
try {
|
||||||
// RC 1.0
|
// RC 1.0
|
||||||
const result = await this.sdk.get('chat.getThreadMessages', {
|
const result = await this.sdk.get('chat.getThreadMessages', {
|
||||||
tmid, count: 50, offset, sort: { ts: -1 }
|
tmid, count: 50, offset, sort: { ts: -1 }, query: { _hidden: { $ne: true } }
|
||||||
});
|
});
|
||||||
if (!result || !result.success) {
|
if (!result || !result.success) {
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -48,6 +48,12 @@ class RightButtonsContainer extends React.PureComponent {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.thread && this.thread.removeAllListeners) {
|
||||||
|
this.thread.removeAllListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateThread = () => {
|
updateThread = () => {
|
||||||
const { userId } = this.props;
|
const { userId } = this.props;
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|
|
@ -59,23 +59,24 @@ export class List extends React.PureComponent {
|
||||||
if (this.updateState && this.updateState.stop) {
|
if (this.updateState && this.updateState.stop) {
|
||||||
this.updateState.stop();
|
this.updateState.stop();
|
||||||
}
|
}
|
||||||
if (this.updateThreads && this.updateThreads.stop) {
|
|
||||||
this.updateThreads.stop();
|
|
||||||
}
|
|
||||||
if (this.interactionManagerState && this.interactionManagerState.cancel) {
|
if (this.interactionManagerState && this.interactionManagerState.cancel) {
|
||||||
this.interactionManagerState.cancel();
|
this.interactionManagerState.cancel();
|
||||||
}
|
}
|
||||||
if (this.interactionManagerThreads && this.interactionManagerThreads.cancel) {
|
|
||||||
this.interactionManagerThreads.cancel();
|
|
||||||
}
|
|
||||||
console.countReset(`${ this.constructor.name }.render calls`);
|
console.countReset(`${ this.constructor.name }.render calls`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/sort-comp
|
// eslint-disable-next-line react/sort-comp
|
||||||
updateState = debounce(() => {
|
updateState = debounce(() => {
|
||||||
this.interactionManagerState = InteractionManager.runAfterInteractions(() => {
|
this.interactionManagerState = InteractionManager.runAfterInteractions(() => {
|
||||||
|
const { tmid } = this.props;
|
||||||
|
let messages = this.data;
|
||||||
|
if (tmid && this.threads[0]) {
|
||||||
|
const thread = { ...this.threads[0] };
|
||||||
|
thread.tlm = null;
|
||||||
|
messages = [...messages, thread];
|
||||||
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
messages: this.data.slice(),
|
messages: messages.slice(),
|
||||||
threads: this.threads.slice(),
|
threads: this.threads.slice(),
|
||||||
loading: false
|
loading: false
|
||||||
});
|
});
|
||||||
|
@ -95,7 +96,8 @@ export class List extends React.PureComponent {
|
||||||
try {
|
try {
|
||||||
let result;
|
let result;
|
||||||
if (tmid) {
|
if (tmid) {
|
||||||
result = await RocketChat.loadThreadMessages({ tmid, offset: messages.length });
|
// `offset` is `messages.length - 1` because we append thread start to `messages` obj
|
||||||
|
result = await RocketChat.loadThreadMessages({ tmid, offset: messages.length - 1 });
|
||||||
} else {
|
} else {
|
||||||
result = await RocketChat.loadMessagesForRoom({ rid, t, latest: messages[messages.length - 1].ts });
|
result = await RocketChat.loadMessagesForRoom({ rid, t, latest: messages[messages.length - 1].ts });
|
||||||
}
|
}
|
||||||
|
@ -130,31 +132,22 @@ export class List extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
console.count(`${ this.constructor.name }.render calls`);
|
console.count(`${ this.constructor.name }.render calls`);
|
||||||
const { messages, threads } = this.state;
|
const { messages } = this.state;
|
||||||
const { tmid } = this.props;
|
|
||||||
let data = [];
|
|
||||||
if (tmid) {
|
|
||||||
const thread = { ...threads[0] };
|
|
||||||
thread.tlm = null;
|
|
||||||
data = [...messages, thread];
|
|
||||||
} else {
|
|
||||||
data = messages;
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<EmptyRoom length={data.length} />
|
<EmptyRoom length={messages.length} />
|
||||||
<FlatList
|
<FlatList
|
||||||
testID='room-view-messages'
|
testID='room-view-messages'
|
||||||
ref={ref => this.list = ref}
|
ref={ref => this.list = ref}
|
||||||
keyExtractor={item => item._id}
|
keyExtractor={item => item._id}
|
||||||
data={data}
|
data={messages}
|
||||||
extraData={this.state}
|
extraData={this.state}
|
||||||
renderItem={this.renderItem}
|
renderItem={this.renderItem}
|
||||||
contentContainerStyle={styles.contentContainer}
|
contentContainerStyle={styles.contentContainer}
|
||||||
style={styles.list}
|
style={styles.list}
|
||||||
inverted
|
inverted
|
||||||
removeClippedSubviews
|
removeClippedSubviews
|
||||||
initialNumToRender={1}
|
initialNumToRender={5}
|
||||||
onEndReached={this.onEndReached}
|
onEndReached={this.onEndReached}
|
||||||
onEndReachedThreshold={0.5}
|
onEndReachedThreshold={0.5}
|
||||||
maxToRenderPerBatch={5}
|
maxToRenderPerBatch={5}
|
||||||
|
|
|
@ -510,7 +510,7 @@ export default class RoomView extends LoggedView {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{room._id && showActions
|
{room._id && showActions
|
||||||
? <MessageActions room={room} user={user} />
|
? <MessageActions room={room} tmid={this.tmid} user={user} />
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
{showErrorActions ? <MessageErrorActions /> : null}
|
{showErrorActions ? <MessageErrorActions /> : null}
|
||||||
|
|
|
@ -271,14 +271,14 @@ export default class RoomsListView extends LoggedView {
|
||||||
updateState = debounce(() => {
|
updateState = debounce(() => {
|
||||||
this.updateStateInteraction = InteractionManager.runAfterInteractions(() => {
|
this.updateStateInteraction = InteractionManager.runAfterInteractions(() => {
|
||||||
this.internalSetState({
|
this.internalSetState({
|
||||||
chats: this.chats,
|
chats: this.chats ? this.chats.slice() : [],
|
||||||
unread: this.unread,
|
unread: this.unread ? this.unread.slice() : [],
|
||||||
favorites: this.favorites,
|
favorites: this.favorites ? this.favorites.slice() : [],
|
||||||
discussions: this.discussions,
|
discussions: this.discussions ? this.discussions.slice() : [],
|
||||||
channels: this.channels,
|
channels: this.channels ? this.channels.slice() : [],
|
||||||
privateGroup: this.privateGroup,
|
privateGroup: this.privateGroup ? this.privateGroup.slice() : [],
|
||||||
direct: this.direct,
|
direct: this.direct ? this.direct.slice() : [],
|
||||||
livechat: this.livechat,
|
livechat: this.livechat ? this.livechat.slice() : [],
|
||||||
loading: false
|
loading: false
|
||||||
});
|
});
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
|
|
|
@ -66,6 +66,9 @@ export default StyleSheet.create({
|
||||||
alignItemsFlexStart: {
|
alignItemsFlexStart: {
|
||||||
alignItems: 'flex-start'
|
alignItems: 'flex-start'
|
||||||
},
|
},
|
||||||
|
alignItemsCenter: {
|
||||||
|
alignItems: 'center'
|
||||||
|
},
|
||||||
textAlignRight: {
|
textAlignRight: {
|
||||||
textAlign: 'right'
|
textAlign: 'right'
|
||||||
},
|
},
|
||||||
|
|
|
@ -286,8 +286,6 @@ describe('Room screen', () => {
|
||||||
await element(by.id('messagebox-send-message')).tap();
|
await element(by.id('messagebox-send-message')).tap();
|
||||||
await waitFor(element(by.id(`message-thread-button-${ thread }`))).toExist().withTimeout(5000);
|
await waitFor(element(by.id(`message-thread-button-${ thread }`))).toExist().withTimeout(5000);
|
||||||
await expect(element(by.id(`message-thread-button-${ thread }`))).toExist();
|
await expect(element(by.id(`message-thread-button-${ thread }`))).toExist();
|
||||||
await waitFor(element(by.id(`message-thread-replied-on-${ thread }`))).toExist().withTimeout(5000);
|
|
||||||
await expect(element(by.id(`message-thread-replied-on-${ thread }`))).toExist();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should navigate to thread from button', async() => {
|
it('should navigate to thread from button', async() => {
|
||||||
|
@ -313,6 +311,16 @@ describe('Room screen', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should navigate to thread from thread name', async() => {
|
it('should navigate to thread from thread name', async() => {
|
||||||
|
await mockMessage('dummymessagebetweenthethread');
|
||||||
|
await element(by.text(thread)).longPress();
|
||||||
|
await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
|
||||||
|
await expect(element(by.text('Message actions'))).toBeVisible();
|
||||||
|
await element(by.text('Reply')).tap();
|
||||||
|
await element(by.id('messagebox-input')).typeText('repliedagain');
|
||||||
|
await element(by.id('messagebox-send-message')).tap();
|
||||||
|
await waitFor(element(by.id(`message-thread-replied-on-${ thread }`))).toExist().withTimeout(5000);
|
||||||
|
await expect(element(by.id(`message-thread-replied-on-${ thread }`))).toExist();
|
||||||
|
|
||||||
await element(by.id(`message-thread-replied-on-${ thread }`)).tap();
|
await element(by.id(`message-thread-replied-on-${ thread }`)).tap();
|
||||||
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
|
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
|
||||||
await waitFor(element(by.id(`room-view-title-${ thread }`))).toBeVisible().withTimeout(5000);
|
await waitFor(element(by.id(`room-view-title-${ thread }`))).toBeVisible().withTimeout(5000);
|
||||||
|
|
|
@ -298,31 +298,37 @@ export default (
|
||||||
msg="I'm fine!"
|
msg="I'm fine!"
|
||||||
tmid='1'
|
tmid='1'
|
||||||
tmsg='How are you?'
|
tmsg='How are you?'
|
||||||
|
isThreadReply
|
||||||
/>
|
/>
|
||||||
<Message
|
<Message
|
||||||
msg="I'm fine!"
|
msg="I'm fine!"
|
||||||
tmid='1'
|
tmid='1'
|
||||||
tmsg='Thread with emoji :) :joy:'
|
tmsg='Thread with emoji :) :joy:'
|
||||||
|
isThreadReply
|
||||||
/>
|
/>
|
||||||
<Message
|
<Message
|
||||||
msg="I'm fine!"
|
msg="I'm fine!"
|
||||||
tmid='1'
|
tmid='1'
|
||||||
tmsg='Markdown: [link](http://www.google.com/) ```block code```'
|
tmsg='Markdown: [link](http://www.google.com/) ```block code```'
|
||||||
|
isThreadReply
|
||||||
/>
|
/>
|
||||||
<Message
|
<Message
|
||||||
msg="I'm fine!"
|
msg="I'm fine!"
|
||||||
tmid='1'
|
tmid='1'
|
||||||
tmsg={longText}
|
tmsg={longText}
|
||||||
|
isThreadReply
|
||||||
/>
|
/>
|
||||||
<Message
|
<Message
|
||||||
msg={longText}
|
msg={longText}
|
||||||
tmid='1'
|
tmid='1'
|
||||||
tmsg='How are you?'
|
tmsg='How are you?'
|
||||||
|
isThreadReply
|
||||||
/>
|
/>
|
||||||
<Message
|
<Message
|
||||||
msg={longText}
|
msg={longText}
|
||||||
tmid='1'
|
tmid='1'
|
||||||
tmsg={longText}
|
tmsg={longText}
|
||||||
|
isThreadReply
|
||||||
/>
|
/>
|
||||||
<Message
|
<Message
|
||||||
tmid='1'
|
tmid='1'
|
||||||
|
@ -332,6 +338,60 @@ export default (
|
||||||
description: 'This is a description',
|
description: 'This is a description',
|
||||||
audio_url: '/file-upload/c4wcNhrbXJLBvAJtN/1535569819516.aac'
|
audio_url: '/file-upload/c4wcNhrbXJLBvAJtN/1535569819516.aac'
|
||||||
}]}
|
}]}
|
||||||
|
isThreadReply
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Separator title='Sequential thread messages following thread button' />
|
||||||
|
<Message
|
||||||
|
msg='How are you?'
|
||||||
|
tcount={1}
|
||||||
|
tlm={date}
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg="I'm fine!"
|
||||||
|
tmid='1'
|
||||||
|
isThreadSequential
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg={longText}
|
||||||
|
tmid='1'
|
||||||
|
isThreadSequential
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
attachments={[{
|
||||||
|
title: 'This is a title',
|
||||||
|
description: 'This is a description',
|
||||||
|
audio_url: '/file-upload/c4wcNhrbXJLBvAJtN/1535569819516.aac'
|
||||||
|
}]}
|
||||||
|
tmid='1'
|
||||||
|
isThreadSequential
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Separator title='Sequential thread messages following thread reply' />
|
||||||
|
<Message
|
||||||
|
msg="I'm fine!"
|
||||||
|
tmid='1'
|
||||||
|
tmsg='How are you?'
|
||||||
|
isThreadReply
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg='Cool!'
|
||||||
|
tmid='1'
|
||||||
|
isThreadSequential
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg={longText}
|
||||||
|
tmid='1'
|
||||||
|
isThreadSequential
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
attachments={[{
|
||||||
|
title: 'This is a title',
|
||||||
|
description: 'This is a description',
|
||||||
|
audio_url: '/file-upload/c4wcNhrbXJLBvAJtN/1535569819516.aac'
|
||||||
|
}]}
|
||||||
|
tmid='1'
|
||||||
|
isThreadSequential
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* <Message
|
{/* <Message
|
||||||
|
|
Loading…
Reference in New Issue