diff --git a/app/actions/helpers/channels.ts b/app/actions/helpers/channels.ts index 21b80b0b9..cc332e1a5 100644 --- a/app/actions/helpers/channels.ts +++ b/app/actions/helpers/channels.ts @@ -368,11 +368,15 @@ async function getProfilesFromPromises(promises: Array>): return null; } - const result = await Promise.all(promises); - const data = result.filter((p: any) => !p.error); + try { + const result = await Promise.all(promises); + const data = result.filter((p: any) => !p.error); - return { - type: UserTypes.RECEIVED_BATCHED_PROFILES_IN_CHANNEL, - data, - }; + return { + type: UserTypes.RECEIVED_BATCHED_PROFILES_IN_CHANNEL, + data, + }; + } catch { + return null; + } } \ No newline at end of file diff --git a/app/actions/views/channel.js b/app/actions/views/channel.js index 6ff23511a..e34929147 100644 --- a/app/actions/views/channel.js +++ b/app/actions/views/channel.js @@ -26,7 +26,7 @@ import { import {getCurrentUserId} from '@mm-redux/selectors/entities/users'; import {getTeamByName} from '@mm-redux/selectors/entities/teams'; -import {getChannelByName as selectChannelByName} from '@mm-redux/utils/channel_utils'; +import {getChannelByName as selectChannelByName, getChannelsIdForTeam} from '@mm-redux/utils/channel_utils'; import EventEmitter from '@mm-redux/utils/event_emitter'; import {loadSidebarDirectMessagesProfiles} from '@actions/helpers/channels'; @@ -224,10 +224,15 @@ export function handleSelectChannel(channelId) { const channel = channels[channelId]; const member = myMembers[channelId]; - dispatch(loadPostsIfNecessaryWithRetry(channelId)); + if (channel) { + dispatch(loadPostsIfNecessaryWithRetry(channelId)); - if (channel && currentChannelId !== channelId) { - const actions = markAsViewedAndReadBatch(state, channelId, currentChannelId); + let previousChannelId = null; + if (currentChannelId !== channelId) { + previousChannelId = currentChannelId; + } + + const actions = markAsViewedAndReadBatch(state, channelId, previousChannelId); actions.push({ type: ChannelTypes.SELECT_CHANNEL, data: channelId, @@ -237,10 +242,11 @@ export function handleSelectChannel(channelId) { teamId: channel.team_id || currentTeamId, }, }); - dispatch(batchActions(actions, 'BATCH_SWITCH_CHANNEL')); - } - console.log('channel switch to', channel?.display_name, channelId, (Date.now() - dt), 'ms'); //eslint-disable-line + dispatch(batchActions(actions, 'BATCH_SWITCH_CHANNEL')); + + console.log('channel switch to', channel?.display_name, channelId, (Date.now() - dt), 'ms'); //eslint-disable-line + } }; } @@ -321,7 +327,9 @@ export function markAsViewedAndReadBatch(state, channelId, prevChannelId = '', m const prevChannel = (!prevChanManuallyUnread && prevChannelId) ? channels[prevChannelId] : null; // May be null since prevChannelId is optional if (markOnServer) { - Client4.viewMyChannel(channelId, prevChanManuallyUnread ? '' : prevChannelId); + Client4.viewMyChannel(channelId, prevChanManuallyUnread ? '' : prevChannelId).catch(() => { + // do nothing just adding the handler to avoid the warning + }); } if (member) { @@ -600,7 +608,12 @@ export function loadChannelsForTeam(teamId, skipDispatch = false) { return async (dispatch, getState) => { const state = getState(); const currentUserId = getCurrentUserId(state); - const data = {sync: true, teamId}; + const data = { + sync: true, + teamId, + teamChannels: getChannelsIdForTeam(state, teamId), + }; + const actions = []; if (currentUserId) { @@ -639,12 +652,17 @@ export function loadChannelsForTeam(teamId, skipDispatch = false) { } if (rolesToLoad.size > 0) { - data.roles = await Client4.getRolesByNames(Array.from(rolesToLoad)); - if (data.roles.length) { - actions.push({ - type: RoleTypes.RECEIVED_ROLES, - data: data.roles, - }); + try { + data.roles = await Client4.getRolesByNames(Array.from(rolesToLoad)); + if (data.roles.length) { + actions.push({ + type: RoleTypes.RECEIVED_ROLES, + data: data.roles, + }); + } + } catch { + //eslint-disable-next-line no-console + console.log('Could not retrieve channel members roles for the user'); } } diff --git a/app/actions/views/channel.test.js b/app/actions/views/channel.test.js index c89a05ad8..0f013651f 100644 --- a/app/actions/views/channel.test.js +++ b/app/actions/views/channel.test.js @@ -320,7 +320,7 @@ describe('Actions.Views.Channel', () => { teamId: currentTeamId, }, }; - if (channelId.includes('not') || channelId === currentChannelId) { + if (channelId.includes('not')) { expect(selectChannelWithMember).toBe(undefined); } else { expect(selectChannelWithMember).toStrictEqual(expectedSelectChannelWithMember); diff --git a/app/actions/views/post.js b/app/actions/views/post.js index d97054434..a01451f2b 100644 --- a/app/actions/views/post.js +++ b/app/actions/views/post.js @@ -87,7 +87,10 @@ export function getPosts(channelId, page = 0, perPage = Posts.POST_CHUNK_SIZE) { const postForChannel = postsInChannel[channelId]; const data = await Client4.getPosts(channelId, page, perPage); const posts = Object.values(data.posts); - const actions = []; + const actions = [{ + type: ViewTypes.SET_CHANNEL_RETRY_FAILED, + failed: false, + }]; if (posts?.length) { actions.push(receivedPosts(data)); diff --git a/app/actions/views/select_team.js b/app/actions/views/select_team.js index 1c94b4a69..fc4019cb6 100644 --- a/app/actions/views/select_team.js +++ b/app/actions/views/select_team.js @@ -32,10 +32,16 @@ export function selectDefaultTeam() { const state = getState(); const {ExperimentalPrimaryTeam} = getConfig(state); - const {teams: allTeams, myMembers} = state.entities.teams; - const teams = Object.keys(myMembers).map((key) => allTeams[key]); + const {teams, myMembers} = state.entities.teams; + const myTeams = Object.keys(teams).reduce((result, id) => { + if (myMembers[id]) { + result.push(teams[id]); + } - let defaultTeam = selectFirstAvailableTeam(teams, ExperimentalPrimaryTeam); + return result; + }, []); + + let defaultTeam = selectFirstAvailableTeam(myTeams, ExperimentalPrimaryTeam); if (defaultTeam) { dispatch(handleTeamChange(defaultTeam.id)); diff --git a/app/actions/views/user.js b/app/actions/views/user.js index 343303fc5..734a27307 100644 --- a/app/actions/views/user.js +++ b/app/actions/views/user.js @@ -183,13 +183,17 @@ export function login(loginId, password, mfaToken, ldapOnly = false) { } export function ssoLogin(token) { - return async (dispatch) => { + return async (dispatch, getState) => { + const state = getState(); + const deviceToken = state.entities?.general?.deviceToken; + Client4.setToken(token); await setCSRFFromCookie(Client4.getUrl()); + const result = await dispatch(loadMe()); if (!result.error) { - dispatch(completeLogin(result.data.user)); + dispatch(completeLogin(result.data.user, deviceToken)); } return result; diff --git a/app/components/failed_network_action/index.js b/app/components/failed_network_action/index.js index 4aada5e00..d601237ae 100644 --- a/app/components/failed_network_action/index.js +++ b/app/components/failed_network_action/index.js @@ -3,39 +3,48 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; -import {View} from 'react-native'; +import {Text, View} from 'react-native'; +import {intlShape} from 'react-intl'; +import Button from 'react-native-button'; -import FormattedText from 'app/components/formatted_text'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import {t} from 'app/utils/i18n'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import Cloud from './cloud'; export default class FailedNetworkAction extends PureComponent { static propTypes = { onRetry: PropTypes.func.isRequired, - actionId: PropTypes.string, - actionDefaultMessage: PropTypes.string, - errorId: PropTypes.string, - errorDefaultMessage: PropTypes.string, + actionText: PropTypes.string, + errorMessage: PropTypes.string, + errorTitle: PropTypes.string, theme: PropTypes.object.isRequired, }; + static contextTypes = { + intl: intlShape.isRequired, + }; + static defaultProps = { - actionId: t('mobile.failed_network_action.retry'), - actionDefaultMessage: 'try again', - errorId: t('mobile.failed_network_action.shortDescription'), - errorDefaultMessage: 'Messages will load when you have an internet connection or {tryAgainAction}.', + showAction: true, }; render() { - const {actionId, actionDefaultMessage, errorId, errorDefaultMessage, onRetry, theme} = this.props; + const {formatMessage} = this.context.intl; + const {onRetry, theme} = this.props; const style = getStyleFromTheme(theme); - const errorTitle = { - id: t('mobile.failed_network_action.title'), + const actionText = this.props.actionText || formatMessage({ + id: 'mobile.failed_network_action.retry', + defaultMessage: 'Try again', + }); + const errorTitle = this.props.errorTitle || formatMessage({ + id: 'mobile.failed_network_action.title', defaultMessage: 'No internet connection', - }; + }); + const errorMessage = this.props.errorMessage || formatMessage({ + id: 'mobile.failed_network_action.shortDescription', + defaultMessage: 'Messages will load when you have an internet connection.', + }); return ( @@ -44,26 +53,14 @@ export default class FailedNetworkAction extends PureComponent { height={76} width={76} /> - - - ), - }} - /> + {errorTitle} + {errorMessage} + ); } @@ -92,7 +89,16 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => { textAlign: 'center', }, link: { - color: theme.linkColor, + color: theme.buttonColor, + fontSize: 15, + }, + buttonContainer: { + backgroundColor: theme.buttonBg, + borderRadius: 5, + height: 42, + justifyContent: 'center', + marginTop: 20, + paddingHorizontal: 12, }, }; }); diff --git a/app/components/root/root.js b/app/components/root/root.js index da1fd0480..c24ca486e 100644 --- a/app/components/root/root.js +++ b/app/components/root/root.js @@ -68,7 +68,7 @@ export default class Root extends PureComponent { const {currentUrl, theme} = this.props; const {intl} = this.providerRef.getChildContext(); - let passProps = null; + let passProps = {theme}; const options = {topBar: {}}; if (Platform.OS === 'android') { options.topBar.rightButtons = [{ @@ -87,6 +87,7 @@ export default class Root extends PureComponent { if (screen === 'SelectTeam') { passProps = { + ...passProps, currentUrl, userWithoutTeams: true, }; diff --git a/app/components/sidebars/main/main_sidebar.ios.js b/app/components/sidebars/main/main_sidebar.ios.js index e38342476..0e9be4762 100644 --- a/app/components/sidebars/main/main_sidebar.ios.js +++ b/app/components/sidebars/main/main_sidebar.ios.js @@ -113,11 +113,15 @@ export default class MainSidebarIOS extends MainSidebarBase { }; render() { - const {children} = this.props; + const {children, currentUserId} = this.props; const {deviceWidth, openDrawerOffset} = this.state; const isTablet = DeviceTypes.IS_TABLET && !this.state.isSplitView && this.state.permanentSidebar; const drawerWidth = DeviceTypes.IS_TABLET ? TABLET_WIDTH : (deviceWidth - openDrawerOffset); + if (!currentUserId) { + return null; + } + return ( { const {logChannelSwitch, handleSelectChannel} = this.props.actions; + if (channel.id === currentChannelId) { + return; + } + logChannelSwitch(channel.id, currentChannelId); tracker.channelSwitch = Date.now(); diff --git a/app/components/team_icon/index.js b/app/components/team_icon/index.js index dc980c565..3fe01c1f5 100644 --- a/app/components/team_icon/index.js +++ b/app/components/team_icon/index.js @@ -10,10 +10,10 @@ import TeamIcon from './team_icon'; function mapStateToProps(state, ownProps) { const team = getTeam(state, ownProps.teamId); - const lastIconUpdate = team.last_team_icon_update; + const lastIconUpdate = team?.last_team_icon_update; return { - displayName: team.display_name, + displayName: team?.display_name, lastIconUpdate, theme: getTheme(state), }; diff --git a/app/components/team_icon/team_icon.js b/app/components/team_icon/team_icon.js index 7cdd5ad91..3365294e6 100644 --- a/app/components/team_icon/team_icon.js +++ b/app/components/team_icon/team_icon.js @@ -87,7 +87,7 @@ export default class TeamIcon extends React.PureComponent { } else { teamIconContent = ( - {displayName.substr(0, 2).toUpperCase()} + {displayName?.substr(0, 2).toUpperCase()} ); } diff --git a/app/mm-redux/reducers/entities/channels.ts b/app/mm-redux/reducers/entities/channels.ts index 77ff46909..97136979f 100644 --- a/app/mm-redux/reducers/entities/channels.ts +++ b/app/mm-redux/reducers/entities/channels.ts @@ -365,14 +365,14 @@ function myMembers(state: RelationOneToOne = {}, act case ChannelTypes.RECEIVED_MY_CHANNELS_WITH_MEMBERS: { // Used by the mobile app const nextState: any = {...state}; const current = Object.values(nextState); - const {sync, channelMembers} = action.data; + const {sync, teamChannels, channelMembers} = action.data; let hasNewValues = channelMembers && channelMembers.length > 0; // Remove existing channel memberships when the user is no longer a member if (sync) { current.forEach((member: ChannelMembership) => { const id = member.channel_id; - if (channelMembers.find((cm: ChannelMembership) => cm.channel_id !== id)) { + if (channelMembers.find((cm: ChannelMembership) => cm.channel_id !== id && teamChannels.includes(id))) { delete nextState[id]; hasNewValues = true; } diff --git a/app/mm-redux/reducers/errors/index.ts b/app/mm-redux/reducers/errors/index.ts index 168df9a42..9a3c685e4 100644 --- a/app/mm-redux/reducers/errors/index.ts +++ b/app/mm-redux/reducers/errors/index.ts @@ -11,7 +11,7 @@ export default ((state: Array<{error: any;displayable?: boolean;date: string}> = return nextState; } case ErrorTypes.LOG_ERROR: { - const nextState = [...state]; + const nextState = state.length ? [...state] : []; const {displayable, error} = action; nextState.push({ displayable, diff --git a/app/screens/channel/channel.android.js b/app/screens/channel/channel.android.js index 1ae28fd5d..6dad2a820 100644 --- a/app/screens/channel/channel.android.js +++ b/app/screens/channel/channel.android.js @@ -31,18 +31,10 @@ export default class ChannelAndroid extends ChannelBase { render() { const {theme} = this.props; - const channelLoadingOrFailed = this.renderLoadingOrFailedChannel(); - if (channelLoadingOrFailed) { - return channelLoadingOrFailed; - } + let component = this.renderLoadingOrFailedChannel(); - const drawerContent = ( - <> - + if (!component) { + component = ( @@ -52,6 +44,17 @@ export default class ChannelAndroid extends ChannelBase { screenId={this.props.componentId} /> + ); + } + + const drawerContent = ( + <> + + {component} {LocalConfig.EnableMobileClientUpgrade && } diff --git a/app/screens/channel/channel.ios.js b/app/screens/channel/channel.ios.js index c871096eb..04f1c280a 100644 --- a/app/screens/channel/channel.ios.js +++ b/app/screens/channel/channel.ios.js @@ -54,10 +54,27 @@ export default class ChannelIOS extends ChannelBase { render() { const {currentChannelId, theme} = this.props; + let component = this.renderLoadingOrFailedChannel(); + let renderDraftArea = false; - const channelLoadingOrFailed = this.renderLoadingOrFailedChannel(); - if (channelLoadingOrFailed) { - return channelLoadingOrFailed; + if (!component) { + renderDraftArea = true; + component = ( + <> + + + + + {LocalConfig.EnableMobileClientUpgrade && } + + ); } const style = getStyle(theme); @@ -71,19 +88,9 @@ export default class ChannelIOS extends ChannelBase { openSettingsSidebar={this.openSettingsSidebar} onPress={this.goToChannelInfo} /> - - - - - {LocalConfig.EnableMobileClientUpgrade && } + {component} + {renderDraftArea && + } ); diff --git a/app/screens/channel/channel_base.js b/app/screens/channel/channel_base.js index 50d15f860..600f547fa 100644 --- a/app/screens/channel/channel_base.js +++ b/app/screens/channel/channel_base.js @@ -7,14 +7,11 @@ import {intlShape} from 'react-intl'; import { Keyboard, StyleSheet, - View, } from 'react-native'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; import {showModal, showModalOverCurrentContext} from '@actions/navigation'; import LocalConfig from '@assets/config'; -import SafeAreaView from '@components/safe_area_view'; -import EmptyToolbar from '@components/start/empty_toolbar'; import {NavigationTypes} from '@constants'; import EventEmitter from '@mm-redux/utils/event_emitter'; import EphemeralStore from '@store/ephemeral_store'; @@ -30,19 +27,19 @@ export let ClientUpgradeListener; export default class ChannelBase extends PureComponent { static propTypes = { actions: PropTypes.shape({ + getChannelStats: PropTypes.func.isRequired, loadChannelsForTeam: PropTypes.func.isRequired, selectDefaultTeam: PropTypes.func.isRequired, selectInitialChannel: PropTypes.func.isRequired, recordLoadTime: PropTypes.func.isRequired, - getChannelStats: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string.isRequired, currentChannelId: PropTypes.string, currentTeamId: PropTypes.string, - isLandscape: PropTypes.bool, + disableTermsModal: PropTypes.bool, + teamName: PropTypes.string, theme: PropTypes.object.isRequired, showTermsOfService: PropTypes.bool, - disableTermsModal: PropTypes.bool, skipMetrics: PropTypes.bool, }; @@ -213,9 +210,10 @@ export default class ChannelBase extends PureComponent { }; renderLoadingOrFailedChannel() { + const {formatMessage} = this.context.intl; const { currentChannelId, - isLandscape, + teamName, theme, } = this.props; @@ -223,37 +221,28 @@ export default class ChannelBase extends PureComponent { if (!currentChannelId) { if (channelsRequestFailed) { const FailedNetworkAction = require('app/components/failed_network_action').default; + const title = formatMessage({id: 'mobile.failed_network_action.teams_title', defaultMessage: 'Something went wrong'}); + const message = formatMessage({ + id: 'mobile.failed_network_action.teams_channel_description', + defaultMessage: 'Channels could not be loaded for {teamName}.', + }, {teamName}); return ( - - - - - - + ); } const Loading = require('app/components/channel_loader').default; return ( - - - - - - + ); } diff --git a/app/screens/channel/index.js b/app/screens/channel/index.js index e061a0ab8..d276db377 100644 --- a/app/screens/channel/index.js +++ b/app/screens/channel/index.js @@ -4,30 +4,24 @@ import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; -import {startPeriodicStatusUpdates, stopPeriodicStatusUpdates} from '@mm-redux/actions/users'; +import {loadChannelsForTeam, selectInitialChannel} from '@actions/views/channel'; +import {recordLoadTime} from '@actions/views/root'; +import {selectDefaultTeam} from '@actions/views/select_team'; import {getCurrentChannelId} from '@mm-redux/selectors/entities/channels'; -import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams'; +import {getCurrentTeam} from '@mm-redux/selectors/entities/teams'; import {getTheme} from '@mm-redux/selectors/entities/preferences'; import {shouldShowTermsOfService} from '@mm-redux/selectors/entities/users'; import {getChannelStats} from '@mm-redux/actions/channels'; -import { - loadChannelsForTeam, - selectInitialChannel, -} from 'app/actions/views/channel'; -import {connection} from 'app/actions/device'; -import {recordLoadTime} from 'app/actions/views/root'; -import {logout} from 'app/actions/views/user'; -import {selectDefaultTeam} from 'app/actions/views/select_team'; -import {isLandscape} from 'app/selectors/device'; - import Channel from './channel'; function mapStateToProps(state) { + const currentTeam = getCurrentTeam(state); + return { - currentTeamId: getCurrentTeamId(state), + currentTeamId: currentTeam?.id, currentChannelId: getCurrentChannelId(state), - isLandscape: isLandscape(state), + teamName: currentTeam?.display_name, theme: getTheme(state), showTermsOfService: shouldShowTermsOfService(state), }; @@ -37,14 +31,10 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ getChannelStats, - connection, loadChannelsForTeam, - logout, selectDefaultTeam, selectInitialChannel, recordLoadTime, - startPeriodicStatusUpdates, - stopPeriodicStatusUpdates, }, dispatch), }; } diff --git a/app/screens/error_teams_list/__snapshots__/error_teams_list.test.js.snap b/app/screens/error_teams_list/__snapshots__/error_teams_list.test.js.snap index 1c18ba7b4..68992cb24 100644 --- a/app/screens/error_teams_list/__snapshots__/error_teams_list.test.js.snap +++ b/app/screens/error_teams_list/__snapshots__/error_teams_list.test.js.snap @@ -10,11 +10,10 @@ exports[`ErrorTeamsList should match snapshot 1`] = ` > ; } + const title = formatMessage({id: 'mobile.failed_network_action.teams_title', defaultMessage: 'Something went wrong'}); + const message = formatMessage({ + id: 'mobile.failed_network_action.teams_description', + defaultMessage: 'Teams could not be loaded.', + }); + return ( diff --git a/app/screens/error_teams_list/error_teams_list.test.js b/app/screens/error_teams_list/error_teams_list.test.js index d85e1e4a8..b0d719813 100644 --- a/app/screens/error_teams_list/error_teams_list.test.js +++ b/app/screens/error_teams_list/error_teams_list.test.js @@ -2,11 +2,11 @@ // See LICENSE.txt for license information. import React from 'react'; -import {shallow} from 'enzyme'; +import FailedNetworkAction from '@components/failed_network_action'; import Preferences from '@mm-redux/constants/preferences'; +import {shallowWithIntl} from 'test/intl-test-helper.js'; -import FailedNetworkAction from 'app/components/failed_network_action'; import ErrorTeamsList from './error_teams_list'; describe('ErrorTeamsList', () => { @@ -28,7 +28,7 @@ describe('ErrorTeamsList', () => { }; test('should match snapshot', () => { - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); expect(wrapper.getElement()).toMatchSnapshot(); @@ -51,7 +51,7 @@ describe('ErrorTeamsList', () => { actions, }; - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); diff --git a/app/screens/flagged_posts/__snapshots__/flagged_posts.test.js.snap b/app/screens/flagged_posts/__snapshots__/flagged_posts.test.js.snap index 4bcfbf8cf..887acd149 100644 --- a/app/screens/flagged_posts/__snapshots__/flagged_posts.test.js.snap +++ b/app/screens/flagged_posts/__snapshots__/flagged_posts.test.js.snap @@ -40,11 +40,8 @@ exports[`FlaggedPosts should match snapshot when getFlaggedPosts failed 1`] = ` > (props) => ( // eslint-disable-line react/display-name + const wrapper = (Comp, excludeEvents = true) => (props) => ( // eslint-disable-line react/display-name - + @@ -29,7 +29,7 @@ export function registerScreens(store, Provider) { Navigation.registerComponent('About', () => wrapper(require('app/screens/about').default), () => require('app/screens/about').default); Navigation.registerComponent('AddReaction', () => wrapper(require('app/screens/add_reaction').default), () => require('app/screens/add_reaction').default); Navigation.registerComponent('AdvancedSettings', () => wrapper(require('app/screens/settings/advanced_settings').default), () => require('app/screens/settings/advanced_settings').default); - Navigation.registerComponent('Channel', () => wrapper(require('app/screens/channel').default), () => require('app/screens/channel').default); + Navigation.registerComponent('Channel', () => wrapper(require('app/screens/channel').default, false), () => require('app/screens/channel').default); Navigation.registerComponent('ChannelAddMembers', () => wrapper(require('app/screens/channel_add_members').default), () => require('app/screens/channel_add_members').default); Navigation.registerComponent('ChannelInfo', () => wrapper(require('app/screens/channel_info').default), () => require('app/screens/channel_info').default); Navigation.registerComponent('ChannelMembers', () => wrapper(require('app/screens/channel_members').default), () => require('app/screens/channel_members').default); @@ -67,7 +67,7 @@ export function registerScreens(store, Provider) { Navigation.registerComponent('RecentMentions', () => wrapper(require('app/screens/recent_mentions').default), () => require('app/screens/recent_mentions').default); Navigation.registerComponent('Search', () => wrapper(require('app/screens/search').default), () => require('app/screens/search').default); Navigation.registerComponent('SelectorScreen', () => wrapper(require('app/screens/selector_screen').default), () => require('app/screens/selector_screen').default); - Navigation.registerComponent('SelectServer', () => wrapper(require('app/screens/select_server').default), () => require('app/screens/select_server').default); + Navigation.registerComponent('SelectServer', () => wrapper(require('app/screens/select_server').default, false), () => require('app/screens/select_server').default); Navigation.registerComponent('SelectTeam', () => wrapper(require('app/screens/select_team').default), () => require('app/screens/select_team').default); Navigation.registerComponent('SelectTimezone', () => wrapper(require('app/screens/settings/timezone/select_timezone').default), () => require('app/screens/settings/timezone/select_timezone').default); Navigation.registerComponent('Settings', () => wrapper(require('app/screens/settings/general').default), () => require('app/screens/settings/general').default); diff --git a/app/screens/pinned_posts/__snapshots__/pinned_posts.test.js.snap b/app/screens/pinned_posts/__snapshots__/pinned_posts.test.js.snap index e712788cd..88df8f356 100644 --- a/app/screens/pinned_posts/__snapshots__/pinned_posts.test.js.snap +++ b/app/screens/pinned_posts/__snapshots__/pinned_posts.test.js.snap @@ -40,11 +40,8 @@ exports[`PinnedPosts should match snapshot when getPinnedPosts failed 1`] = ` > { }, teams: { currentTeamId, + teams: { + [currentTeamId]: { + id: 'currentTeamId', + name: 'test', + display_name: 'Test', + }, + }, + myMembers: { + [currentTeamId]: {}, + }, }, preferences: { myPreferences: { @@ -74,10 +84,12 @@ describe('getStateForReset', () => { expect(users.profiles[currentUserId]).toBeDefined(); }); - it('should keep the current team ID', () => { + it('should keep the current team', () => { const resetState = getStateForReset(initialState, currentState); const {teams} = resetState.entities; expect(teams.currentTeamId).toEqual(currentTeamId); + expect(teams.teams[currentTeamId]).toEqual(currentState.entities.teams.teams[currentTeamId]); + expect(teams.myMembers[currentTeamId]).toEqual(currentState.entities.teams.myMembers[currentTeamId]); }); it('should keep theme preferences', () => { diff --git a/app/utils/teams.js b/app/utils/teams.js index f02bb4d76..991a7727c 100644 --- a/app/utils/teams.js +++ b/app/utils/teams.js @@ -2,10 +2,10 @@ // See LICENSE.txt for license information. // @flow -export function selectFirstAvailableTeam(teams, primaryTeam) { +export function selectFirstAvailableTeam(teams, primaryTeamName) { let defaultTeam; - if (primaryTeam) { - defaultTeam = teams.find((t) => t.name === primaryTeam.toLowerCase()); + if (primaryTeamName) { + defaultTeam = teams.find((t) => t?.name === primaryTeamName.toLowerCase()); } if (!defaultTeam) { diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index ef66e336b..a118231be 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -253,8 +253,11 @@ "mobile.extension.permission": "Mattermost needs access to the device storage to share files.", "mobile.extension.team_required": "You must belong to a team before you can share files.", "mobile.extension.title": "Share in Mattermost", - "mobile.failed_network_action.retry": "try again", - "mobile.failed_network_action.shortDescription": "Messages will load when you have an internet connection or {tryAgainAction}.", + "mobile.failed_network_action.retry": "Try again", + "mobile.failed_network_action.shortDescription": "Messages will load when you have an internet connection.", + "mobile.failed_network_action.teams_channel_description": "Channels could not be loaded for {teamName}.", + "mobile.failed_network_action.teams_description": "Teams could not be loaded.", + "mobile.failed_network_action.teams_title": "Something went wrong", "mobile.failed_network_action.title": "No internet connection", "mobile.file_upload.browse": "Browse Files", "mobile.file_upload.camera_photo": "Take Photo",