[FIX] Remove nested room navigation (#4702)

* reset from room to room

* jump from room to room it's fine

* threads ipad

* fix ts roomslistview

* remove params

* fix the ipad highlight

* jump to a message from a thread to main room

* the reset within the goRoom

* create channel

* changes in canned response

* fix discussion navigation

* navigation newmessageview to users

* fix go room from room info view

* inappnotification, deeplinking, room.js

* change from room.rooms to room.subscribed

* minor tweak jumptomessage

* fix add existing channel to team and fixing test 02 of teams

* keep the same behavior after add existing channel

* keep the same behavior after add existing channel

* clean cosole

* changes requested about the name

* inapp redux to hooks

* added a comment to addexistingchanneltoteam

* minor tweak jumptomessage

* refactor goRoom to add the param popToRoot, also refactor the navigate in deeplinking too

* refactor other places that exist goRoom

* fix the didUpdate

* added in app notification test

* clean js

* minor tweak test
This commit is contained in:
Reinaldo Neto 2022-11-25 10:21:56 -03:00 committed by GitHub
parent 21ee25e818
commit aa26f7251e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 254 additions and 264 deletions

View File

@ -12,7 +12,6 @@ import { themes } from '../../lib/constants';
import { useTheme } from '../../theme'; import { useTheme } from '../../theme';
import { ROW_HEIGHT } from '../RoomItem'; import { ROW_HEIGHT } from '../RoomItem';
import { goRoom } from '../../lib/methods/helpers/goRoom'; import { goRoom } from '../../lib/methods/helpers/goRoom';
import Navigation from '../../lib/navigation/appNavigation';
import { useOrientation } from '../../dimensions'; import { useOrientation } from '../../dimensions';
import { IApplicationState, ISubscription, SubscriptionType } from '../../definitions'; import { IApplicationState, ISubscription, SubscriptionType } from '../../definitions';
@ -98,12 +97,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifie
prid prid
}; };
if (isMasterDetail) { goRoom({ item, isMasterDetail, jumpToMessageId: _id, popToRoot: true });
Navigation.navigate('DrawerNavigator');
} else {
Navigation.navigate('RoomsListView');
}
goRoom({ item, isMasterDetail, jumpToMessageId: _id });
hideNotification(); hideNotification();
}; };
@ -124,6 +118,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifie
onPress={onPress} onPress={onPress}
hitSlop={BUTTON_HIT_SLOP} hitSlop={BUTTON_HIT_SLOP}
background={Touchable.SelectableBackgroundBorderless()} background={Touchable.SelectableBackgroundBorderless()}
testID={`in-app-notification-${text}`}
> >
<> <>
<Avatar text={avatar} size={AVATAR_SIZE} type={type} rid={rid} style={styles.avatar} /> <Avatar text={avatar} size={AVATAR_SIZE} type={type} rid={rid} style={styles.avatar} />

View File

@ -1,56 +1,50 @@
import React, { memo, useEffect } from 'react'; import React, { memo, useEffect } from 'react';
import { Easing, Notifier, NotifierRoot } from 'react-native-notifier'; import { Easing, Notifier, NotifierRoot } from 'react-native-notifier';
import { connect } from 'react-redux';
import { dequal } from 'dequal';
import NotifierComponent, { INotifierComponent } from './NotifierComponent'; import NotifierComponent, { INotifierComponent } from './NotifierComponent';
import EventEmitter from '../../lib/methods/helpers/events'; import EventEmitter from '../../lib/methods/helpers/events';
import Navigation from '../../lib/navigation/appNavigation'; import Navigation from '../../lib/navigation/appNavigation';
import { getActiveRoute } from '../../lib/methods/helpers/navigation'; import { getActiveRoute } from '../../lib/methods/helpers/navigation';
import { IApplicationState } from '../../definitions'; import { useAppSelector } from '../../lib/hooks';
import { IRoom } from '../../reducers/room';
export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp'; export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp';
const InAppNotification = memo( const InAppNotification = memo(() => {
({ rooms, appState }: { rooms: IRoom['rooms']; appState: string }) => { const { appState, subscribedRoom } = useAppSelector(state => ({
const show = (notification: INotifierComponent['notification']) => { subscribedRoom: state.room.subscribedRoom,
if (appState !== 'foreground') { appState: state.app.ready && state.app.foreground ? 'foreground' : 'background'
}));
const show = (notification: INotifierComponent['notification']) => {
if (appState !== 'foreground') {
return;
}
const { payload } = notification;
const state = Navigation.navigationRef.current?.getRootState();
const route = getActiveRoute(state);
if (payload.rid) {
if (payload.rid === subscribedRoom || route?.name === 'JitsiMeetView') {
return; return;
} }
Notifier.showNotification({
const { payload } = notification; showEasing: Easing.inOut(Easing.quad),
const state = Navigation.navigationRef.current?.getRootState(); Component: NotifierComponent,
const route = getActiveRoute(state); componentProps: {
if (payload.rid) { notification
if (rooms.includes(payload.rid) || route?.name === 'JitsiMeetView') {
return;
} }
Notifier.showNotification({ });
showEasing: Easing.inOut(Easing.quad), }
Component: NotifierComponent, };
componentProps: {
notification useEffect(() => {
} const listener = EventEmitter.addEventListener(INAPP_NOTIFICATION_EMITTER, show);
}); return () => {
} EventEmitter.removeListener(INAPP_NOTIFICATION_EMITTER, listener);
}; };
}, [subscribedRoom, appState]);
useEffect(() => { return <NotifierRoot />;
const listener = EventEmitter.addEventListener(INAPP_NOTIFICATION_EMITTER, show);
return () => {
EventEmitter.removeListener(INAPP_NOTIFICATION_EMITTER, listener);
};
}, [rooms]);
return <NotifierRoot />;
},
(prevProps, nextProps) => dequal(prevProps.rooms, nextProps.rooms)
);
const mapStateToProps = (state: IApplicationState) => ({
rooms: state.room.rooms,
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background'
}); });
export default connect(mapStateToProps)(InAppNotification); export default InAppNotification;

View File

@ -237,7 +237,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
async componentDidMount() { async componentDidMount() {
const db = database.active; const db = database.active;
const { rid, tmid, navigation, sharing, usedCannedResponse, isMasterDetail } = this.props; const { rid, tmid, navigation, sharing, usedCannedResponse } = this.props;
let msg; let msg;
try { try {
const threadsCollection = db.get('threads'); const threadsCollection = db.get('threads');
@ -272,7 +272,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
EventEmiter.addEventListener(KEY_COMMAND, this.handleCommands); EventEmiter.addEventListener(KEY_COMMAND, this.handleCommands);
} }
if (isMasterDetail && usedCannedResponse) { if (usedCannedResponse) {
this.onChangeText(usedCannedResponse); this.onChangeText(usedCannedResponse);
} }

View File

@ -1,14 +1,11 @@
import React from 'react'; import React from 'react';
import { StyleProp, Text, TextStyle } from 'react-native'; import { StyleProp, Text, TextStyle } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
import { themes } from '../../lib/constants'; import { themes } from '../../lib/constants';
import { useTheme } from '../../theme'; import { useTheme } from '../../theme';
import { IUserChannel } from './interfaces'; import { IUserChannel } from './interfaces';
import styles from './styles'; import styles from './styles';
import { getSubscriptionByRoomId } from '../../lib/database/services/Subscription'; import { getSubscriptionByRoomId } from '../../lib/database/services/Subscription';
import { ChatsStackParamList } from '../../stacks/types';
import { useAppSelector } from '../../lib/hooks'; import { useAppSelector } from '../../lib/hooks';
import { goRoom } from '../../lib/methods/helpers/goRoom'; import { goRoom } from '../../lib/methods/helpers/goRoom';
@ -22,7 +19,6 @@ interface IHashtag {
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => { const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => {
const { theme } = useTheme(); const { theme } = useTheme();
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail); const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
const navigation = useNavigation<StackNavigationProp<ChatsStackParamList, 'RoomView'>>();
const handlePress = async () => { const handlePress = async () => {
const index = channels?.findIndex(channel => channel.name === hashtag); const index = channels?.findIndex(channel => channel.name === hashtag);
@ -33,7 +29,7 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IH
}; };
const room = navParam.rid && (await getSubscriptionByRoomId(navParam.rid)); const room = navParam.rid && (await getSubscriptionByRoomId(navParam.rid));
if (room) { if (room) {
goRoom({ item: room, isMasterDetail, navigationMethod: isMasterDetail ? navigation.replace : navigation.push }); goRoom({ item: room, isMasterDetail });
} else { } else {
navToRoomInfo(navParam); navToRoomInfo(navParam);
} }

View File

@ -73,19 +73,14 @@ const QueueListView = React.memo(() => {
const onPressItem = (item = {} as IOmnichannelRoom) => { const onPressItem = (item = {} as IOmnichannelRoom) => {
logEvent(events.QL_GO_ROOM); logEvent(events.QL_GO_ROOM);
if (isMasterDetail) {
navigation.navigate('DrawerNavigator');
} else {
navigation.navigate('RoomsListView');
}
goRoom({ goRoom({
item: { item: {
...item, ...item,
// we're calling v as visitor on our mergeSubscriptionsRooms // we're calling v as visitor on our mergeSubscriptionsRooms
visitor: item.v visitor: item.v
}, },
isMasterDetail isMasterDetail,
popToRoot: true
}); });
}; };

View File

@ -1,4 +1,5 @@
import { ChatsStackParamList } from '../../../stacks/types'; import { CommonActions } from '@react-navigation/native';
import Navigation from '../../navigation/appNavigation'; import Navigation from '../../navigation/appNavigation';
import { IOmnichannelRoom, SubscriptionType, IVisitor, TSubscriptionModel, ISubscription } from '../../../definitions'; import { IOmnichannelRoom, SubscriptionType, IVisitor, TSubscriptionModel, ISubscription } from '../../../definitions';
import { getRoomTitle, getUidDirectMessage } from './helpers'; import { getRoomTitle, getUidDirectMessage } from './helpers';
@ -19,19 +20,14 @@ export type TGoRoomItem = IGoRoomItem | TSubscriptionModel | ISubscription | IOm
const navigate = ({ const navigate = ({
item, item,
isMasterDetail, isMasterDetail,
popToRoot,
...props ...props
}: { }: {
item: TGoRoomItem; item: TGoRoomItem;
isMasterDetail: boolean; isMasterDetail: boolean;
navigationMethod?: () => ChatsStackParamList; popToRoot: boolean;
}) => { }) => {
let navigationMethod = props.navigationMethod ?? Navigation.navigate; const routeParams = {
if (isMasterDetail) {
navigationMethod = Navigation.replace;
}
navigationMethod('RoomView', {
rid: item.rid, rid: item.rid,
name: getRoomTitle(item), name: getRoomTitle(item),
t: item.t, t: item.t,
@ -40,6 +36,44 @@ const navigate = ({
visitor: item.visitor, visitor: item.visitor,
roomUserId: getUidDirectMessage(item), roomUserId: getUidDirectMessage(item),
...props ...props
};
if (isMasterDetail) {
if (popToRoot) {
Navigation.navigate('DrawerNavigator');
}
return Navigation.dispatch((state: any) => {
const routesRoomView = state.routes.filter((r: any) => r.name !== 'RoomView');
return CommonActions.reset({
...state,
routes: [
...routesRoomView,
{
name: 'RoomView',
params: routeParams
}
],
index: routesRoomView.length
});
});
}
if (popToRoot) {
Navigation.navigate('RoomsListView');
}
return Navigation.dispatch((state: any) => {
const routesRoomsListView = state.routes.filter((r: any) => r.name === 'RoomsListView');
return CommonActions.reset({
...state,
routes: [
...routesRoomsListView,
{
name: 'RoomView',
params: routeParams
}
],
index: routesRoomsListView.length
});
}); });
}; };
@ -51,13 +85,14 @@ interface IOmnichannelRoomVisitor extends IOmnichannelRoom {
export const goRoom = async ({ export const goRoom = async ({
item, item,
isMasterDetail = false, isMasterDetail = false,
popToRoot = false,
...props ...props
}: { }: {
item: TGoRoomItem; item: TGoRoomItem;
isMasterDetail: boolean; isMasterDetail: boolean;
navigationMethod?: any;
jumpToMessageId?: string; jumpToMessageId?: string;
usedCannedResponse?: string; usedCannedResponse?: string;
popToRoot?: boolean;
}): Promise<void> => { }): Promise<void> => {
if (!('id' in item) && item.t === SubscriptionType.DIRECT && item?.search) { if (!('id' in item) && item.t === SubscriptionType.DIRECT && item?.search) {
// if user is using the search we need first to join/create room // if user is using the search we need first to join/create room
@ -72,6 +107,7 @@ export const goRoom = async ({
t: SubscriptionType.DIRECT t: SubscriptionType.DIRECT
}, },
isMasterDetail, isMasterDetail,
popToRoot,
...props ...props
}); });
} }
@ -80,5 +116,5 @@ export const goRoom = async ({
} }
} }
return navigate({ item, isMasterDetail, ...props }); return navigate({ item, isMasterDetail, popToRoot, ...props });
}; };

View File

@ -125,8 +125,8 @@ export default class RoomSubscription {
if (ev === 'typing') { if (ev === 'typing') {
const { user } = reduxStore.getState().login; const { user } = reduxStore.getState().login;
const { UI_Use_Real_Name } = reduxStore.getState().settings; const { UI_Use_Real_Name } = reduxStore.getState().settings;
const { rooms } = reduxStore.getState().room; const { subscribedRoom } = reduxStore.getState().room;
if (rooms[0] !== _rid) { if (subscribedRoom !== _rid) {
return; return;
} }
const [name, typing] = ddpMessage.fields.args; const [name, typing] = ddpMessage.fields.args;

View File

@ -197,8 +197,8 @@ const createOrUpdateSubscription = async (subscription: ISubscription, room: ISe
} }
} }
const { rooms } = store.getState().room; const { subscribedRoom } = store.getState().room;
if (tmp.lastMessage && !rooms.includes(tmp.rid)) { if (tmp.lastMessage && subscribedRoom !== tmp.rid) {
const lastMessage = buildMessage(tmp.lastMessage); const lastMessage = buildMessage(tmp.lastMessage);
const messagesCollection = db.get('messages'); const messagesCollection = db.get('messages');
let messageRecord = {} as TMessageModel | null; let messageRecord = {} as TMessageModel | null;

View File

@ -21,11 +21,16 @@ function popToTop() {
navigationRef.current?.dispatch(StackActions.popToTop()); navigationRef.current?.dispatch(StackActions.popToTop());
} }
function dispatch(params: any) {
navigationRef.current?.dispatch(params);
}
export default { export default {
navigationRef, navigationRef,
routeNameRef, routeNameRef,
navigate, navigate,
back, back,
replace, replace,
popToTop popToTop,
dispatch
}; };

View File

@ -12,13 +12,13 @@ describe('test room reducer', () => {
it('should return modified store after subscribeRoom', () => { it('should return modified store after subscribeRoom', () => {
mockedStore.dispatch(subscribeRoom('GENERAL')); mockedStore.dispatch(subscribeRoom('GENERAL'));
const state = mockedStore.getState().room; const state = mockedStore.getState().room;
expect(state.rooms).toEqual(['GENERAL']); expect(state.subscribedRoom).toEqual('GENERAL');
}); });
it('should return empty store after remove unsubscribeRoom', () => { it('should return empty store after remove unsubscribeRoom', () => {
mockedStore.dispatch(unsubscribeRoom('GENERAL')); mockedStore.dispatch(unsubscribeRoom('GENERAL'));
const state = mockedStore.getState().room; const state = mockedStore.getState().room;
expect(state.rooms).toEqual([]); expect(state.subscribedRoom).toEqual('');
}); });
it('should return initial state after leaveRoom', () => { it('should return initial state after leaveRoom', () => {

View File

@ -6,13 +6,13 @@ export type IRoomRecord = string[];
export interface IRoom { export interface IRoom {
rid: string; rid: string;
isDeleting: boolean; isDeleting: boolean;
rooms: IRoomRecord; subscribedRoom: string;
} }
export const initialState: IRoom = { export const initialState: IRoom = {
rid: '', rid: '',
isDeleting: false, isDeleting: false,
rooms: [] subscribedRoom: ''
}; };
export default function (state = initialState, action: TActionsRoom): IRoom { export default function (state = initialState, action: TActionsRoom): IRoom {
@ -20,12 +20,12 @@ export default function (state = initialState, action: TActionsRoom): IRoom {
case ROOM.SUBSCRIBE: case ROOM.SUBSCRIBE:
return { return {
...state, ...state,
rooms: [action.rid, ...state.rooms] subscribedRoom: action.rid
}; };
case ROOM.UNSUBSCRIBE: case ROOM.UNSUBSCRIBE:
return { return {
...state, ...state,
rooms: state.rooms.filter(rid => rid !== action.rid) subscribedRoom: state.subscribedRoom === action.rid ? '' : state.subscribedRoom
}; };
case ROOM.LEAVE: case ROOM.LEAVE:
return { return {

View File

@ -4,7 +4,6 @@ import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { CREATE_CHANNEL, LOGIN } from '../actions/actionsTypes'; import { CREATE_CHANNEL, LOGIN } from '../actions/actionsTypes';
import { createChannelFailure, createChannelSuccess } from '../actions/createChannel'; import { createChannelFailure, createChannelSuccess } from '../actions/createChannel';
import { showErrorAlert } from '../lib/methods/helpers/info'; import { showErrorAlert } from '../lib/methods/helpers/info';
import Navigation from '../lib/navigation/appNavigation';
import database from '../lib/database'; import database from '../lib/database';
import I18n from '../i18n'; import I18n from '../i18n';
import { events, logEvent } from '../lib/methods/helpers/log'; import { events, logEvent } from '../lib/methods/helpers/log';
@ -78,10 +77,7 @@ const handleRequest = function* handleRequest({ data }) {
const handleSuccess = function* handleSuccess({ data }) { const handleSuccess = function* handleSuccess({ data }) {
const isMasterDetail = yield select(state => state.app.isMasterDetail); const isMasterDetail = yield select(state => state.app.isMasterDetail);
if (isMasterDetail) { goRoom({ item: data, isMasterDetail, popToRoot: true });
Navigation.navigate('DrawerNavigator');
}
goRoom({ item: data, isMasterDetail });
}; };
const handleFailure = function handleFailure({ err, isTeam }) { const handleFailure = function handleFailure({ err, isTeam }) {

View File

@ -1,7 +1,6 @@
import { all, delay, put, select, take, takeLatest } from 'redux-saga/effects'; import { all, delay, put, select, take, takeLatest } from 'redux-saga/effects';
import UserPreferences from '../lib/methods/userPreferences'; import UserPreferences from '../lib/methods/userPreferences';
import Navigation from '../lib/navigation/appNavigation';
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
import { selectServerRequest, serverInitAdd } from '../actions/server'; import { selectServerRequest, serverInitAdd } from '../actions/server';
import { inviteLinksRequest, inviteLinksSetToken } from '../actions/inviteLinks'; import { inviteLinksRequest, inviteLinksSetToken } from '../actions/inviteLinks';
@ -36,14 +35,6 @@ const handleInviteLink = function* handleInviteLink({ params, requireLogin = fal
} }
}; };
const popToRoot = function popToRoot({ isMasterDetail }) {
if (isMasterDetail) {
Navigation.navigate('DrawerNavigator');
} else {
Navigation.navigate('RoomsListView');
}
};
const navigate = function* navigate({ params }) { const navigate = function* navigate({ params }) {
yield put(appStart({ root: RootEnum.ROOT_INSIDE })); yield put(appStart({ root: RootEnum.ROOT_INSIDE }));
if (params.path || params.rid) { if (params.path || params.rid) {
@ -65,27 +56,9 @@ const navigate = function* navigate({ params }) {
}; };
const isMasterDetail = yield select(state => state.app.isMasterDetail); const isMasterDetail = yield select(state => state.app.isMasterDetail);
const focusedRooms = yield select(state => state.room.rooms);
const jumpToMessageId = params.messageId; const jumpToMessageId = params.messageId;
if (focusedRooms.includes(room.rid)) { yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId, popToRoot: true });
// if there's one room on the list or last room is the one
if (focusedRooms.length === 1 || focusedRooms[0] === room.rid) {
if (jumpToThreadId) {
// With this conditional when there is a jumpToThreadId we can avoid the thread open again
// above other thread and the room could call again the thread
popToRoot({ isMasterDetail });
}
yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId });
} else {
popToRoot({ isMasterDetail });
yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId });
}
} else {
popToRoot({ isMasterDetail });
yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId });
}
if (params.isCall) { if (params.isCall) {
callJitsi(item); callJitsi(item);
} }

View File

@ -1,7 +1,6 @@
import { select, takeLatest } from 'redux-saga/effects'; import { select, takeLatest } from 'redux-saga/effects';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import Navigation from '../lib/navigation/appNavigation';
import { MESSAGES } from '../actions/actionsTypes'; import { MESSAGES } from '../actions/actionsTypes';
import database from '../lib/database'; import database from '../lib/database';
import log from '../lib/methods/helpers/log'; import log from '../lib/methods/helpers/log';
@ -16,18 +15,13 @@ const handleReplyBroadcast = function* handleReplyBroadcast({ message }) {
const subscriptions = yield subsCollection.query(Q.where('name', username)).fetch(); const subscriptions = yield subsCollection.query(Q.where('name', username)).fetch();
const isMasterDetail = yield select(state => state.app.isMasterDetail); const isMasterDetail = yield select(state => state.app.isMasterDetail);
if (isMasterDetail) {
Navigation.navigate('DrawerNavigator');
} else {
Navigation.navigate('RoomsListView');
}
if (subscriptions.length) { if (subscriptions.length) {
goRoom({ item: subscriptions[0], isMasterDetail, message }); goRoom({ item: subscriptions[0], isMasterDetail, popToRoot: true, message });
} else { } else {
const result = yield Services.createDirectMessage(username); const result = yield Services.createDirectMessage(username);
if (result?.success) { if (result?.success) {
goRoom({ item: result?.room, isMasterDetail, message }); goRoom({ item: result?.room, isMasterDetail, popToRoot: true, message });
} }
} }
} catch (e) { } catch (e) {

View File

@ -58,13 +58,13 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) {
const subsToCreate = subscriptions.filter(i1 => !existingSubs.find(i2 => i1._id === i2._id)); const subsToCreate = subscriptions.filter(i1 => !existingSubs.find(i2 => i1._id === i2._id));
const subsToDelete = existingSubs.filter(i1 => !subscriptions.find(i2 => i1._id === i2._id)); const subsToDelete = existingSubs.filter(i1 => !subscriptions.find(i2 => i1._id === i2._id));
const openedRooms = yield select(state => state.room.rooms); const subscribedRoom = yield select(state => state.room.subscribedRoom);
const lastMessages = subscriptions const lastMessages = subscriptions
/** Checks for opened rooms and filter them out. /** Checks for opened rooms and filter them out.
* It prevents this process to try persisting the same last message on the room messages fetch. * It prevents this process to try persisting the same last message on the room messages fetch.
* This race condition is easy to reproduce on push notification tap. * This race condition is easy to reproduce on push notification tap.
*/ */
.filter(sub => !openedRooms.includes(sub.rid)) .filter(sub => subscribedRoom !== sub.rid)
.map(sub => sub.lastMessage && buildMessage(sub.lastMessage)) .map(sub => sub.lastMessage && buildMessage(sub.lastMessage))
.filter(lm => lm); .filter(lm => lm);
const lastMessagesIds = lastMessages.map(lm => lm._id).filter(lm => lm); const lastMessagesIds = lastMessages.map(lm => lm._id).filter(lm => lm);

View File

@ -17,7 +17,6 @@ import { TSupportedThemes, withTheme } from '../theme';
import SafeAreaView from '../containers/SafeAreaView'; import SafeAreaView from '../containers/SafeAreaView';
import { sendLoadingEvent } from '../containers/Loading'; import { sendLoadingEvent } from '../containers/Loading';
import { animateNextTransition } from '../lib/methods/helpers/layoutAnimation'; import { animateNextTransition } from '../lib/methods/helpers/layoutAnimation';
import { goRoom } from '../lib/methods/helpers/goRoom';
import { showErrorAlert } from '../lib/methods/helpers/info'; import { showErrorAlert } from '../lib/methods/helpers/info';
import { ChatsStackParamList } from '../stacks/types'; import { ChatsStackParamList } from '../stacks/types';
import { TSubscriptionModel, SubscriptionType, IApplicationState } from '../definitions'; import { TSubscriptionModel, SubscriptionType, IApplicationState } from '../definitions';
@ -126,7 +125,7 @@ class AddExistingChannelView extends React.Component<IAddExistingChannelViewProp
submit = async () => { submit = async () => {
const { selected } = this.state; const { selected } = this.state;
const { isMasterDetail } = this.props; const { navigation } = this.props;
sendLoadingEvent({ visible: true }); sendLoadingEvent({ visible: true });
try { try {
@ -134,9 +133,8 @@ class AddExistingChannelView extends React.Component<IAddExistingChannelViewProp
const result = await Services.addRoomsToTeam({ rooms: selected, teamId: this.teamId }); const result = await Services.addRoomsToTeam({ rooms: selected, teamId: this.teamId });
if (result.success) { if (result.success) {
sendLoadingEvent({ visible: false }); sendLoadingEvent({ visible: false });
// @ts-ignore // Expect that after you add an existing channel to a team, the user should move back to the team
// TODO: Verify goRoom interface for return of call navigation.navigate('RoomView');
goRoom({ item: result, isMasterDetail });
} }
} catch (e: any) { } catch (e: any) {
logEvent(events.CT_ADD_ROOM_TO_TEAM_F); logEvent(events.CT_ADD_ROOM_TO_TEAM_F);

View File

@ -8,14 +8,12 @@ import SafeAreaView from '../containers/SafeAreaView';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
import Button from '../containers/Button'; import Button from '../containers/Button';
import { TSupportedThemes, useTheme } from '../theme'; import { TSupportedThemes, useTheme } from '../theme';
import Navigation from '../lib/navigation/appNavigation';
import { goRoom } from '../lib/methods/helpers/goRoom'; import { goRoom } from '../lib/methods/helpers/goRoom';
import { themes } from '../lib/constants'; import { themes } from '../lib/constants';
import Markdown from '../containers/markdown'; import Markdown from '../containers/markdown';
import { ICannedResponse } from '../definitions/ICannedResponse'; import { ICannedResponse } from '../definitions/ICannedResponse';
import { ChatsStackParamList } from '../stacks/types'; import { ChatsStackParamList } from '../stacks/types';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import { getRoomTitle, getUidDirectMessage } from '../lib/methods/helpers';
import { useAppSelector } from '../lib/hooks'; import { useAppSelector } from '../lib/hooks';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@ -97,7 +95,6 @@ const CannedResponseDetail = ({ navigation, route }: ICannedResponseDetailProps)
const { cannedResponse } = route?.params; const { cannedResponse } = route?.params;
const { theme } = useTheme(); const { theme } = useTheme();
const { isMasterDetail } = useAppSelector(state => state.app); const { isMasterDetail } = useAppSelector(state => state.app);
const { rooms } = useAppSelector(state => state.room);
useEffect(() => { useEffect(() => {
navigation.setOptions({ navigation.setOptions({
@ -107,31 +104,9 @@ const CannedResponseDetail = ({ navigation, route }: ICannedResponseDetailProps)
const navigateToRoom = (item: ICannedResponse) => { const navigateToRoom = (item: ICannedResponse) => {
const { room } = route.params; const { room } = route.params;
const { name } = room;
const params = {
rid: room.rid,
name: getRoomTitle({
t: room.t,
fname: name
}),
t: room.t,
roomUserId: getUidDirectMessage(room),
usedCannedResponse: item.text
};
if (room.rid) { if (room.rid) {
// if it's on master detail layout, we close the modal and replace RoomView goRoom({ item: room, isMasterDetail, popToRoot: true, usedCannedResponse: item.text });
if (isMasterDetail) {
Navigation.navigate('DrawerNavigator');
goRoom({ item: params, isMasterDetail });
} else {
let navigate = navigation.push;
// if this is a room focused
if (rooms.includes(room.rid)) {
({ navigate } = navigation);
}
navigate('RoomView', params);
}
} }
}; };

View File

@ -12,7 +12,6 @@ import ActivityIndicator from '../../containers/ActivityIndicator';
import SearchHeader from '../../containers/SearchHeader'; import SearchHeader from '../../containers/SearchHeader';
import BackgroundContainer from '../../containers/BackgroundContainer'; import BackgroundContainer from '../../containers/BackgroundContainer';
import { useTheme } from '../../theme'; import { useTheme } from '../../theme';
import Navigation from '../../lib/navigation/appNavigation';
import { goRoom } from '../../lib/methods/helpers/goRoom'; import { goRoom } from '../../lib/methods/helpers/goRoom';
import * as HeaderButton from '../../containers/HeaderButton'; import * as HeaderButton from '../../containers/HeaderButton';
import * as List from '../../containers/List'; import * as List from '../../containers/List';
@ -24,7 +23,7 @@ import DropdownItemHeader from './Dropdown/DropdownItemHeader';
import styles from './styles'; import styles from './styles';
import { ICannedResponse } from '../../definitions/ICannedResponse'; import { ICannedResponse } from '../../definitions/ICannedResponse';
import { ChatsStackParamList } from '../../stacks/types'; import { ChatsStackParamList } from '../../stacks/types';
import { getRoomTitle, getUidDirectMessage, useDebounce } from '../../lib/methods/helpers'; import { useDebounce } from '../../lib/methods/helpers';
import { Services } from '../../lib/services'; import { Services } from '../../lib/services';
import { ILivechatDepartment } from '../../definitions/ILivechatDepartment'; import { ILivechatDepartment } from '../../definitions/ILivechatDepartment';
import { useAppSelector } from '../../lib/hooks'; import { useAppSelector } from '../../lib/hooks';
@ -73,7 +72,6 @@ const CannedResponsesListView = ({ navigation, route }: ICannedResponsesListView
const { theme } = useTheme(); const { theme } = useTheme();
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail); const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
const rooms = useAppSelector(state => state.room.rooms);
const getRoomFromDb = async () => { const getRoomFromDb = async () => {
const { rid } = route.params; const { rid } = route.params;
@ -107,34 +105,8 @@ const CannedResponsesListView = ({ navigation, route }: ICannedResponsesListView
}; };
const navigateToRoom = (item: ICannedResponse) => { const navigateToRoom = (item: ICannedResponse) => {
if (!room) { if (room?.rid) {
return; goRoom({ item: room, isMasterDetail, popToRoot: true, usedCannedResponse: item.text });
}
const { name } = room;
const params = {
rid: room.rid,
name: getRoomTitle({
t: room.t,
fname: name
}),
t: room.t,
roomUserId: getUidDirectMessage(room),
usedCannedResponse: item.text
};
if (room.rid) {
// if it's on master detail layout, we close the modal and replace RoomView
if (isMasterDetail) {
Navigation.navigate('DrawerNavigator');
goRoom({ item: params, isMasterDetail });
} else {
let navigate = navigation.push;
// if this is a room focused
if (rooms.includes(room.rid)) {
({ navigate } = navigation);
}
navigate('RoomView', params);
}
} }
}; };

View File

@ -12,7 +12,6 @@ import StatusBar from '../../containers/StatusBar';
import { withTheme } from '../../theme'; import { withTheme } from '../../theme';
import { getUserSelector } from '../../selectors/login'; import { getUserSelector } from '../../selectors/login';
import { FormTextInput } from '../../containers/TextInput'; import { FormTextInput } from '../../containers/TextInput';
import Navigation from '../../lib/navigation/appNavigation';
import { createDiscussionRequest, ICreateDiscussionRequestData } from '../../actions/createDiscussion'; import { createDiscussionRequest, ICreateDiscussionRequestData } from '../../actions/createDiscussion';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import { goRoom } from '../../lib/methods/helpers/goRoom'; import { goRoom } from '../../lib/methods/helpers/goRoom';
@ -60,18 +59,13 @@ class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreate
showErrorAlert(msg); showErrorAlert(msg);
} else { } else {
const { rid, t, prid } = result; const { rid, t, prid } = result;
if (isMasterDetail) {
Navigation.navigate('DrawerNavigator');
} else {
Navigation.navigate('RoomsListView');
}
const item = { const item = {
rid, rid,
name: getRoomTitle(result), name: getRoomTitle(result),
t, t,
prid prid
}; };
goRoom({ item, isMasterDetail }); goRoom({ item, isMasterDetail, popToRoot: true });
} }
} }
} }

View File

@ -152,13 +152,8 @@ class DirectoryView extends React.Component<IDirectoryViewProps, IDirectoryViewS
}; };
goRoom = (item: TGoRoomItem) => { goRoom = (item: TGoRoomItem) => {
const { navigation, isMasterDetail } = this.props; const { isMasterDetail } = this.props;
if (isMasterDetail) { goRoom({ item, isMasterDetail, popToRoot: true });
navigation.navigate('DrawerNavigator');
} else {
navigation.navigate('RoomsListView');
}
goRoom({ item, isMasterDetail });
}; };
onPressItem = async (item: IServerRoom) => { onPressItem = async (item: IServerRoom) => {

View File

@ -74,10 +74,7 @@ const NewMessageView = () => {
const goRoom = useCallback( const goRoom = useCallback(
(item: TGoRoomItem) => { (item: TGoRoomItem) => {
logEvent(events.NEW_MSG_CHAT_WITH_USER); logEvent(events.NEW_MSG_CHAT_WITH_USER);
navigation.pop();
if (isMasterDetail) {
navigation.pop();
}
goRoomMethod({ item, isMasterDetail }); goRoomMethod({ item, isMasterDetail });
}, },
[isMasterDetail, navigation] [isMasterDetail, navigation]

View File

@ -87,7 +87,7 @@ interface IRoomInfoViewProps {
StackNavigationProp<MasterDetailInsideStackParamList> StackNavigationProp<MasterDetailInsideStackParamList>
>; >;
route: RouteProp<ChatsStackParamList, 'RoomInfoView'>; route: RouteProp<ChatsStackParamList, 'RoomInfoView'>;
rooms: string[]; subscribedRoom: string;
theme: TSupportedThemes; theme: TSupportedThemes;
isMasterDetail: boolean; isMasterDetail: boolean;
jitsiEnabled: boolean; jitsiEnabled: boolean;
@ -353,7 +353,7 @@ class RoomInfoView extends React.Component<IRoomInfoViewProps, IRoomInfoViewStat
goRoom = () => { goRoom = () => {
logEvent(events.RI_GO_ROOM_USER); logEvent(events.RI_GO_ROOM_USER);
const { room } = this.state; const { room } = this.state;
const { rooms, navigation, isMasterDetail } = this.props; const { navigation, isMasterDetail, subscribedRoom } = this.props;
const params = { const params = {
rid: room.rid, rid: room.rid,
name: getRoomTitle(room), name: getRoomTitle(room),
@ -362,18 +362,14 @@ class RoomInfoView extends React.Component<IRoomInfoViewProps, IRoomInfoViewStat
}; };
if (room.rid) { if (room.rid) {
// if it's on master detail layout, we close the modal and replace RoomView if (room.rid === subscribedRoom) {
if (isMasterDetail) { if (isMasterDetail) {
Navigation.navigate('DrawerNavigator'); return Navigation.navigate('DrawerNavigator');
goRoom({ item: params, isMasterDetail });
} else {
let navigate = navigation.push;
// if this is a room focused
if (rooms.includes(room.rid)) {
({ navigate } = navigation);
} }
navigate('RoomView', params); return navigation.goBack();
} }
// if it's on master detail layout, we close the modal and replace RoomView
goRoom({ item: params, isMasterDetail, popToRoot: true });
} }
}; };
@ -513,7 +509,7 @@ class RoomInfoView extends React.Component<IRoomInfoViewProps, IRoomInfoViewStat
} }
const mapStateToProps = (state: IApplicationState) => ({ const mapStateToProps = (state: IApplicationState) => ({
rooms: state.room.rooms, subscribedRoom: state.room.subscribedRoom,
isMasterDetail: state.app.isMasterDetail, isMasterDetail: state.app.isMasterDetail,
jitsiEnabled: (state.settings.Jitsi_Enabled as boolean) || false, jitsiEnabled: (state.settings.Jitsi_Enabled as boolean) || false,
editRoomPermission: state.permissions['edit-room'], editRoomPermission: state.permissions['edit-room'],

View File

@ -15,12 +15,7 @@ import { RoomTypes } from '../../lib/methods';
export type TRoomType = SubscriptionType.CHANNEL | SubscriptionType.GROUP | SubscriptionType.OMNICHANNEL; export type TRoomType = SubscriptionType.CHANNEL | SubscriptionType.GROUP | SubscriptionType.OMNICHANNEL;
const handleGoRoom = (item: TGoRoomItem, isMasterDetail: boolean): void => { const handleGoRoom = (item: TGoRoomItem, isMasterDetail: boolean): void => {
if (isMasterDetail) { goRoom({ item, isMasterDetail, popToRoot: true });
appNavigation.navigate('DrawerNavigator');
} else {
appNavigation.popToTop();
}
goRoom({ item, isMasterDetail });
}; };
export const fetchRole = (role: string, selectedUser: TUserModel, roomRoles: any): boolean => { export const fetchRole = (role: string, selectedUser: TUserModel, roomRoles: any): boolean => {

View File

@ -43,7 +43,6 @@ import SafeAreaView from '../../containers/SafeAreaView';
import { withDimensions } from '../../dimensions'; import { withDimensions } from '../../dimensions';
import { takeInquiry, takeResume } from '../../ee/omnichannel/lib'; import { takeInquiry, takeResume } from '../../ee/omnichannel/lib';
import { sendLoadingEvent } from '../../containers/Loading'; import { sendLoadingEvent } from '../../containers/Loading';
import { goRoom, TGoRoomItem } from '../../lib/methods/helpers/goRoom';
import getThreadName from '../../lib/methods/getThreadName'; import getThreadName from '../../lib/methods/getThreadName';
import getRoomInfo from '../../lib/methods/getRoomInfo'; import getRoomInfo from '../../lib/methods/getRoomInfo';
import { ContainerTypes } from '../../containers/UIKit/interfaces'; import { ContainerTypes } from '../../containers/UIKit/interfaces';
@ -101,6 +100,7 @@ import {
} from '../../lib/methods/helpers'; } from '../../lib/methods/helpers';
import { Services } from '../../lib/services'; import { Services } from '../../lib/services';
import { withActionSheet, IActionSheetProvider } from '../../containers/ActionSheet'; import { withActionSheet, IActionSheetProvider } from '../../containers/ActionSheet';
import { goRoom, TGoRoomItem } from '../../lib/methods/helpers/goRoom';
type TStateAttrsUpdate = keyof IRoomViewState; type TStateAttrsUpdate = keyof IRoomViewState;
@ -910,15 +910,15 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
onDiscussionPress = debounce( onDiscussionPress = debounce(
async (item: TAnyMessageModel) => { async (item: TAnyMessageModel) => {
const { navigation } = this.props; const { isMasterDetail } = this.props;
if (!item.drid) return; if (!item.drid) return;
const sub = await getRoomInfo(item.drid); const sub = await getRoomInfo(item.drid);
navigation.push('RoomView', { if (sub) {
rid: item.drid as string, goRoom({
prid: item?.subscription?.id, item: sub as TGoRoomItem,
name: item.msg, isMasterDetail
t: (sub?.t as SubscriptionType) || (this.t as SubscriptionType) });
}); }
}, },
1000, 1000,
true true
@ -987,6 +987,11 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
} else { } else {
this.navToThread(message); this.navToThread(message);
} }
} else if (!message.tmid && message.rid === this.rid && this.t === 'thread' && !message.replies) {
/**
* if the user is within a thread and the message that he is trying to jump to, is a message in the main room
*/
return this.navToRoom(message);
} else { } else {
/** /**
* if it's from server, we don't have it saved locally and so we fetch surroundings * if it's from server, we don't have it saved locally and so we fetch surroundings
@ -1198,12 +1203,12 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
}; };
navToRoom = async (message: TAnyMessageModel) => { navToRoom = async (message: TAnyMessageModel) => {
const { navigation, isMasterDetail } = this.props; const { isMasterDetail } = this.props;
const roomInfo = await getRoomInfo(message.rid); const roomInfo = await getRoomInfo(message.rid);
return goRoom({ return goRoom({
item: roomInfo as TGoRoomItem, item: roomInfo as TGoRoomItem,
isMasterDetail, isMasterDetail,
navigationMethod: navigation.push,
jumpToMessageId: message.id jumpToMessageId: message.id
}); });
}; };

View File

@ -6,8 +6,10 @@ import Orientation from 'react-native-orientation-locker';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import { withSafeAreaInsets } from 'react-native-safe-area-context'; import { withSafeAreaInsets } from 'react-native-safe-area-context';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { StackNavigationOptions } from '@react-navigation/stack'; import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import { Header } from '@react-navigation/elements'; import { Header } from '@react-navigation/elements';
import { CompositeNavigationProp, RouteProp } from '@react-navigation/native';
import { Dispatch } from 'redux';
import database from '../../lib/database'; import database from '../../lib/database';
import RoomItem, { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from '../../containers/RoomItem'; import RoomItem, { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from '../../containers/RoomItem';
@ -20,7 +22,7 @@ import StatusBar from '../../containers/StatusBar';
import ActivityIndicator from '../../containers/ActivityIndicator'; import ActivityIndicator from '../../containers/ActivityIndicator';
import { serverInitAdd } from '../../actions/server'; import { serverInitAdd } from '../../actions/server';
import { animateNextTransition } from '../../lib/methods/helpers/layoutAnimation'; import { animateNextTransition } from '../../lib/methods/helpers/layoutAnimation';
import { withTheme } from '../../theme'; import { TSupportedThemes, withTheme } from '../../theme';
import EventEmitter from '../../lib/methods/helpers/events'; import EventEmitter from '../../lib/methods/helpers/events';
import { themedHeader } from '../../lib/methods/helpers/navigation'; import { themedHeader } from '../../lib/methods/helpers/navigation';
import { import {
@ -39,20 +41,12 @@ import { goRoom } from '../../lib/methods/helpers/goRoom';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import { withDimensions } from '../../dimensions'; import { withDimensions } from '../../dimensions';
import { getInquiryQueueSelector } from '../../ee/omnichannel/selectors/inquiry'; import { getInquiryQueueSelector } from '../../ee/omnichannel/selectors/inquiry';
import { import { IApplicationState, ISubscription, IUser, RootEnum, SubscriptionType, TSubscriptionModel } from '../../definitions';
IApplicationState,
IBaseScreen,
ISubscription,
IUser,
RootEnum,
SubscriptionType,
TSubscriptionModel
} from '../../definitions';
import styles from './styles'; import styles from './styles';
import ServerDropdown from './ServerDropdown'; import ServerDropdown from './ServerDropdown';
import ListHeader, { TEncryptionBanner } from './ListHeader'; import ListHeader, { TEncryptionBanner } from './ListHeader';
import RoomsListHeaderView from './Header'; import RoomsListHeaderView from './Header';
import { ChatsStackParamList } from '../../stacks/types'; import { ChatsStackParamList, DrawerParamList } from '../../stacks/types';
import { RoomTypes, search } from '../../lib/methods'; import { RoomTypes, search } from '../../lib/methods';
import { import {
getRoomAvatar, getRoomAvatar,
@ -67,7 +61,16 @@ import {
import { E2E_BANNER_TYPE, DisplayMode, SortBy, MAX_SIDEBAR_WIDTH, themes } from '../../lib/constants'; import { E2E_BANNER_TYPE, DisplayMode, SortBy, MAX_SIDEBAR_WIDTH, themes } from '../../lib/constants';
import { Services } from '../../lib/services'; import { Services } from '../../lib/services';
interface IRoomsListViewProps extends IBaseScreen<ChatsStackParamList, 'RoomsListView'> { type TNavigation = CompositeNavigationProp<
StackNavigationProp<ChatsStackParamList, 'RoomsListView'>,
CompositeNavigationProp<StackNavigationProp<ChatsStackParamList>, StackNavigationProp<DrawerParamList>>
>;
interface IRoomsListViewProps {
navigation: TNavigation;
route: RouteProp<ChatsStackParamList, 'RoomsListView'>;
theme: TSupportedThemes;
dispatch: Dispatch;
[key: string]: IUser | string | boolean | ISubscription[] | number | object | TEncryptionBanner; [key: string]: IUser | string | boolean | ISubscription[] | number | object | TEncryptionBanner;
user: IUser; user: IUser;
server: string; server: string;
@ -83,7 +86,7 @@ interface IRoomsListViewProps extends IBaseScreen<ChatsStackParamList, 'RoomsLis
StoreLastMessage: boolean; StoreLastMessage: boolean;
useRealName: boolean; useRealName: boolean;
isMasterDetail: boolean; isMasterDetail: boolean;
rooms: string[]; subscribedRoom: string;
width: number; width: number;
insets: { insets: {
left: number; left: number;
@ -304,7 +307,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
} }
const { loading, search } = this.state; const { loading, search } = this.state;
const { rooms, width, insets } = this.props; const { width, insets, subscribedRoom } = this.props;
if (nextState.loading !== loading) { if (nextState.loading !== loading) {
return true; return true;
} }
@ -314,7 +317,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
if (!dequal(nextState.search, search)) { if (!dequal(nextState.search, search)) {
return true; return true;
} }
if (!dequal(nextProps.rooms, rooms)) { if (nextProps.subscribedRoom !== subscribedRoom) {
return true; return true;
} }
if (!dequal(nextProps.insets, insets)) { if (!dequal(nextProps.insets, insets)) {
@ -334,7 +337,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
groupByType, groupByType,
showFavorites, showFavorites,
showUnread, showUnread,
rooms, subscribedRoom,
isMasterDetail, isMasterDetail,
insets, insets,
createTeamPermission, createTeamPermission,
@ -359,9 +362,9 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
) { ) {
this.getSubscriptions(); this.getSubscriptions();
} }
// Update current item in case of another action triggers an update on rooms reducer // Update current item in case of another action triggers an update on room subscribed reducer
if (isMasterDetail && rooms[0] && item?.rid !== rooms[0] && !dequal(rooms, prevProps.rooms)) { if (isMasterDetail && item?.rid !== subscribedRoom && subscribedRoom !== prevProps.subscribedRoom) {
this.setState({ item: { rid: rooms[0] } as ISubscription }); this.setState({ item: { rid: subscribedRoom } as ISubscription });
} }
if (insets.left !== prevProps.insets.left || insets.right !== prevProps.insets.right) { if (insets.left !== prevProps.insets.left || insets.right !== prevProps.insets.right) {
this.setHeader(); this.setHeader();
@ -765,9 +768,9 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
goRoom = ({ item, isMasterDetail }: { item: ISubscription; isMasterDetail: boolean }) => { goRoom = ({ item, isMasterDetail }: { item: ISubscription; isMasterDetail: boolean }) => {
logEvent(events.RL_GO_ROOM); logEvent(events.RL_GO_ROOM);
const { item: currentItem } = this.state; const { item: currentItem } = this.state;
const { rooms } = this.props; const { subscribedRoom } = this.props;
// @ts-ignore
if (currentItem?.rid === item.rid || rooms?.includes(item.rid)) { if (currentItem?.rid === item.rid || subscribedRoom === item.rid) {
return; return;
} }
// Only mark room as focused when in master detail layout // Only mark room as focused when in master detail layout
@ -1040,7 +1043,7 @@ const mapStateToProps = (state: IApplicationState) => ({
showUnread: state.sortPreferences.showUnread, showUnread: state.sortPreferences.showUnread,
useRealName: state.settings.UI_Use_Real_Name, useRealName: state.settings.UI_Use_Real_Name,
StoreLastMessage: state.settings.Store_Last_Message, StoreLastMessage: state.settings.Store_Last_Message,
rooms: state.room.rooms, subscribedRoom: state.room.subscribedRoom,
queueSize: getInquiryQueueSelector(state).length, queueSize: getInquiryQueueSelector(state).length,
inquiryEnabled: state.inquiry.enabled, inquiryEnabled: state.inquiry.enabled,
encryptionBanner: state.encryption.banner, encryptionBanner: state.encryption.banner,

View File

@ -322,7 +322,7 @@ class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChan
onPressItem = debounce( onPressItem = debounce(
async (item: IItem) => { async (item: IItem) => {
logEvent(events.TC_GO_ROOM); logEvent(events.TC_GO_ROOM);
const { navigation, isMasterDetail } = this.props; const { isMasterDetail } = this.props;
try { try {
let params = {}; let params = {};
const result = await Services.getRoomInfo(item._id); const result = await Services.getRoomInfo(item._id);
@ -335,10 +335,7 @@ class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChan
teamId: result.room.teamId teamId: result.room.teamId
}; };
} }
if (isMasterDetail) { goRoom({ item: params, isMasterDetail, popToRoot: !!isMasterDetail });
navigation.pop();
}
goRoom({ item: params, isMasterDetail, navigationMethod: navigation.push });
} catch (e: any) { } catch (e: any) {
if (e.data.error === 'not-allowed') { if (e.data.error === 'not-allowed') {
showErrorAlert(I18n.t('error-not-allowed')); showErrorAlert(I18n.t('error-not-allowed'));

View File

@ -0,0 +1,71 @@
import { expect } from 'detox';
import data from '../../data';
import { navigateToLogin, login, sleep, tapBack } from '../../helpers/app';
import { sendMessage, post } from '../../helpers/data_setup';
describe('InApp Notification', () => {
let dmCreatedRid: string;
before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
const result = await post(`im.create`, { username: data.users.alternate.username });
dmCreatedRid = result.data.room.rid;
});
describe('receive in RoomsListView', () => {
const text = 'Message in DM';
it('should have rooms list screen', async () => {
await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
it('should send direct message from user alternate to user regular', async () => {
await sleep(1000);
await sendMessage(data.users.alternate, dmCreatedRid, text);
});
it('should tap on InApp Notification', async () => {
await waitFor(element(by.id(`in-app-notification-${text}`)))
.toExist()
.withTimeout(2000);
await sleep(500);
await element(by.id(`in-app-notification-${text}`)).tap();
await sleep(500);
await expect(element(by.id('room-header'))).toExist();
await expect(element(by.id(`room-view-title-${data.users.alternate.username}`))).toExist();
});
});
describe('receive in another room', () => {
const text = 'Another msg';
it('should back to RoomsListView and open the channel Detox Public', async () => {
await tapBack();
await sleep(500);
await element(by.id(`rooms-list-view-item-${data.userRegularChannels.detoxpublic.name}`)).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
await expect(element(by.id(`room-view-title-${data.userRegularChannels.detoxpublic.name}`))).toExist();
});
it('should receive and tap InAppNotification in another room', async () => {
await sendMessage(data.users.alternate, dmCreatedRid, text);
await waitFor(element(by.id(`in-app-notification-${text}`)))
.toExist()
.withTimeout(2000);
await sleep(500);
await element(by.id(`in-app-notification-${text}`)).tap();
await sleep(500);
await expect(element(by.id('room-header'))).toExist();
await expect(element(by.id(`room-view-title-${data.users.alternate.username}`))).toExist();
});
it('should back to RoomsListView', async () => {
await tapBack();
await sleep(500);
await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
});
});

View File

@ -200,7 +200,7 @@ describe('Room', () => {
const expectThreadMessages = async (message: string) => { const expectThreadMessages = async (message: string) => {
await waitFor(element(by.id('room-view-title-thread 1'))) await waitFor(element(by.id('room-view-title-thread 1')))
.toExist() .toExist()
.withTimeout(5000); .withTimeout(10000);
await waitFor(element(by[textMatcher](message)).atIndex(0)) await waitFor(element(by[textMatcher](message)).atIndex(0))
.toExist() .toExist()
.withTimeout(10000); .withTimeout(10000);

View File

@ -199,6 +199,14 @@ describe('Team', () => {
}); });
it('should add existing channel to team', async () => { it('should add existing channel to team', async () => {
await navigateToRoom(team);
await waitFor(element(by.id('room-view-header-team-channels')))
.toExist()
.withTimeout(5000);
await element(by.id('room-view-header-team-channels')).tap();
await waitFor(element(by.id('team-channels-view')))
.toExist()
.withTimeout(5000);
await element(by.id('team-channels-view-create')).tap(); await element(by.id('team-channels-view-create')).tap();
await waitFor(element(by.id('add-channel-team-view'))) await waitFor(element(by.id('add-channel-team-view')))
.toExist() .toExist()