Active users improvements (#855)

This commit is contained in:
Diego Mello 2019-04-29 13:03:52 -03:00 committed by GitHub
parent d1b21a304f
commit 44f3b7f1a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 222 additions and 195 deletions

26
__mocks__/realm.js Normal file
View File

@ -0,0 +1,26 @@
export default class Realm {
schema = [];
data = [];
constructor(params) {
require('lodash').each(params.schema, (schema) => {
this.data[schema.name] = [];
this.data[schema.name].filtered = () => this.data[schema.name];
});
this.schema = params.schema;
}
objects(schemaName) {
return this.data[schemaName];
}
write = (fn) => {
fn();
}
create(schemaName, data) {
this.data[schemaName].push(data);
return data;
}
}

View File

@ -64,7 +64,6 @@ export const SERVER = createRequestTypes('SERVER', [
]); ]);
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT']); export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT']);
export const LOGOUT = 'LOGOUT'; // logout is always success export const LOGOUT = 'LOGOUT'; // logout is always success
export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET']);
export const SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']); export const SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']); export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']);
export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']); export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']);

View File

@ -1,8 +0,0 @@
import * as types from './actionsTypes';
export function setActiveUser(data) {
return {
type: types.ACTIVE_USERS.SET,
data
};
}

View File

@ -4,28 +4,16 @@ import { connect } from 'react-redux';
import { ViewPropTypes } from 'react-native'; import { ViewPropTypes } from 'react-native';
import Status from './Status'; import Status from './Status';
import database, { safeAddListener } from '../../lib/realm';
@connect((state, ownProps) => { @connect(state => ({
if (state.login.user && ownProps.id === state.login.user.id) {
return {
status: state.login.user && state.login.user.status,
offline: !state.meteor.connected offline: !state.meteor.connected
}; }))
}
const user = state.activeUsers[ownProps.id];
return {
status: (user && user.status) || 'offline'
};
})
export default class StatusContainer extends React.PureComponent { export default class StatusContainer extends React.PureComponent {
static propTypes = { static propTypes = {
// id is a prop, but it's used only inside @connect to find for current status id: PropTypes.string,
id: PropTypes.string, // eslint-disable-line
style: ViewPropTypes.style, style: ViewPropTypes.style,
size: PropTypes.number, size: PropTypes.number,
status: PropTypes.string,
offline: PropTypes.bool offline: PropTypes.bool
}; };
@ -33,12 +21,32 @@ export default class StatusContainer extends React.PureComponent {
size: 16 size: 16
} }
constructor(props) {
super(props);
this.user = database.memoryDatabase.objects('activeUsers').filtered('id == $0', props.id);
this.state = {
user: this.user[0] || {}
};
safeAddListener(this.user, this.updateState);
}
componentWillUnmount() {
this.user.removeAllListeners();
}
get status() { get status() {
const { offline, status } = this.props; const { user } = this.state;
if (offline) { const { offline } = this.props;
if (offline || !user) {
return 'offline'; return 'offline';
} }
return status; return user.status || 'offline';
}
updateState = () => {
if (this.user.length) {
this.setState({ user: this.user[0] });
}
} }
render() { render() {

View File

@ -331,6 +331,18 @@ const usersTypingSchema = {
} }
}; };
const activeUsersSchema = {
name: 'activeUsers',
primaryKey: 'id',
properties: {
id: 'string',
name: 'string?',
username: 'string?',
status: 'string?',
utcOffset: 'double?'
}
};
const schema = [ const schema = [
settingsSchema, settingsSchema,
subscriptionSchema, subscriptionSchema,
@ -353,7 +365,7 @@ const schema = [
uploadsSchema uploadsSchema
]; ];
const inMemorySchema = [usersTypingSchema]; const inMemorySchema = [usersTypingSchema, activeUsersSchema];
class DB { class DB {
databases = { databases = {
@ -362,9 +374,9 @@ class DB {
schema: [ schema: [
serversSchema serversSchema
], ],
schemaVersion: 5, schemaVersion: 6,
migration: (oldRealm, newRealm) => { migration: (oldRealm, newRealm) => {
if (oldRealm.schemaVersion >= 1 && newRealm.schemaVersion <= 5) { if (oldRealm.schemaVersion >= 1 && newRealm.schemaVersion <= 6) {
const newServers = newRealm.objects('servers'); const newServers = newRealm.objects('servers');
// eslint-disable-next-line no-plusplus // eslint-disable-next-line no-plusplus
@ -377,7 +389,7 @@ class DB {
inMemoryDB: new Realm({ inMemoryDB: new Realm({
path: 'memory.realm', path: 'memory.realm',
schema: inMemorySchema, schema: inMemorySchema,
schemaVersion: 1, schemaVersion: 2,
inMemory: true inMemory: true
}) })
} }

View File

@ -14,7 +14,6 @@ import {
setUser, setLoginServices, loginRequest, loginFailure, logout setUser, setLoginServices, loginRequest, loginFailure, logout
} from '../actions/login'; } from '../actions/login';
import { disconnect, connectSuccess, connectRequest } from '../actions/connect'; import { disconnect, connectSuccess, connectRequest } from '../actions/connect';
import { setActiveUser } from '../actions/activeUsers';
import subscribeRooms from './methods/subscriptions/rooms'; import subscribeRooms from './methods/subscriptions/rooms';
import subscribeRoom from './methods/subscriptions/room'; import subscribeRoom from './methods/subscriptions/room';
@ -119,18 +118,40 @@ const RocketChat = {
this._setUserTimer = setTimeout(() => { this._setUserTimer = setTimeout(() => {
const batchUsers = this.activeUsers; const batchUsers = this.activeUsers;
InteractionManager.runAfterInteractions(() => { InteractionManager.runAfterInteractions(() => {
reduxStore.dispatch(setActiveUser(batchUsers)); database.memoryDatabase.write(() => {
Object.keys(batchUsers).forEach((key) => {
if (batchUsers[key] && batchUsers[key].id) {
try {
const data = batchUsers[key];
if (data.removed) {
const userRecord = database.memoryDatabase.objectForPrimaryKey('activeUsers', data.id);
if (userRecord) {
userRecord.status = 'offline';
}
} else {
database.memoryDatabase.create('activeUsers', data, true);
}
} catch (error) {
console.log(error);
}
}
});
});
}); });
this._setUserTimer = null; this._setUserTimer = null;
return this.activeUsers = {}; return this.activeUsers = {};
}, 10000); }, 10000);
} }
const activeUser = reduxStore.getState().activeUsers[ddpMessage.id];
if (!ddpMessage.fields) { if (!ddpMessage.fields) {
this.activeUsers[ddpMessage.id] = {}; this.activeUsers[ddpMessage.id] = {
id: ddpMessage.id,
removed: true
};
} else { } else {
this.activeUsers[ddpMessage.id] = { ...this.activeUsers[ddpMessage.id], ...activeUser, ...ddpMessage.fields }; this.activeUsers[ddpMessage.id] = {
id: ddpMessage.id, ...this.activeUsers[ddpMessage.id], ...ddpMessage.fields
};
} }
}, },
async loginSuccess({ user }) { async loginSuccess({ user }) {
@ -559,16 +580,15 @@ const RocketChat = {
// RC 0.48.0 // RC 0.48.0
return this.sdk.get('channels.info', { roomId }); return this.sdk.get('channels.info', { roomId });
}, },
async getRoomMember(rid, currentUserId) { getUserInfo(userId) {
try { // RC 0.48.0
return this.sdk.get('users.info', { userId });
},
getRoomMemberId(rid, currentUserId) {
if (rid === `${ currentUserId }${ currentUserId }`) { if (rid === `${ currentUserId }${ currentUserId }`) {
return Promise.resolve(currentUserId); return currentUserId;
}
const membersResult = await RocketChat.getRoomMembers(rid, true);
return Promise.resolve(membersResult.records.find(m => m._id !== currentUserId));
} catch (error) {
return Promise.reject(error);
} }
return rid.replace(currentUserId, '').trim();
}, },
toggleBlockUser(rid, blocked, block) { toggleBlockUser(rid, blocked, block) {
if (block) { if (block) {

View File

@ -1,15 +0,0 @@
import * as types from '../actions/actionsTypes';
const initialState = {};
export default (state = initialState, action) => {
switch (action.type) {
case types.ACTIVE_USERS.SET:
return {
...state,
...action.data
};
default:
return state;
}
};

View File

@ -9,7 +9,6 @@ import selectedUsers from './selectedUsers';
import createChannel from './createChannel'; import createChannel from './createChannel';
import app from './app'; import app from './app';
import customEmojis from './customEmojis'; import customEmojis from './customEmojis';
import activeUsers from './activeUsers';
import sortPreferences from './sortPreferences'; import sortPreferences from './sortPreferences';
export default combineReducers({ export default combineReducers({
@ -23,6 +22,5 @@ export default combineReducers({
app, app,
rooms, rooms,
customEmojis, customEmojis,
activeUsers,
sortPreferences sortPreferences
}); });

View File

@ -331,8 +331,11 @@ export default class RoomActionsView extends LoggedView {
const { user } = this.props; const { user } = this.props;
try { try {
const member = await RocketChat.getRoomMember(rid, user.id); const roomUserId = RocketChat.getRoomMemberId(rid, user.id);
this.setState({ member: member || {} }); const result = await RocketChat.getUserInfo(roomUserId);
if (result.success) {
this.setState({ member: result.user });
}
} catch (e) { } catch (e) {
log('RoomActions updateRoomMember', e); log('RoomActions updateRoomMember', e);
this.setState({ member: {} }); this.setState({ member: {} });
@ -400,7 +403,7 @@ export default class RoomActionsView extends LoggedView {
userId={user.id} userId={user.id}
token={user.token} token={user.token}
> >
{t === 'd' ? <Status style={sharedStyles.status} id={member._id} /> : null } {t === 'd' && member._id ? <Status style={sharedStyles.status} id={member._id} /> : null }
</Avatar>, </Avatar>,
<View key='name' style={styles.roomTitleContainer}> <View key='name' style={styles.roomTitleContainer}>
{room.t === 'd' {room.t === 'd'

View File

@ -4,7 +4,6 @@ import { View, Text, ScrollView } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import moment from 'moment'; import moment from 'moment';
import { SafeAreaView } from 'react-navigation'; import { SafeAreaView } from 'react-navigation';
import equal from 'deep-equal';
import LoggedView from '../View'; import LoggedView from '../View';
import Status from '../../containers/Status'; import Status from '../../containers/Status';
@ -13,11 +12,11 @@ import styles from './styles';
import sharedStyles from '../Styles'; import sharedStyles from '../Styles';
import database, { safeAddListener } from '../../lib/realm'; import database, { safeAddListener } from '../../lib/realm';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import log from '../../utils/log';
import RoomTypeIcon from '../../containers/RoomTypeIcon'; import RoomTypeIcon from '../../containers/RoomTypeIcon';
import I18n from '../../i18n'; import I18n from '../../i18n';
import { CustomHeaderButtons, Item } from '../../containers/HeaderButton'; import { CustomHeaderButtons, Item } from '../../containers/HeaderButton';
import StatusBar from '../../containers/StatusBar'; import StatusBar from '../../containers/StatusBar';
import log from '../../utils/log';
const PERMISSION_EDIT_ROOM = 'edit-room'; const PERMISSION_EDIT_ROOM = 'edit-room';
@ -38,7 +37,6 @@ const getRoomTitle = room => (room.t === 'd'
id: state.login.user && state.login.user.id, id: state.login.user && state.login.user.id,
token: state.login.user && state.login.user.token token: state.login.user && state.login.user.token
}, },
activeUsers: state.activeUsers, // TODO: remove it
Message_TimeFormat: state.settings.Message_TimeFormat Message_TimeFormat: state.settings.Message_TimeFormat
})) }))
/** @extends React.Component */ /** @extends React.Component */
@ -65,22 +63,22 @@ export default class RoomInfoView extends LoggedView {
token: PropTypes.string token: PropTypes.string
}), }),
baseUrl: PropTypes.string, baseUrl: PropTypes.string,
activeUsers: PropTypes.object,
Message_TimeFormat: PropTypes.string Message_TimeFormat: PropTypes.string
} }
constructor(props) { constructor(props) {
super('RoomInfoView', props); super('RoomInfoView', props);
const rid = props.navigation.getParam('rid'); this.rid = props.navigation.getParam('rid');
const room = props.navigation.getParam('room'); const room = props.navigation.getParam('room');
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid); this.t = props.navigation.getParam('t');
this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid);
this.roles = database.objects('roles');
this.sub = { this.sub = {
unsubscribe: () => {} unsubscribe: () => {}
}; };
this.state = { this.state = {
room: this.rooms[0] || room || {}, room: this.rooms[0] || room || {},
roomUser: {}, roomUser: {}
roles: []
}; };
} }
@ -93,70 +91,22 @@ export default class RoomInfoView extends LoggedView {
navigation.setParams({ showEdit: true }); navigation.setParams({ showEdit: true });
} }
// get user of room if (this.t === 'd') {
if (room) { const { user } = this.props;
if (room.t === 'd') { const roomUserId = RocketChat.getRoomMemberId(this.rid, user.id);
try { try {
const { user, activeUsers } = this.props; const result = await RocketChat.getUserInfo(roomUserId);
const roomUser = await RocketChat.getRoomMember(room.rid, user.id); if (result.success) {
this.setState({ roomUser: roomUser || {} }); this.setState({ roomUser: result.user });
const username = room.name;
const activeUser = activeUsers[roomUser._id];
if (!activeUser || !activeUser.utcOffset) {
// get full user data looking for utcOffset
// will be catched by .on('users) and saved on activeUsers reducer
this.getFullUserData(username);
} }
} catch (error) {
// get all users roles log('RoomInfoView.getUserInfo', error);
// needs to be changed by a better method
const allUsersRoles = await RocketChat.getUserRoles();
const userRoles = allUsersRoles.find(u => u.username === username);
if (userRoles) {
this.setState({ roles: userRoles.roles || [] });
}
} catch (e) {
log('RoomInfoView.componentDidMount', e);
} }
} }
} }
}
shouldComponentUpdate(nextProps, nextState) {
const {
room, roomUser, roles
} = this.state;
const { activeUsers } = this.props;
if (!equal(nextState.room, room)) {
return true;
}
if (!equal(nextState.roomUser, roomUser)) {
return true;
}
if (!equal(nextState.roles, roles)) {
return true;
}
if (roomUser._id) {
if (nextProps.activeUsers[roomUser._id] !== activeUsers[roomUser._id]) {
return true;
}
}
return false;
}
componentWillUnmount() { componentWillUnmount() {
this.rooms.removeAllListeners(); this.rooms.removeAllListeners();
this.sub.unsubscribe();
}
getFullUserData = async(username) => {
try {
const result = await RocketChat.subscribe('fullUserData', username);
this.sub = result;
} catch (e) {
log('getFullUserData', e);
}
} }
getRoleDescription = (id) => { getRoleDescription = (id) => {
@ -189,32 +139,47 @@ export default class RoomInfoView extends LoggedView {
</View> </View>
); );
renderRoles = () => { getRoleDescription = (id) => {
const { roles } = this.state; const role = database.objectForPrimaryKey('roles', id);
if (role) {
return role.description;
}
return null;
}
renderRole = (role) => {
const description = this.getRoleDescription(role);
if (description) {
return ( return (
roles.length > 0
? (
<View style={styles.item}>
<Text style={styles.itemLabel}>{I18n.t('Roles')}</Text>
<View style={styles.rolesContainer}>
{roles.map(role => (
<View style={styles.roleBadge} key={role}> <View style={styles.roleBadge} key={role}>
<Text style={styles.role}>{ this.getRoleDescription(role) }</Text> <Text style={styles.role}>{ this.getRoleDescription(role) }</Text>
</View> </View>
))}
</View>
</View>
)
: null
); );
} }
return null;
}
renderTimezone = (userId) => { renderRoles = () => {
const { activeUsers, Message_TimeFormat } = this.props; const { roomUser } = this.state;
if (roomUser && roomUser.roles && roomUser.roles.length) {
return (
<View style={styles.item}>
<Text style={styles.itemLabel}>{I18n.t('Roles')}</Text>
<View style={styles.rolesContainer}>
{roomUser.roles.map(role => this.renderRole(role))}
</View>
</View>
);
}
return null;
}
if (activeUsers[userId]) { renderTimezone = () => {
const { utcOffset } = activeUsers[userId]; const { roomUser } = this.state;
const { Message_TimeFormat } = this.props;
if (roomUser) {
const { utcOffset } = roomUser;
if (!utcOffset) { if (!utcOffset) {
return null; return null;
@ -242,7 +207,7 @@ export default class RoomInfoView extends LoggedView {
userId={user.id} userId={user.id}
token={user.token} token={user.token}
> >
{room.t === 'd' ? <Status style={[sharedStyles.status, styles.status]} size={24} id={roomUser._id} /> : null} {room.t === 'd' && roomUser._id ? <Status style={[sharedStyles.status, styles.status]} size={24} id={roomUser._id} /> : null}
</Avatar> </Avatar>
); );
} }
@ -258,12 +223,12 @@ export default class RoomInfoView extends LoggedView {
</View> </View>
) )
renderCustomFields = (userId) => { renderCustomFields = () => {
const { activeUsers } = this.props; const { roomUser } = this.state;
if (activeUsers[userId]) { if (roomUser) {
const { customFields } = activeUsers[userId]; const { customFields } = roomUser;
if (!customFields) { if (!roomUser.customFields) {
return null; return null;
} }
@ -301,7 +266,7 @@ export default class RoomInfoView extends LoggedView {
{!this.isDirect() ? this.renderItem('topic', room) : null} {!this.isDirect() ? this.renderItem('topic', room) : null}
{!this.isDirect() ? this.renderItem('announcement', room) : null} {!this.isDirect() ? this.renderItem('announcement', room) : null}
{this.isDirect() ? this.renderRoles() : null} {this.isDirect() ? this.renderRoles() : null}
{this.isDirect() ? this.renderTimezone(roomUser._id) : null} {this.isDirect() ? this.renderTimezone() : null}
{this.isDirect() ? this.renderCustomFields(roomUser._id) : null} {this.isDirect() ? this.renderCustomFields(roomUser._id) : null}
{room.broadcast ? this.renderBroadcast() : null} {room.broadcast ? this.renderBroadcast() : null}
</SafeAreaView> </SafeAreaView>

View File

@ -4,28 +4,30 @@ import { connect } from 'react-redux';
import { responsive } from 'react-native-responsive-ui'; import { responsive } from 'react-native-responsive-ui';
import equal from 'deep-equal'; import equal from 'deep-equal';
import database from '../../../lib/realm'; import database, { safeAddListener } from '../../../lib/realm';
import Header from './Header'; import Header from './Header';
import RightButtons from './RightButtons'; import RightButtons from './RightButtons';
@responsive @responsive
@connect((state, ownProps) => { @connect((state, ownProps) => {
let status = ''; let status;
let userId;
let isLoggedUser = false;
const { rid, type } = ownProps; const { rid, type } = ownProps;
if (type === 'd') { if (type === 'd') {
if (state.login.user && state.login.user.id) { if (state.login.user && state.login.user.id) {
const { id: loggedUserId } = state.login.user; const { id: loggedUserId } = state.login.user;
const userId = rid.replace(loggedUserId, '').trim(); userId = rid.replace(loggedUserId, '').trim();
if (userId === loggedUserId) { isLoggedUser = userId === loggedUserId;
if (isLoggedUser) {
status = state.login.user.status; // eslint-disable-line status = state.login.user.status; // eslint-disable-line
} else {
const user = state.activeUsers[userId];
status = (user && user.status) || 'offline';
} }
} }
} }
return { return {
userId,
isLoggedUser,
status status
}; };
}) })
@ -38,20 +40,28 @@ export default class RoomHeaderView extends Component {
rid: PropTypes.string, rid: PropTypes.string,
window: PropTypes.object, window: PropTypes.object,
status: PropTypes.string, status: PropTypes.string,
widthOffset: PropTypes.number widthOffset: PropTypes.number,
isLoggedUser: PropTypes.bool,
userId: PropTypes.string
}; };
constructor(props) { constructor(props) {
super(props); super(props);
this.usersTyping = database.memoryDatabase.objects('usersTyping').filtered('rid = $0', props.rid); this.usersTyping = database.memoryDatabase.objects('usersTyping').filtered('rid = $0', props.rid);
this.user = [];
if (props.type === 'd' && !props.isLoggedUser) {
this.user = database.memoryDatabase.objects('activeUsers').filtered('id == $0', props.userId);
safeAddListener(this.user, this.updateUser);
}
this.state = { this.state = {
usersTyping: this.usersTyping.slice() || [] usersTyping: this.usersTyping.slice() || [],
user: this.user[0] || {}
}; };
this.usersTyping.addListener(this.updateState); this.usersTyping.addListener(this.updateState);
} }
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps, nextState) {
const { usersTyping } = this.state; const { usersTyping, user } = this.state;
const { const {
type, title, status, window type, title, status, window
} = this.props; } = this.props;
@ -73,27 +83,36 @@ export default class RoomHeaderView extends Component {
if (!equal(nextState.usersTyping, usersTyping)) { if (!equal(nextState.usersTyping, usersTyping)) {
return true; return true;
} }
if (!equal(nextState.user, user)) {
return true;
}
return false; return false;
} }
// componentDidUpdate(prevProps) {
// if (isIOS) {
// const { usersTyping } = this.props;
// if (!equal(prevProps.usersTyping, usersTyping)) {
// LayoutAnimation.easeInEaseOut();
// }
// }
// }
updateState = () => { updateState = () => {
this.setState({ usersTyping: this.usersTyping.slice() }); this.setState({ usersTyping: this.usersTyping.slice() });
} }
updateUser = () => {
if (this.user.length) {
this.setState({ user: this.user[0] });
}
}
render() { render() {
const { usersTyping } = this.state; const { usersTyping, user } = this.state;
const { const {
window, title, type, status, prid, tmid, widthOffset window, title, type, prid, tmid, widthOffset, isLoggedUser, status: userStatus
} = this.props; } = this.props;
let status = 'offline';
if (type === 'd') {
if (isLoggedUser) {
status = userStatus;
} else {
status = user.status || 'offline';
}
}
return ( return (
<Header <Header

View File

@ -29,22 +29,22 @@ describe('Legal screen', () => {
}); });
describe('Usage', async() => { describe('Usage', async() => {
it('should navigate to terms', async() => { // We can't simulate how webview behaves, so I had to disable :(
await element(by.id('legal-terms-button')).tap(); // it('should navigate to terms', async() => {
await waitFor(element(by.id('terms-view'))).toBeVisible().withTimeout(2000); // await element(by.id('legal-terms-button')).tap();
await expect(element(by.id('terms-view'))).toBeVisible(); // await waitFor(element(by.id('terms-view'))).toBeVisible().withTimeout(2000);
}); // await expect(element(by.id('terms-view'))).toBeVisible();
// });
it('should navigate to privacy', async() => { // it('should navigate to privacy', async() => {
await tapBack(); // await tapBack();
await element(by.id('legal-privacy-button')).tap(); // await element(by.id('legal-privacy-button')).tap();
await waitFor(element(by.id('privacy-view'))).toBeVisible().withTimeout(2000); // await waitFor(element(by.id('privacy-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('privacy-view'))).toBeVisible(); // await expect(element(by.id('privacy-view'))).toBeVisible();
}); // });
it('should navigate to welcome', async() => { it('should navigate to welcome', async() => {
await tapBack(); await tapBack();
await element(by.id('legal-view-close')).tap();
await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000); await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('welcome-view'))).toBeVisible(); await expect(element(by.id('welcome-view'))).toBeVisible();
}); });