forked from Ivasoft/mattermost-mobile
* Fix infinite skeleton in different use cases * Apply suggestions from code review Co-authored-by: Miguel Alatzar <migbot@users.noreply.github.com> * Update app/utils/teams.js Co-authored-by: Elias Nahum <nahumhbl@gmail.com> Co-authored-by: Miguel Alatzar <migbot@users.noreply.github.com>
This commit is contained in:
@@ -368,11 +368,15 @@ async function getProfilesFromPromises(promises: Array<Promise<ActionResult>>):
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 (
|
||||
<View style={style.container}>
|
||||
@@ -44,26 +53,14 @@ export default class FailedNetworkAction extends PureComponent {
|
||||
height={76}
|
||||
width={76}
|
||||
/>
|
||||
<FormattedText
|
||||
id={errorTitle.id}
|
||||
defaultMessage={errorTitle.defaultMessage}
|
||||
style={style.title}
|
||||
/>
|
||||
<FormattedText
|
||||
id={errorId}
|
||||
defaultMessage={errorDefaultMessage}
|
||||
style={style.description}
|
||||
values={{
|
||||
tryAgainAction: (
|
||||
<FormattedText
|
||||
id={actionId}
|
||||
defaultMessage={actionDefaultMessage}
|
||||
style={style.link}
|
||||
onPress={onRetry}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Text style={style.title}>{errorTitle}</Text>
|
||||
<Text style={style.description}>{errorMessage}</Text>
|
||||
<Button
|
||||
onPress={onRetry}
|
||||
containerStyle={style.buttonContainer}
|
||||
>
|
||||
<Text style={style.link}>{actionText}</Text>
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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 (
|
||||
<DrawerLayout
|
||||
ref={this.drawerRef}
|
||||
|
||||
@@ -71,7 +71,7 @@ export default class MainSidebarBase extends Component {
|
||||
|
||||
const condition = nextProps.currentTeamId !== currentTeamId ||
|
||||
nextProps.teamsCount !== teamsCount ||
|
||||
nextProps.theme !== theme;
|
||||
nextProps.theme !== theme || this.props.children !== nextProps.children;
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
return condition ||
|
||||
@@ -263,6 +263,10 @@ export default class MainSidebarBase extends Component {
|
||||
selectChannel = (channel, currentChannelId, closeDrawer = true) => {
|
||||
const {logChannelSwitch, handleSelectChannel} = this.props.actions;
|
||||
|
||||
if (channel.id === currentChannelId) {
|
||||
return;
|
||||
}
|
||||
|
||||
logChannelSwitch(channel.id, currentChannelId);
|
||||
|
||||
tracker.channelSwitch = Date.now();
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
@@ -87,7 +87,7 @@ export default class TeamIcon extends React.PureComponent {
|
||||
} else {
|
||||
teamIconContent = (
|
||||
<Text style={[styles.text, styleText]}>
|
||||
{displayName.substr(0, 2).toUpperCase()}
|
||||
{displayName?.substr(0, 2).toUpperCase()}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -365,14 +365,14 @@ function myMembers(state: RelationOneToOne<Channel, ChannelMembership> = {}, 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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = (
|
||||
<>
|
||||
<ChannelNavBar
|
||||
openMainSidebar={this.openMainSidebar}
|
||||
openSettingsSidebar={this.openSettingsSidebar}
|
||||
onPress={this.goToChannelInfo}
|
||||
/>
|
||||
if (!component) {
|
||||
component = (
|
||||
<KeyboardLayout>
|
||||
<View style={style.flex}>
|
||||
<ChannelPostList/>
|
||||
@@ -52,6 +44,17 @@ export default class ChannelAndroid extends ChannelBase {
|
||||
screenId={this.props.componentId}
|
||||
/>
|
||||
</KeyboardLayout>
|
||||
);
|
||||
}
|
||||
|
||||
const drawerContent = (
|
||||
<>
|
||||
<ChannelNavBar
|
||||
openMainSidebar={this.openMainSidebar}
|
||||
openSettingsSidebar={this.openSettingsSidebar}
|
||||
onPress={this.goToChannelInfo}
|
||||
/>
|
||||
{component}
|
||||
<NetworkIndicator/>
|
||||
{LocalConfig.EnableMobileClientUpgrade && <ClientUpgradeListener/>}
|
||||
</>
|
||||
|
||||
@@ -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 = (
|
||||
<>
|
||||
<ChannelPostList
|
||||
updateNativeScrollView={this.updateNativeScrollView}
|
||||
/>
|
||||
<View nativeID={ACCESSORIES_CONTAINER_NATIVE_ID}>
|
||||
<Autocomplete
|
||||
maxHeight={AUTOCOMPLETE_MAX_HEIGHT}
|
||||
onChangeText={this.handleAutoComplete}
|
||||
cursorPositionEvent={CHANNEL_POST_TEXTBOX_CURSOR_CHANGE}
|
||||
valueEvent={CHANNEL_POST_TEXTBOX_VALUE_CHANGE}
|
||||
/>
|
||||
</View>
|
||||
{LocalConfig.EnableMobileClientUpgrade && <ClientUpgradeListener/>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const style = getStyle(theme);
|
||||
@@ -71,19 +88,9 @@ export default class ChannelIOS extends ChannelBase {
|
||||
openSettingsSidebar={this.openSettingsSidebar}
|
||||
onPress={this.goToChannelInfo}
|
||||
/>
|
||||
<ChannelPostList
|
||||
updateNativeScrollView={this.updateNativeScrollView}
|
||||
/>
|
||||
<View nativeID={ACCESSORIES_CONTAINER_NATIVE_ID}>
|
||||
<Autocomplete
|
||||
maxHeight={AUTOCOMPLETE_MAX_HEIGHT}
|
||||
onChangeText={this.handleAutoComplete}
|
||||
cursorPositionEvent={CHANNEL_POST_TEXTBOX_CURSOR_CHANGE}
|
||||
valueEvent={CHANNEL_POST_TEXTBOX_VALUE_CHANGE}
|
||||
/>
|
||||
</View>
|
||||
{LocalConfig.EnableMobileClientUpgrade && <ClientUpgradeListener/>}
|
||||
{component}
|
||||
</SafeAreaView>
|
||||
{renderDraftArea &&
|
||||
<KeyboardTrackingView
|
||||
ref={this.keyboardTracker}
|
||||
scrollViewNativeID={currentChannelId}
|
||||
@@ -96,6 +103,7 @@ export default class ChannelIOS extends ChannelBase {
|
||||
screenId={this.props.componentId}
|
||||
/>
|
||||
</KeyboardTrackingView>
|
||||
}
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<SafeAreaView>
|
||||
<View style={style.flex}>
|
||||
<EmptyToolbar
|
||||
theme={theme}
|
||||
isLandscape={isLandscape}
|
||||
/>
|
||||
<FailedNetworkAction
|
||||
onRetry={this.retryLoadChannels}
|
||||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
<FailedNetworkAction
|
||||
errorMessage={message}
|
||||
errorTitle={title}
|
||||
onRetry={this.retryLoadChannels}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const Loading = require('app/components/channel_loader').default;
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<View style={style.flex}>
|
||||
<EmptyToolbar
|
||||
theme={theme}
|
||||
isLandscape={isLandscape}
|
||||
/>
|
||||
<Loading
|
||||
channelIsLoading={true}
|
||||
color={theme.centerChannelColor}
|
||||
/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
<Loading
|
||||
channelIsLoading={true}
|
||||
color={theme.centerChannelColor}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,11 +10,10 @@ exports[`ErrorTeamsList should match snapshot 1`] = `
|
||||
>
|
||||
<Connect(StatusBar) />
|
||||
<FailedNetworkAction
|
||||
actionDefaultMessage="try again"
|
||||
actionId="mobile.failed_network_action.retry"
|
||||
errorDefaultMessage="Messages will load when you have an internet connection or {tryAgainAction}."
|
||||
errorId="mobile.failed_network_action.shortDescription"
|
||||
errorMessage="Teams could not be loaded."
|
||||
errorTitle="Something went wrong"
|
||||
onRetry={[Function]}
|
||||
showAction={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {intlShape} from 'react-intl';
|
||||
import {
|
||||
InteractionManager,
|
||||
StyleSheet,
|
||||
@@ -26,6 +27,10 @@ export default class ErrorTeamsList extends PureComponent {
|
||||
theme: PropTypes.object,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -67,16 +72,25 @@ export default class ErrorTeamsList extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {formatMessage} = this.context.intl;
|
||||
const {theme} = this.props;
|
||||
|
||||
if (this.state.loading) {
|
||||
return <Loading color={theme.centerChannelColor}/>;
|
||||
}
|
||||
|
||||
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 (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<FailedNetworkAction
|
||||
errorMessage={message}
|
||||
errorTitle={title}
|
||||
onRetry={this.getUserInfo}
|
||||
theme={theme}
|
||||
/>
|
||||
|
||||
@@ -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(
|
||||
<ErrorTeamsList {...baseProps}/>,
|
||||
);
|
||||
expect(wrapper.getElement()).toMatchSnapshot();
|
||||
@@ -51,7 +51,7 @@ describe('ErrorTeamsList', () => {
|
||||
actions,
|
||||
};
|
||||
|
||||
const wrapper = shallow(
|
||||
const wrapper = shallowWithIntl(
|
||||
<ErrorTeamsList {...newProps}/>,
|
||||
);
|
||||
|
||||
|
||||
@@ -40,11 +40,8 @@ exports[`FlaggedPosts should match snapshot when getFlaggedPosts failed 1`] = `
|
||||
>
|
||||
<Connect(StatusBar) />
|
||||
<FailedNetworkAction
|
||||
actionDefaultMessage="try again"
|
||||
actionId="mobile.failed_network_action.retry"
|
||||
errorDefaultMessage="Messages will load when you have an internet connection or {tryAgainAction}."
|
||||
errorId="mobile.failed_network_action.shortDescription"
|
||||
onRetry={[Function]}
|
||||
showAction={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
||||
@@ -16,10 +16,10 @@ export function registerScreens(store, Provider) {
|
||||
Root = require('app/components/root').default;
|
||||
}
|
||||
|
||||
const wrapper = (Comp) => (props) => ( // eslint-disable-line react/display-name
|
||||
const wrapper = (Comp, excludeEvents = true) => (props) => ( // eslint-disable-line react/display-name
|
||||
<Provider store={store}>
|
||||
<ThemeProvider>
|
||||
<Root>
|
||||
<Root excludeEvents={excludeEvents}>
|
||||
<Comp {...props}/>
|
||||
</Root>
|
||||
</ThemeProvider>
|
||||
@@ -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);
|
||||
|
||||
@@ -40,11 +40,8 @@ exports[`PinnedPosts should match snapshot when getPinnedPosts failed 1`] = `
|
||||
>
|
||||
<Connect(StatusBar) />
|
||||
<FailedNetworkAction
|
||||
actionDefaultMessage="try again"
|
||||
actionId="mobile.failed_network_action.retry"
|
||||
errorDefaultMessage="Messages will load when you have an internet connection or {tryAgainAction}."
|
||||
errorId="mobile.failed_network_action.shortDescription"
|
||||
onRetry={[Function]}
|
||||
showAction={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
||||
@@ -40,11 +40,8 @@ exports[`RecentMentions should match snapshot when getRecentMentions failed 1`]
|
||||
>
|
||||
<Connect(StatusBar) />
|
||||
<FailedNetworkAction
|
||||
actionDefaultMessage="try again"
|
||||
actionId="mobile.failed_network_action.retry"
|
||||
errorDefaultMessage="Messages will load when you have an internet connection or {tryAgainAction}."
|
||||
errorId="mobile.failed_network_action.shortDescription"
|
||||
onRetry={[Function]}
|
||||
showAction={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
||||
@@ -2,11 +2,8 @@
|
||||
|
||||
exports[`SelectTeam should match snapshot for fail of teams 1`] = `
|
||||
<FailedNetworkAction
|
||||
actionDefaultMessage="try again"
|
||||
actionId="mobile.failed_network_action.retry"
|
||||
errorDefaultMessage="Messages will load when you have an internet connection or {tryAgainAction}."
|
||||
errorId="mobile.failed_network_action.shortDescription"
|
||||
onRetry={[Function]}
|
||||
showAction={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
||||
@@ -32,7 +32,7 @@ class Settings extends PureComponent {
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
currentUserId: PropTypes.string.isRequired,
|
||||
currentUrl: PropTypes.string.isRequired,
|
||||
errors: PropTypes.array.isRequired,
|
||||
errors: PropTypes.object.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
joinableTeams: PropTypes.array.isRequired,
|
||||
theme: PropTypes.object,
|
||||
|
||||
@@ -288,11 +288,8 @@ exports[`TermsOfService should match snapshot for fail of get terms 1`] = `
|
||||
>
|
||||
<Connect(StatusBar) />
|
||||
<FailedNetworkAction
|
||||
actionDefaultMessage="try again"
|
||||
actionId="mobile.failed_network_action.retry"
|
||||
errorDefaultMessage="Messages will load when you have an internet connection or {tryAgainAction}."
|
||||
errorId="mobile.failed_network_action.shortDescription"
|
||||
onRetry={[Function]}
|
||||
showAction={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
||||
@@ -93,6 +93,12 @@ export function getStateForReset(initialState, currentState) {
|
||||
},
|
||||
teams: {
|
||||
currentTeamId,
|
||||
teams: {
|
||||
[currentTeamId]: currentState.entities.teams.teams[currentTeamId],
|
||||
},
|
||||
myMembers: {
|
||||
[currentTeamId]: currentState.entities.teams.myMembers[currentTeamId],
|
||||
},
|
||||
},
|
||||
preferences,
|
||||
},
|
||||
|
||||
@@ -37,6 +37,16 @@ describe('getStateForReset', () => {
|
||||
},
|
||||
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', () => {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user