forked from Ivasoft/mattermost-mobile
Various fixes (#3078)
* MM-17588 Remove navigation component from stack * MM-175986 Fix Clock Display Settings on iOS * Fix markdown and team icon currentServerUrl * Fix closing permalink logs out the user * Fix file attachment document ref * Fix applyTheme when changing a theme in the app * Feedback review * remove / when fetching the image on the markdown table on relative paths
This commit is contained in:
committed by
Saturnino Abril
parent
24bd57ad3f
commit
7b75868101
@@ -360,24 +360,22 @@ export function dismissOverlay(componentId) {
|
||||
};
|
||||
}
|
||||
|
||||
export function applyTheme() {
|
||||
export function applyTheme(componentId) {
|
||||
return (dispatch, getState) => {
|
||||
const theme = getTheme(getState());
|
||||
|
||||
EphemeralStore.getNavigationComponentIds().forEach((componentId) => {
|
||||
Navigation.mergeOptions(componentId, {
|
||||
topBar: {
|
||||
backButton: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
},
|
||||
background: {
|
||||
color: theme.sidebarHeaderBg,
|
||||
},
|
||||
title: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
},
|
||||
Navigation.mergeOptions(componentId, {
|
||||
topBar: {
|
||||
backButton: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
},
|
||||
});
|
||||
background: {
|
||||
color: theme.sidebarHeaderBg,
|
||||
},
|
||||
title: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,4 +16,4 @@ function mapDispatchToProps(dispatch) {
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(FileAttachmentDocument);
|
||||
export default connect(null, mapDispatchToProps, null, {forwardRef: true})(FileAttachmentDocument);
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import ProgressiveImage from 'app/components/progressive_image';
|
||||
import CustomPropTypes from 'app/constants/custom_prop_types';
|
||||
import {getCurrentServerUrl} from 'app/init/credentials';
|
||||
import mattermostManaged from 'app/mattermost_managed';
|
||||
import BottomSheet from 'app/utils/bottom_sheet';
|
||||
import ImageCacheManager from 'app/utils/image_cache_manager';
|
||||
@@ -42,7 +43,7 @@ export default class MarkdownImage extends React.Component {
|
||||
imagesMetadata: PropTypes.object,
|
||||
linkDestination: PropTypes.string,
|
||||
isReplyPost: PropTypes.bool,
|
||||
serverURL: PropTypes.string.isRequired,
|
||||
serverURL: PropTypes.string,
|
||||
source: PropTypes.string.isRequired,
|
||||
errorTextStyle: CustomPropTypes.Style,
|
||||
};
|
||||
@@ -95,11 +96,15 @@ export default class MarkdownImage extends React.Component {
|
||||
this.mounted = false;
|
||||
}
|
||||
|
||||
getSource = () => {
|
||||
getSource = async () => {
|
||||
let source = this.props.source;
|
||||
let serverUrl = this.props.serverURL;
|
||||
if (!serverUrl) {
|
||||
serverUrl = await getCurrentServerUrl();
|
||||
}
|
||||
|
||||
if (source.startsWith('/')) {
|
||||
source = this.props.serverURL + '/' + source;
|
||||
source = serverUrl + source;
|
||||
}
|
||||
|
||||
return source;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {intlShape} from 'react-intl';
|
||||
|
||||
import CustomPropTypes from 'app/constants/custom_prop_types';
|
||||
import {DeepLinkTypes} from 'app/constants';
|
||||
import {getCurrentServerUrl} from 'app/init/credentials';
|
||||
import mattermostManaged from 'app/mattermost_managed';
|
||||
import BottomSheet from 'app/utils/bottom_sheet';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
@@ -24,7 +25,7 @@ export default class MarkdownLink extends PureComponent {
|
||||
children: CustomPropTypes.Children.isRequired,
|
||||
href: PropTypes.string.isRequired,
|
||||
onPermalinkPress: PropTypes.func,
|
||||
serverURL: PropTypes.string.isRequired,
|
||||
serverURL: PropTypes.string,
|
||||
siteURL: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
@@ -38,7 +39,7 @@ export default class MarkdownLink extends PureComponent {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
handlePress = preventDoubleTap(() => {
|
||||
handlePress = preventDoubleTap(async () => {
|
||||
const {href, onPermalinkPress, serverURL, siteURL} = this.props;
|
||||
const url = normalizeProtocol(href);
|
||||
|
||||
@@ -46,7 +47,12 @@ export default class MarkdownLink extends PureComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
const match = matchDeepLink(url, serverURL, siteURL);
|
||||
let serverUrl = serverURL;
|
||||
if (!serverUrl) {
|
||||
serverUrl = await getCurrentServerUrl();
|
||||
}
|
||||
|
||||
const match = matchDeepLink(url, serverUrl, siteURL);
|
||||
if (match) {
|
||||
if (match.type === DeepLinkTypes.CHANNEL) {
|
||||
this.props.actions.handleSelectChannelByName(match.channelName, match.teamName);
|
||||
|
||||
@@ -7,6 +7,7 @@ import {intlShape} from 'react-intl';
|
||||
import {Text} from 'react-native';
|
||||
|
||||
import CustomPropTypes from 'app/constants/custom_prop_types';
|
||||
import {getCurrentServerUrl} from 'app/init/credentials';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
|
||||
export default class MarkdownTableImage extends React.PureComponent {
|
||||
@@ -17,7 +18,7 @@ export default class MarkdownTableImage extends React.PureComponent {
|
||||
children: PropTypes.node.isRequired,
|
||||
source: PropTypes.string.isRequired,
|
||||
textStyle: CustomPropTypes.Style.isRequired,
|
||||
serverURL: PropTypes.string.isRequired,
|
||||
serverURL: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
@@ -40,11 +41,16 @@ export default class MarkdownTableImage extends React.PureComponent {
|
||||
actions.goToScreen(screen, title, passProps);
|
||||
});
|
||||
|
||||
getImageSource = () => {
|
||||
getImageSource = async () => {
|
||||
let source = this.props.source;
|
||||
let serverUrl = this.props.serverURL;
|
||||
|
||||
if (!serverUrl) {
|
||||
serverUrl = await getCurrentServerUrl();
|
||||
}
|
||||
|
||||
if (source.startsWith('/')) {
|
||||
source = `${this.props.serverURL}/${source}`;
|
||||
source = serverUrl + source;
|
||||
}
|
||||
|
||||
return source;
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getCurrentTeamId, getMySortedTeamIds, getJoinableTeamIds} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {showModal} from 'app/actions/navigation';
|
||||
import {handleTeamChange} from 'app/actions/views/select_team';
|
||||
import {getCurrentLocale} from 'app/selectors/i18n';
|
||||
import {removeProtocol} from 'app/utils/url';
|
||||
|
||||
import TeamsList from './teams_list';
|
||||
|
||||
@@ -20,7 +18,6 @@ function mapStateToProps(state) {
|
||||
|
||||
return {
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
currentUrl: removeProtocol(getCurrentUrl(state)),
|
||||
hasOtherJoinableTeams: getJoinableTeamIds(state).length > 0,
|
||||
teamIds: getMySortedTeamIds(state, locale),
|
||||
theme: getTheme(state),
|
||||
|
||||
@@ -17,8 +17,10 @@ import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {DeviceTypes, ListTypes, ViewTypes} from 'app/constants';
|
||||
import {getCurrentServerUrl} from 'app/init/credentials';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import {removeProtocol} from 'app/utils/url';
|
||||
import tracker from 'app/utils/time_tracker';
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
@@ -38,7 +40,6 @@ export default class TeamsList extends PureComponent {
|
||||
}).isRequired,
|
||||
closeChannelDrawer: PropTypes.func.isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
currentUrl: PropTypes.string.isRequired,
|
||||
hasOtherJoinableTeams: PropTypes.bool,
|
||||
teamIds: PropTypes.array.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
@@ -51,9 +52,17 @@ export default class TeamsList extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
serverUrl: '',
|
||||
};
|
||||
|
||||
MaterialIcon.getImageSource('close', 20, props.theme.sidebarHeaderTextColor).then((source) => {
|
||||
this.closeButton = source;
|
||||
});
|
||||
|
||||
getCurrentServerUrl().then((url) => {
|
||||
this.setState({serverUrl: removeProtocol(url)});
|
||||
});
|
||||
}
|
||||
|
||||
selectTeam = (teamId) => {
|
||||
@@ -75,13 +84,14 @@ export default class TeamsList extends PureComponent {
|
||||
});
|
||||
};
|
||||
|
||||
goToSelectTeam = preventDoubleTap(() => {
|
||||
goToSelectTeam = preventDoubleTap(async () => {
|
||||
const {intl} = this.context;
|
||||
const {currentUrl, theme, actions} = this.props;
|
||||
const {theme, actions} = this.props;
|
||||
const {serverUrl} = this.state;
|
||||
const screen = 'SelectTeam';
|
||||
const title = intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'});
|
||||
const passProps = {
|
||||
currentUrl,
|
||||
currentUrl: serverUrl,
|
||||
theme,
|
||||
};
|
||||
const options = {
|
||||
@@ -117,6 +127,7 @@ export default class TeamsList extends PureComponent {
|
||||
renderItem = ({item}) => {
|
||||
return (
|
||||
<TeamsListItem
|
||||
currentUrl={this.state.serverUrl}
|
||||
selectTeam={this.selectTeam}
|
||||
teamId={item}
|
||||
/>
|
||||
@@ -155,6 +166,7 @@ export default class TeamsList extends PureComponent {
|
||||
{moreAction}
|
||||
</View>
|
||||
<FlatList
|
||||
extraData={this.state.serverUrl}
|
||||
contentContainerStyle={this.listContentPadding()}
|
||||
data={teamIds}
|
||||
renderItem={this.renderItem}
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getCurrentTeamId, getTeam, makeGetBadgeCountForTeamId} from 'mattermost-redux/selectors/entities/teams';
|
||||
|
||||
import {removeProtocol} from 'app/utils/url';
|
||||
|
||||
import TeamsListItem from './teams_list_item.js';
|
||||
|
||||
function makeMapStateToProps() {
|
||||
@@ -19,7 +16,6 @@ function makeMapStateToProps() {
|
||||
|
||||
return {
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
currentUrl: removeProtocol(getCurrentUrl(state)),
|
||||
displayName: team.display_name,
|
||||
mentionCount: getMentionCount(state, ownProps.teamId),
|
||||
name: team.name,
|
||||
|
||||
@@ -92,4 +92,8 @@ Navigation.events().registerAppLaunchedListener(() => {
|
||||
Navigation.events().registerComponentDidAppearListener(({componentId}) => {
|
||||
EphemeralStore.addNavigationComponentId(componentId);
|
||||
});
|
||||
|
||||
Navigation.events().registerComponentDidDisappearListener(({componentId}) => {
|
||||
EphemeralStore.removeNavigationComponentId(componentId);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -66,7 +66,7 @@ export function registerScreens(store, Provider) {
|
||||
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);
|
||||
Navigation.registerComponent('SidebarSettings', () => wrapper(require('app/screens//settings/sidebar').default), () => require('app/screens/settings/sidebar').default);
|
||||
Navigation.registerComponent('SidebarSettings', () => wrapper(require('app/screens/settings/sidebar').default), () => require('app/screens/settings/sidebar').default);
|
||||
Navigation.registerComponent('SSO', () => wrapper(require('app/screens/sso').default), () => require('app/screens/sso').default);
|
||||
Navigation.registerComponent('Table', () => wrapper(require('app/screens/table').default), () => require('app/screens/table').default);
|
||||
Navigation.registerComponent('TableImage', () => wrapper(require('app/screens/table_image').default), () => require('app/screens/table_image').default);
|
||||
|
||||
@@ -267,41 +267,43 @@ export default class Permalink extends PureComponent {
|
||||
const {formatMessage} = intl;
|
||||
let focusChannelId = channelId;
|
||||
|
||||
const post = await actions.getPostThread(focusedPostId, false);
|
||||
if (post.error && (!postIds || !postIds.length)) {
|
||||
if (this.mounted && isPermalink && post.error.message.toLowerCase() !== 'network request failed') {
|
||||
this.setState({
|
||||
error: formatMessage({
|
||||
id: 'permalink.error.access',
|
||||
defaultMessage: 'Permalink belongs to a deleted message or to a channel to which you do not have access.',
|
||||
}),
|
||||
title: formatMessage({
|
||||
id: 'mobile.search.no_results',
|
||||
defaultMessage: 'No Results Found',
|
||||
}),
|
||||
});
|
||||
} else if (this.mounted) {
|
||||
this.setState({error: post.error.message, retry: true});
|
||||
if (focusedPostId) {
|
||||
const post = await actions.getPostThread(focusedPostId, false);
|
||||
if (post.error && (!postIds || !postIds.length)) {
|
||||
if (this.mounted && isPermalink && post.error.message.toLowerCase() !== 'network request failed') {
|
||||
this.setState({
|
||||
error: formatMessage({
|
||||
id: 'permalink.error.access',
|
||||
defaultMessage: 'Permalink belongs to a deleted message or to a channel to which you do not have access.',
|
||||
}),
|
||||
title: formatMessage({
|
||||
id: 'mobile.search.no_results',
|
||||
defaultMessage: 'No Results Found',
|
||||
}),
|
||||
});
|
||||
} else if (this.mounted) {
|
||||
this.setState({error: post.error.message, retry: true});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!channelId) {
|
||||
const focusedPost = post.data && post.data.posts ? post.data.posts[focusedPostId] : null;
|
||||
focusChannelId = focusedPost ? focusedPost.channel_id : '';
|
||||
if (focusChannelId) {
|
||||
const {data: channel} = await actions.getChannel(focusChannelId);
|
||||
if (!this.props.myMembers[focusChannelId] && channel && channel.type === General.OPEN_CHANNEL) {
|
||||
await actions.joinChannel(currentUserId, channel.team_id, channel.id);
|
||||
if (!channelId) {
|
||||
const focusedPost = post.data && post.data.posts ? post.data.posts[focusedPostId] : null;
|
||||
focusChannelId = focusedPost ? focusedPost.channel_id : '';
|
||||
if (focusChannelId) {
|
||||
const {data: channel} = await actions.getChannel(focusChannelId);
|
||||
if (!this.props.myMembers[focusChannelId] && channel && channel.type === General.OPEN_CHANNEL) {
|
||||
await actions.joinChannel(currentUserId, channel.team_id, channel.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await actions.getPostsAround(focusChannelId, focusedPostId, 10);
|
||||
await actions.getPostsAround(focusChannelId, focusedPostId, 10);
|
||||
|
||||
if (this.mounted) {
|
||||
this.setState({loading: false});
|
||||
if (this.mounted) {
|
||||
this.setState({loading: false});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
import {intlShape} from 'react-intl';
|
||||
import {
|
||||
Platform,
|
||||
@@ -19,6 +20,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
export default class DisplaySettings extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
applyTheme: PropTypes.func.isRequired,
|
||||
goToScreen: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
componentId: PropTypes.string,
|
||||
@@ -35,6 +37,15 @@ export default class DisplaySettings extends PureComponent {
|
||||
showClockDisplaySettings: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.navigationEventListener = Navigation.events().bindComponent(this);
|
||||
}
|
||||
|
||||
componentDidAppear() {
|
||||
const {actions, componentId} = this.props;
|
||||
actions.applyTheme(componentId);
|
||||
}
|
||||
|
||||
closeClockDisplaySettings = () => {
|
||||
this.setState({showClockDisplaySettings: false});
|
||||
};
|
||||
@@ -44,7 +55,7 @@ export default class DisplaySettings extends PureComponent {
|
||||
const {intl} = this.context;
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
const screen = 'ClockDisplay';
|
||||
const screen = 'ClockDisplaySettings';
|
||||
const title = intl.formatMessage({id: 'user.settings.display.clockDisplay', defaultMessage: 'Clock Display'});
|
||||
actions.goToScreen(screen, title);
|
||||
return;
|
||||
|
||||
@@ -16,6 +16,7 @@ jest.mock('react-intl');
|
||||
describe('DisplaySettings', () => {
|
||||
const baseProps = {
|
||||
actions: {
|
||||
applyTheme: jest.fn(),
|
||||
goToScreen: jest.fn(),
|
||||
},
|
||||
theme: Preferences.THEMES.default,
|
||||
|
||||
@@ -7,7 +7,7 @@ import {connect} from 'react-redux';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone';
|
||||
|
||||
import {goToScreen} from 'app/actions/navigation';
|
||||
import {applyTheme, goToScreen} from 'app/actions/navigation';
|
||||
import {getAllowedThemes} from 'app/selectors/theme';
|
||||
import {isThemeSwitchingEnabled} from 'app/utils/theme';
|
||||
|
||||
@@ -28,6 +28,7 @@ function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
goToScreen,
|
||||
applyTheme,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {getCurrentUrl, getConfig} from 'mattermost-redux/selectors/entities/gene
|
||||
import {getJoinableTeams} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {goToScreen, dismissModal} from 'app/actions/navigation';
|
||||
import {applyTheme, goToScreen, dismissModal} from 'app/actions/navigation';
|
||||
import {purgeOfflineStore} from 'app/actions/views/root';
|
||||
import {removeProtocol} from 'app/utils/url';
|
||||
|
||||
@@ -32,6 +32,7 @@ function mapStateToProps(state) {
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
applyTheme,
|
||||
clearErrors,
|
||||
purgeOfflineStore,
|
||||
goToScreen,
|
||||
|
||||
@@ -25,6 +25,7 @@ import LocalConfig from 'assets/config';
|
||||
class Settings extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
applyTheme: PropTypes.func.isRequired,
|
||||
clearErrors: PropTypes.func.isRequired,
|
||||
purgeOfflineStore: PropTypes.func.isRequired,
|
||||
goToScreen: PropTypes.func.isRequired,
|
||||
@@ -50,6 +51,11 @@ class Settings extends PureComponent {
|
||||
this.navigationEventListener = Navigation.events().bindComponent(this);
|
||||
}
|
||||
|
||||
componentDidAppear() {
|
||||
const {actions, componentId} = this.props;
|
||||
actions.applyTheme(componentId);
|
||||
}
|
||||
|
||||
navigationButtonPressed({buttonId}) {
|
||||
if (buttonId === 'close-settings') {
|
||||
this.props.actions.dismissModal();
|
||||
|
||||
@@ -11,7 +11,6 @@ import {savePreferences} from 'mattermost-redux/actions/preferences';
|
||||
|
||||
import {getAllowedThemes, getCustomTheme} from 'app/selectors/theme';
|
||||
import {isLandscape, isTablet} from 'app/selectors/device';
|
||||
import {applyTheme} from 'app/actions/navigation';
|
||||
|
||||
import Theme from './theme';
|
||||
|
||||
@@ -28,7 +27,6 @@ const mapStateToProps = (state) => ({
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
actions: bindActionCreators({
|
||||
savePreferences,
|
||||
applyTheme,
|
||||
}, dispatch),
|
||||
});
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ export default class Theme extends React.PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
savePreferences: PropTypes.func.isRequired,
|
||||
applyTheme: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
componentId: PropTypes.string,
|
||||
allowedThemes: PropTypes.arrayOf(PropTypes.object),
|
||||
@@ -66,7 +65,7 @@ export default class Theme extends React.PureComponent {
|
||||
userId,
|
||||
teamId,
|
||||
allowedThemes,
|
||||
actions: {savePreferences, applyTheme},
|
||||
actions: {savePreferences},
|
||||
} = this.props;
|
||||
const {customTheme} = this.state;
|
||||
const selectedTheme = allowedThemes.concat(customTheme).find((theme) => theme.key === key);
|
||||
@@ -77,8 +76,7 @@ export default class Theme extends React.PureComponent {
|
||||
name: teamId,
|
||||
value: JSON.stringify(selectedTheme),
|
||||
}]);
|
||||
applyTheme();
|
||||
}
|
||||
};
|
||||
|
||||
renderAllowedThemeTiles = () => {
|
||||
const {theme, allowedThemes, isLandscape, isTablet} = this.props;
|
||||
|
||||
@@ -3,29 +3,14 @@
|
||||
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
import Preferences from 'mattermost-redux/constants/preferences';
|
||||
|
||||
import {applyTheme} from 'app/actions/navigation';
|
||||
import EphemeralStore from 'app/store/ephemeral_store';
|
||||
|
||||
import Theme from './theme';
|
||||
import ThemeTile from './theme_tile';
|
||||
|
||||
jest.mock('react-intl');
|
||||
|
||||
jest.mock('react-native-navigation', () => ({
|
||||
Navigation: {
|
||||
mergeOptions: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
const mockStore = configureMockStore([thunk]);
|
||||
|
||||
const allowedThemes = [
|
||||
{
|
||||
type: 'Mattermost',
|
||||
@@ -141,7 +126,6 @@ describe('Theme', () => {
|
||||
const baseProps = {
|
||||
actions: {
|
||||
savePreferences: jest.fn(),
|
||||
applyTheme: jest.fn(),
|
||||
},
|
||||
allowedThemes,
|
||||
isLandscape: false,
|
||||
@@ -159,57 +143,4 @@ describe('Theme', () => {
|
||||
expect(wrapper.getElement()).toMatchSnapshot();
|
||||
expect(wrapper.find(ThemeTile)).toHaveLength(4);
|
||||
});
|
||||
|
||||
test('should apply new theme to all navigation components that have appeared', () => {
|
||||
const componentIds = ['component-1', 'component-2', 'component-3'];
|
||||
componentIds.forEach((componentId) => {
|
||||
EphemeralStore.addNavigationComponentId(componentId);
|
||||
});
|
||||
|
||||
const store = mockStore({
|
||||
entities: {
|
||||
preferences: {
|
||||
myPreferences: {
|
||||
theme: {},
|
||||
},
|
||||
},
|
||||
teams: {
|
||||
currentTeamId: 'current-team-id',
|
||||
},
|
||||
general: {
|
||||
config: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
baseProps.actions.applyTheme.mockImplementation(() => {
|
||||
store.dispatch(applyTheme());
|
||||
});
|
||||
|
||||
const wrapper = shallow(
|
||||
<Theme {...baseProps}/>,
|
||||
);
|
||||
|
||||
const theme = allowedThemes[0];
|
||||
wrapper.instance().setTheme(theme.key);
|
||||
expect(baseProps.actions.applyTheme).toHaveBeenCalledTimes(1);
|
||||
|
||||
const options = {
|
||||
topBar: {
|
||||
backButton: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
},
|
||||
background: {
|
||||
color: theme.sidebarHeaderBg,
|
||||
},
|
||||
title: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(Navigation.mergeOptions.mock.calls).toEqual([
|
||||
[componentIds[2], options],
|
||||
[componentIds[1], options],
|
||||
[componentIds[0], options],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,13 @@ class EphemeralStore {
|
||||
}
|
||||
|
||||
this.navigationComponentIdStack.unshift(componentId);
|
||||
};
|
||||
|
||||
removeNavigationComponentId = (componentId) => {
|
||||
const index = this.navigationComponentIdStack.indexOf(componentId);
|
||||
if (index >= 0) {
|
||||
this.navigationComponentIdStack.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ export function getScheme(url) {
|
||||
}
|
||||
|
||||
export function matchDeepLink(url, serverURL, siteURL) {
|
||||
if (!url || !serverURL || !siteURL) {
|
||||
if (!url || (!serverURL && !siteURL)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,11 @@ jest.mock('NativeModules', () => {
|
||||
END: 'END',
|
||||
},
|
||||
},
|
||||
RNKeychainManager: {
|
||||
SECURITY_LEVEL_ANY: 'ANY',
|
||||
SECURITY_LEVEL_SECURE_SOFTWARE: 'SOFTWARE',
|
||||
SECURITY_LEVEL_SECURE_HARDWARE: 'HARDWARE',
|
||||
},
|
||||
};
|
||||
});
|
||||
jest.mock('NativeEventEmitter');
|
||||
|
||||
Reference in New Issue
Block a user