forked from Ivasoft/mattermost-mobile
Apply new theme to all navigation components (#3021)
This commit is contained in:
committed by
Elias Nahum
parent
8887319324
commit
1f8e853e41
@@ -133,7 +133,7 @@ export function resetToTeams(name, title, passProps = {}, options = {}) {
|
||||
export function goToScreen(name, title, passProps = {}, options = {}) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const componentId = EphemeralStore.getTopComponentId();
|
||||
const componentId = EphemeralStore.getNavigationTopComponentId();
|
||||
const theme = getTheme(state);
|
||||
const defaultOptions = {
|
||||
layout: {
|
||||
@@ -168,7 +168,7 @@ export function goToScreen(name, title, passProps = {}, options = {}) {
|
||||
|
||||
export function popTopScreen() {
|
||||
return () => {
|
||||
const componentId = EphemeralStore.getTopComponentId();
|
||||
const componentId = EphemeralStore.getNavigationTopComponentId();
|
||||
|
||||
Navigation.pop(componentId);
|
||||
};
|
||||
@@ -176,7 +176,7 @@ export function popTopScreen() {
|
||||
|
||||
export function popToRoot() {
|
||||
return () => {
|
||||
const componentId = EphemeralStore.getTopComponentId();
|
||||
const componentId = EphemeralStore.getNavigationTopComponentId();
|
||||
|
||||
Navigation.popToRoot(componentId).catch(() => {
|
||||
// RNN returns a promise rejection if there are no screens
|
||||
@@ -286,7 +286,7 @@ export function showSearchModal(initialValue = '') {
|
||||
|
||||
export function dismissModal(options = {}) {
|
||||
return () => {
|
||||
const componentId = EphemeralStore.getTopComponentId();
|
||||
const componentId = EphemeralStore.getNavigationTopComponentId();
|
||||
|
||||
Navigation.dismissModal(componentId, options);
|
||||
};
|
||||
@@ -304,7 +304,7 @@ export function dismissAllModals(options = {}) {
|
||||
|
||||
export function peek(name, passProps = {}, options = {}) {
|
||||
return () => {
|
||||
const componentId = EphemeralStore.getTopComponentId();
|
||||
const componentId = EphemeralStore.getNavigationTopComponentId();
|
||||
const defaultOptions = {
|
||||
preview: {
|
||||
commit: false,
|
||||
@@ -359,3 +359,25 @@ export function dismissOverlay(componentId) {
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function applyTheme() {
|
||||
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,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -87,11 +87,8 @@ const launchAppAndAuthenticateIfNeeded = async () => {
|
||||
Navigation.events().registerAppLaunchedListener(() => {
|
||||
init();
|
||||
|
||||
// Keep track of the latest componentId to appear and disappear
|
||||
// Keep track of the latest componentId to appear
|
||||
Navigation.events().registerComponentDidAppearListener(({componentId}) => {
|
||||
EphemeralStore.addComponentIdToStack(componentId);
|
||||
});
|
||||
Navigation.events().registerComponentDidDisappearListener(({componentId}) => {
|
||||
EphemeralStore.removeComponentIdFromStack(componentId);
|
||||
EphemeralStore.addNavigationComponentId(componentId);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,12 +3,15 @@
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
||||
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
|
||||
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';
|
||||
|
||||
@@ -25,6 +28,7 @@ const mapStateToProps = (state) => ({
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
actions: bindActionCreators({
|
||||
savePreferences,
|
||||
applyTheme,
|
||||
}, dispatch),
|
||||
});
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ 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),
|
||||
@@ -61,7 +62,12 @@ export default class Theme extends React.PureComponent {
|
||||
}
|
||||
|
||||
setTheme = (key) => {
|
||||
const {userId, teamId, actions: {savePreferences}, allowedThemes} = this.props;
|
||||
const {
|
||||
userId,
|
||||
teamId,
|
||||
allowedThemes,
|
||||
actions: {savePreferences, applyTheme},
|
||||
} = this.props;
|
||||
const {customTheme} = this.state;
|
||||
const selectedTheme = allowedThemes.concat(customTheme).find((theme) => theme.key === key);
|
||||
|
||||
@@ -71,6 +77,7 @@ export default class Theme extends React.PureComponent {
|
||||
name: teamId,
|
||||
value: JSON.stringify(selectedTheme),
|
||||
}]);
|
||||
applyTheme();
|
||||
}
|
||||
|
||||
renderAllowedThemeTiles = () => {
|
||||
|
||||
@@ -3,15 +3,29 @@
|
||||
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import ThemeTile from './theme_tile';
|
||||
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',
|
||||
@@ -127,6 +141,7 @@ describe('Theme', () => {
|
||||
const baseProps = {
|
||||
actions: {
|
||||
savePreferences: jest.fn(),
|
||||
applyTheme: jest.fn(),
|
||||
},
|
||||
allowedThemes,
|
||||
isLandscape: false,
|
||||
@@ -144,4 +159,57 @@ 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],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,19 +6,19 @@ class EphemeralStore {
|
||||
this.appStarted = false;
|
||||
this.appStartedFromPushNotification = false;
|
||||
this.deviceToken = null;
|
||||
this.componentIdStack = [];
|
||||
this.navigationComponentIdStack = [];
|
||||
}
|
||||
|
||||
getTopComponentId = () => this.componentIdStack[0];
|
||||
getNavigationTopComponentId = () => this.navigationComponentIdStack[0];
|
||||
getNavigationComponentIds = () => this.navigationComponentIdStack;
|
||||
|
||||
addComponentIdToStack = (componentId) => {
|
||||
this.componentIdStack.unshift(componentId);
|
||||
}
|
||||
addNavigationComponentId = (componentId) => {
|
||||
const index = this.navigationComponentIdStack.indexOf(componentId);
|
||||
if (index > 0) {
|
||||
this.navigationComponentIdStack.slice(index, 1);
|
||||
}
|
||||
|
||||
removeComponentIdFromStack = (componentId) => {
|
||||
this.componentIdStack = this.componentIdStack.filter((id) => {
|
||||
return id !== componentId;
|
||||
});
|
||||
this.navigationComponentIdStack.unshift(componentId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user