diff --git a/Makefile b/Makefile
index 956503b0e..009459e66 100644
--- a/Makefile
+++ b/Makefile
@@ -95,7 +95,7 @@ post-install:
fi
@sed -i'' -e 's|transform: \[{scaleY: -1}\],|...Platform.select({android: {transform: \[{perspective: 1}, {scaleY: -1}\]}, ios: {transform: \[{scaleY: -1}\]}}),|g' node_modules/react-native/Libraries/Lists/VirtualizedList.js
- @./node_modules/.bin/patch-package --patch-dir=native_modules
+ @./node_modules/.bin/patch-package
start: | pre-run ## Starts the React Native packager server
$(call start_packager)
diff --git a/app/actions/navigation.js b/app/actions/navigation.js
deleted file mode 100644
index 11a186b89..000000000
--- a/app/actions/navigation.js
+++ /dev/null
@@ -1,394 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-import {Platform} from 'react-native';
-import {Navigation} from 'react-native-navigation';
-
-import merge from 'deepmerge';
-
-import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-
-import EphemeralStore from 'app/store/ephemeral_store';
-
-export function resetToChannel(passProps = {}) {
- return (dispatch, getState) => {
- const theme = getTheme(getState());
-
- Navigation.setRoot({
- root: {
- stack: {
- children: [{
- component: {
- name: 'Channel',
- passProps,
- options: {
- layout: {
- backgroundColor: 'transparent',
- },
- statusBar: {
- visible: true,
- },
- topBar: {
- visible: false,
- height: 0,
- backButton: {
- color: theme.sidebarHeaderTextColor,
- title: '',
- },
- background: {
- color: theme.sidebarHeaderBg,
- },
- title: {
- color: theme.sidebarHeaderTextColor,
- },
- },
- },
- },
- }],
- },
- },
- });
- };
-}
-
-export function resetToSelectServer(allowOtherServers) {
- return (dispatch, getState) => {
- const theme = getTheme(getState());
-
- Navigation.setRoot({
- root: {
- stack: {
- children: [{
- component: {
- name: 'SelectServer',
- passProps: {
- allowOtherServers,
- },
- options: {
- statusBar: {
- visible: true,
- },
- topBar: {
- backButton: {
- color: theme.sidebarHeaderTextColor,
- title: '',
- },
- background: {
- color: theme.sidebarHeaderBg,
- },
- visible: false,
- height: 0,
- },
- },
- },
- }],
- },
- },
- });
- };
-}
-
-export function resetToTeams(name, title, passProps = {}, options = {}) {
- return (dispatch, getState) => {
- const theme = getTheme(getState());
- const defaultOptions = {
- layout: {
- backgroundColor: theme.centerChannelBg,
- },
- statusBar: {
- visible: true,
- },
- topBar: {
- visible: true,
- title: {
- color: theme.sidebarHeaderTextColor,
- text: title,
- },
- backButton: {
- color: theme.sidebarHeaderTextColor,
- title: '',
- },
- background: {
- color: theme.sidebarHeaderBg,
- },
- },
- };
-
- Navigation.setRoot({
- root: {
- stack: {
- children: [{
- component: {
- name,
- passProps,
- options: merge(defaultOptions, options),
- },
- }],
- },
- },
- });
- };
-}
-
-export function goToScreen(name, title, passProps = {}, options = {}) {
- return (dispatch, getState) => {
- const state = getState();
- const componentId = EphemeralStore.getNavigationTopComponentId();
- const theme = getTheme(state);
- const defaultOptions = {
- layout: {
- backgroundColor: theme.centerChannelBg,
- },
- topBar: {
- animate: true,
- visible: true,
- backButton: {
- color: theme.sidebarHeaderTextColor,
- title: '',
- },
- background: {
- color: theme.sidebarHeaderBg,
- },
- title: {
- color: theme.sidebarHeaderTextColor,
- text: title,
- },
- },
- };
-
- Navigation.push(componentId, {
- component: {
- name,
- passProps,
- options: merge(defaultOptions, options),
- },
- });
- };
-}
-
-export function popTopScreen(screenId) {
- return () => {
- if (screenId) {
- Navigation.pop(screenId);
- } else {
- const componentId = EphemeralStore.getNavigationTopComponentId();
- Navigation.pop(componentId);
- }
- };
-}
-
-export function popToRoot() {
- return () => {
- const componentId = EphemeralStore.getNavigationTopComponentId();
-
- Navigation.popToRoot(componentId).catch(() => {
- // RNN returns a promise rejection if there are no screens
- // atop the root screen to pop. We'll do nothing in this
- // case but we will catch the rejection here so that the
- // caller doesn't have to.
- });
- };
-}
-
-export function showModal(name, title, passProps = {}, options = {}) {
- return (dispatch, getState) => {
- const theme = getTheme(getState());
- const defaultOptions = {
- layout: {
- backgroundColor: theme.centerChannelBg,
- },
- statusBar: {
- visible: true,
- },
- topBar: {
- animate: true,
- visible: true,
- backButton: {
- color: theme.sidebarHeaderTextColor,
- title: '',
- },
- background: {
- color: theme.sidebarHeaderBg,
- },
- title: {
- color: theme.sidebarHeaderTextColor,
- text: title,
- },
- leftButtonColor: theme.sidebarHeaderTextColor,
- rightButtonColor: theme.sidebarHeaderTextColor,
- },
- };
-
- Navigation.showModal({
- stack: {
- children: [{
- component: {
- name,
- passProps,
- options: merge(defaultOptions, options),
- },
- }],
- },
- });
- };
-}
-
-export function showModalOverCurrentContext(name, passProps = {}, options = {}) {
- return (dispatch) => {
- const title = '';
- const animationsEnabled = (Platform.OS === 'android').toString();
- const defaultOptions = {
- modalPresentationStyle: 'overCurrentContext',
- layout: {
- backgroundColor: 'transparent',
- },
- topBar: {
- visible: false,
- height: 0,
- },
- animations: {
- showModal: {
- enabled: animationsEnabled,
- alpha: {
- from: 0,
- to: 1,
- duration: 250,
- },
- },
- dismissModal: {
- enabled: animationsEnabled,
- alpha: {
- from: 1,
- to: 0,
- duration: 250,
- },
- },
- },
- };
- const mergeOptions = merge(defaultOptions, options);
-
- dispatch(showModal(name, title, passProps, mergeOptions));
- };
-}
-
-export function showSearchModal(initialValue = '') {
- return (dispatch) => {
- const name = 'Search';
- const title = '';
- const passProps = {initialValue};
- const options = {
- topBar: {
- visible: false,
- height: 0,
- },
- };
-
- dispatch(showModal(name, title, passProps, options));
- };
-}
-
-export function dismissModal(options = {}) {
- return () => {
- const componentId = EphemeralStore.getNavigationTopComponentId();
-
- Navigation.dismissModal(componentId, options).catch(() => {
- // RNN returns a promise rejection if there is no modal to
- // dismiss. We'll do nothing in this case but we will catch
- // the rejection here so that the caller doesn't have to.
- });
- };
-}
-
-export function dismissAllModals(options = {}) {
- return () => {
- Navigation.dismissAllModals(options).catch(() => {
- // RNN returns a promise rejection if there are no modals to
- // dismiss. We'll do nothing in this case but we will catch
- // the rejection here so that the caller doesn't have to.
- });
- };
-}
-
-export function peek(name, passProps = {}, options = {}) {
- return () => {
- const componentId = EphemeralStore.getNavigationTopComponentId();
- const defaultOptions = {
- preview: {
- commit: false,
- },
- };
-
- Navigation.push(componentId, {
- component: {
- name,
- passProps,
- options: merge(defaultOptions, options),
- },
- });
- };
-}
-
-export function setButtons(componentId, buttons = {leftButtons: [], rightButtons: []}) {
- return () => {
- Navigation.mergeOptions(componentId, {
- topBar: {
- ...buttons,
- },
- });
- };
-}
-
-export function showOverlay(name, passProps, options = {}) {
- return () => {
- const defaultOptions = {
- overlay: {
- interceptTouchOutside: false,
- },
- };
-
- Navigation.showOverlay({
- component: {
- name,
- passProps,
- options: merge(defaultOptions, options),
- },
- });
- };
-}
-
-export function dismissOverlay(componentId) {
- return () => {
- return Navigation.dismissOverlay(componentId).catch(() => {
- // RNN returns a promise rejection if there is no modal with
- // this componentId to dismiss. We'll do nothing in this case
- // but we will catch the rejection here so that the caller
- // doesn't have to.
- });
- };
-}
-
-export function applyTheme(componentId, skipBackButtonStyle = false) {
- return (dispatch, getState) => {
- const theme = getTheme(getState());
-
- let backButton = {
- color: theme.sidebarHeaderTextColor,
- };
-
- if (skipBackButtonStyle && Platform.OS === 'android') {
- backButton = null;
- }
-
- Navigation.mergeOptions(componentId, {
- topBar: {
- backButton,
- background: {
- color: theme.sidebarHeaderBg,
- },
- title: {
- color: theme.sidebarHeaderTextColor,
- },
- },
- });
- };
-}
diff --git a/app/actions/navigation/index.js b/app/actions/navigation/index.js
new file mode 100644
index 000000000..cfbacdb44
--- /dev/null
+++ b/app/actions/navigation/index.js
@@ -0,0 +1,352 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {Platform} from 'react-native';
+import {Navigation} from 'react-native-navigation';
+
+import merge from 'deepmerge';
+
+import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
+
+import store from 'app/store';
+import EphemeralStore from 'app/store/ephemeral_store';
+
+function getThemeFromState() {
+ const state = store.getState();
+
+ return getTheme(state);
+}
+
+export function resetToChannel(passProps = {}) {
+ const theme = getThemeFromState();
+
+ Navigation.setRoot({
+ root: {
+ stack: {
+ children: [{
+ component: {
+ name: 'Channel',
+ passProps,
+ options: {
+ layout: {
+ backgroundColor: 'transparent',
+ },
+ statusBar: {
+ visible: true,
+ },
+ topBar: {
+ visible: false,
+ height: 0,
+ backButton: {
+ color: theme.sidebarHeaderTextColor,
+ title: '',
+ },
+ background: {
+ color: theme.sidebarHeaderBg,
+ },
+ title: {
+ color: theme.sidebarHeaderTextColor,
+ },
+ },
+ },
+ },
+ }],
+ },
+ },
+ });
+}
+
+export function resetToSelectServer(allowOtherServers) {
+ const theme = getThemeFromState();
+
+ Navigation.setRoot({
+ root: {
+ stack: {
+ children: [{
+ component: {
+ name: 'SelectServer',
+ passProps: {
+ allowOtherServers,
+ },
+ options: {
+ statusBar: {
+ visible: true,
+ },
+ topBar: {
+ backButton: {
+ color: theme.sidebarHeaderTextColor,
+ title: '',
+ },
+ background: {
+ color: theme.sidebarHeaderBg,
+ },
+ visible: false,
+ height: 0,
+ },
+ },
+ },
+ }],
+ },
+ },
+ });
+}
+
+export function resetToTeams(name, title, passProps = {}, options = {}) {
+ const theme = getThemeFromState();
+ const defaultOptions = {
+ layout: {
+ backgroundColor: theme.centerChannelBg,
+ },
+ statusBar: {
+ visible: true,
+ },
+ topBar: {
+ visible: true,
+ title: {
+ color: theme.sidebarHeaderTextColor,
+ text: title,
+ },
+ backButton: {
+ color: theme.sidebarHeaderTextColor,
+ title: '',
+ },
+ background: {
+ color: theme.sidebarHeaderBg,
+ },
+ },
+ };
+
+ Navigation.setRoot({
+ root: {
+ stack: {
+ children: [{
+ component: {
+ name,
+ passProps,
+ options: merge(defaultOptions, options),
+ },
+ }],
+ },
+ },
+ });
+}
+
+export function goToScreen(name, title, passProps = {}, options = {}) {
+ const theme = getThemeFromState();
+ const componentId = EphemeralStore.getNavigationTopComponentId();
+ const defaultOptions = {
+ layout: {
+ backgroundColor: theme.centerChannelBg,
+ },
+ topBar: {
+ animate: true,
+ visible: true,
+ backButton: {
+ color: theme.sidebarHeaderTextColor,
+ title: '',
+ },
+ background: {
+ color: theme.sidebarHeaderBg,
+ },
+ title: {
+ color: theme.sidebarHeaderTextColor,
+ text: title,
+ },
+ },
+ };
+
+ Navigation.push(componentId, {
+ component: {
+ name,
+ passProps,
+ options: merge(defaultOptions, options),
+ },
+ });
+}
+
+export function popTopScreen(screenId) {
+ if (screenId) {
+ Navigation.pop(screenId);
+ } else {
+ const componentId = EphemeralStore.getNavigationTopComponentId();
+ Navigation.pop(componentId);
+ }
+}
+
+export async function popToRoot() {
+ const componentId = EphemeralStore.getNavigationTopComponentId();
+
+ try {
+ await Navigation.popToRoot(componentId);
+ } catch (error) {
+ // RNN returns a promise rejection if there are no screens
+ // atop the root screen to pop. We'll do nothing in this case.
+ }
+}
+
+export function showModal(name, title, passProps = {}, options = {}) {
+ const theme = getThemeFromState();
+ const defaultOptions = {
+ layout: {
+ backgroundColor: theme.centerChannelBg,
+ },
+ statusBar: {
+ visible: true,
+ },
+ topBar: {
+ animate: true,
+ visible: true,
+ backButton: {
+ color: theme.sidebarHeaderTextColor,
+ title: '',
+ },
+ background: {
+ color: theme.sidebarHeaderBg,
+ },
+ title: {
+ color: theme.sidebarHeaderTextColor,
+ text: title,
+ },
+ leftButtonColor: theme.sidebarHeaderTextColor,
+ rightButtonColor: theme.sidebarHeaderTextColor,
+ },
+ };
+
+ Navigation.showModal({
+ stack: {
+ children: [{
+ component: {
+ name,
+ passProps,
+ options: merge(defaultOptions, options),
+ },
+ }],
+ },
+ });
+}
+
+export function showModalOverCurrentContext(name, passProps = {}, options = {}) {
+ const title = '';
+ const animationsEnabled = (Platform.OS === 'android').toString();
+ const defaultOptions = {
+ modalPresentationStyle: 'overCurrentContext',
+ layout: {
+ backgroundColor: 'transparent',
+ },
+ topBar: {
+ visible: false,
+ height: 0,
+ },
+ animations: {
+ showModal: {
+ enabled: animationsEnabled,
+ alpha: {
+ from: 0,
+ to: 1,
+ duration: 250,
+ },
+ },
+ dismissModal: {
+ enabled: animationsEnabled,
+ alpha: {
+ from: 1,
+ to: 0,
+ duration: 250,
+ },
+ },
+ },
+ };
+ const mergeOptions = merge(defaultOptions, options);
+
+ showModal(name, title, passProps, mergeOptions);
+}
+
+export function showSearchModal(initialValue = '') {
+ const name = 'Search';
+ const title = '';
+ const passProps = {initialValue};
+ const options = {
+ topBar: {
+ visible: false,
+ height: 0,
+ },
+ };
+
+ showModal(name, title, passProps, options);
+}
+
+export async function dismissModal(options = {}) {
+ const componentId = EphemeralStore.getNavigationTopComponentId();
+
+ try {
+ await Navigation.dismissModal(componentId, options);
+ } catch (error) {
+ // RNN returns a promise rejection if there is no modal to
+ // dismiss. We'll do nothing in this case.
+ }
+}
+
+export async function dismissAllModals(options = {}) {
+ try {
+ await Navigation.dismissAllModals(options);
+ } catch (error) {
+ // RNN returns a promise rejection if there are no modals to
+ // dismiss. We'll do nothing in this case.
+ }
+}
+
+export function peek(name, passProps = {}, options = {}) {
+ const componentId = EphemeralStore.getNavigationTopComponentId();
+ const defaultOptions = {
+ preview: {
+ commit: false,
+ },
+ };
+
+ Navigation.push(componentId, {
+ component: {
+ name,
+ passProps,
+ options: merge(defaultOptions, options),
+ },
+ });
+}
+
+export function setButtons(componentId, buttons = {leftButtons: [], rightButtons: []}) {
+ const options = {
+ topBar: {
+ ...buttons,
+ },
+ };
+
+ mergeNavigationOptions(componentId, options);
+}
+
+export function mergeNavigationOptions(componentId, options) {
+ Navigation.mergeOptions(componentId, options);
+}
+
+export function showOverlay(name, passProps, options = {}) {
+ const defaultOptions = {
+ overlay: {
+ interceptTouchOutside: false,
+ },
+ };
+
+ Navigation.showOverlay({
+ component: {
+ name,
+ passProps,
+ options: merge(defaultOptions, options),
+ },
+ });
+}
+
+export async function dismissOverlay(componentId) {
+ try {
+ await Navigation.dismissOverlay(componentId);
+ } catch (error) {
+ // RNN returns a promise rejection if there is no modal with
+ // this componentId to dismiss. We'll do nothing in this case.
+ }
+}
diff --git a/app/actions/navigation/index.test.js b/app/actions/navigation/index.test.js
new file mode 100644
index 000000000..cdb5d93cc
--- /dev/null
+++ b/app/actions/navigation/index.test.js
@@ -0,0 +1,473 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {Platform} from 'react-native';
+import {Navigation} from 'react-native-navigation';
+
+import merge from 'deepmerge';
+
+import Preferences from 'mattermost-redux/constants/preferences';
+
+import EphemeralStore from 'app/store/ephemeral_store';
+import * as NavigationActions from 'app/actions/navigation';
+
+jest.unmock('app/actions/navigation');
+jest.mock('app/store/ephemeral_store', () => ({
+ getNavigationTopComponentId: jest.fn(),
+}));
+
+describe('app/actions/navigation', () => {
+ const topComponentId = 'top-component-id';
+ const name = 'name';
+ const title = 'title';
+ const theme = Preferences.THEMES.default;
+ const passProps = {
+ testProp: 'prop',
+ };
+ const options = {
+ testOption: 'test',
+ };
+ EphemeralStore.getNavigationTopComponentId.mockReturnValue(topComponentId);
+
+ test('resetToChannel should call Navigation.setRoot', () => {
+ const setRoot = jest.spyOn(Navigation, 'setRoot');
+
+ const expectedLayout = {
+ root: {
+ stack: {
+ children: [{
+ component: {
+ name: 'Channel',
+ passProps,
+ options: {
+ layout: {
+ backgroundColor: 'transparent',
+ },
+ statusBar: {
+ visible: true,
+ },
+ topBar: {
+ visible: false,
+ height: 0,
+ backButton: {
+ color: theme.sidebarHeaderTextColor,
+ title: '',
+ },
+ background: {
+ color: theme.sidebarHeaderBg,
+ },
+ title: {
+ color: theme.sidebarHeaderTextColor,
+ },
+ },
+ },
+ },
+ }],
+ },
+ },
+ };
+
+ NavigationActions.resetToChannel(passProps);
+ expect(setRoot).toHaveBeenCalledWith(expectedLayout);
+ });
+
+ test('resetToSelectServer should call Navigation.setRoot', () => {
+ const setRoot = jest.spyOn(Navigation, 'setRoot');
+
+ const allowOtherServers = false;
+ const expectedLayout = {
+ root: {
+ stack: {
+ children: [{
+ component: {
+ name: 'SelectServer',
+ passProps: {
+ allowOtherServers,
+ },
+ options: {
+ statusBar: {
+ visible: true,
+ },
+ topBar: {
+ backButton: {
+ color: theme.sidebarHeaderTextColor,
+ title: '',
+ },
+ background: {
+ color: theme.sidebarHeaderBg,
+ },
+ visible: false,
+ height: 0,
+ },
+ },
+ },
+ }],
+ },
+ },
+ };
+
+ NavigationActions.resetToSelectServer(allowOtherServers);
+ expect(setRoot).toHaveBeenCalledWith(expectedLayout);
+ });
+
+ test('resetToTeams should call Navigation.setRoot', () => {
+ const setRoot = jest.spyOn(Navigation, 'setRoot');
+
+ const defaultOptions = {
+ layout: {
+ backgroundColor: theme.centerChannelBg,
+ },
+ statusBar: {
+ visible: true,
+ },
+ topBar: {
+ visible: true,
+ title: {
+ color: theme.sidebarHeaderTextColor,
+ text: title,
+ },
+ backButton: {
+ color: theme.sidebarHeaderTextColor,
+ title: '',
+ },
+ background: {
+ color: theme.sidebarHeaderBg,
+ },
+ },
+ };
+
+ const expectedLayout = {
+ root: {
+ stack: {
+ children: [{
+ component: {
+ name,
+ passProps,
+ options: merge(defaultOptions, options),
+ },
+ }],
+ },
+ },
+ };
+
+ NavigationActions.resetToTeams(name, title, passProps, options);
+ expect(setRoot).toHaveBeenCalledWith(expectedLayout);
+ });
+
+ test('goToScreen should call Navigation.push', () => {
+ const push = jest.spyOn(Navigation, 'push');
+
+ const defaultOptions = {
+ layout: {
+ backgroundColor: theme.centerChannelBg,
+ },
+ topBar: {
+ animate: true,
+ visible: true,
+ backButton: {
+ color: theme.sidebarHeaderTextColor,
+ title: '',
+ },
+ background: {
+ color: theme.sidebarHeaderBg,
+ },
+ title: {
+ color: theme.sidebarHeaderTextColor,
+ text: title,
+ },
+ },
+ };
+
+ const expectedLayout = {
+ component: {
+ name,
+ passProps,
+ options: merge(defaultOptions, options),
+ },
+ };
+
+ NavigationActions.goToScreen(name, title, passProps, options);
+ expect(push).toHaveBeenCalledWith(topComponentId, expectedLayout);
+ });
+
+ test('popTopScreen should call Navigation.pop', () => {
+ const pop = jest.spyOn(Navigation, 'pop');
+
+ NavigationActions.popTopScreen();
+ expect(pop).toHaveBeenCalledWith(topComponentId);
+
+ const otherComponentId = `other-${topComponentId}`;
+ NavigationActions.popTopScreen(otherComponentId);
+ expect(pop).toHaveBeenCalledWith(otherComponentId);
+ });
+
+ test('popToRoot should call Navigation.popToRoot', async () => {
+ const popToRoot = jest.spyOn(Navigation, 'popToRoot');
+
+ await NavigationActions.popToRoot();
+ expect(popToRoot).toHaveBeenCalledWith(topComponentId);
+ });
+
+ test('showModal should call Navigation.showModal', () => {
+ const showModal = jest.spyOn(Navigation, 'showModal');
+
+ const defaultOptions = {
+ layout: {
+ backgroundColor: theme.centerChannelBg,
+ },
+ statusBar: {
+ visible: true,
+ },
+ topBar: {
+ animate: true,
+ visible: true,
+ backButton: {
+ color: theme.sidebarHeaderTextColor,
+ title: '',
+ },
+ background: {
+ color: theme.sidebarHeaderBg,
+ },
+ title: {
+ color: theme.sidebarHeaderTextColor,
+ text: title,
+ },
+ leftButtonColor: theme.sidebarHeaderTextColor,
+ rightButtonColor: theme.sidebarHeaderTextColor,
+ },
+ };
+
+ const expectedLayout = {
+ stack: {
+ children: [{
+ component: {
+ name,
+ passProps,
+ options: merge(defaultOptions, options),
+ },
+ }],
+ },
+ };
+
+ NavigationActions.showModal(name, title, passProps, options);
+ expect(showModal).toHaveBeenCalledWith(expectedLayout);
+ });
+
+ test('showModalOverCurrentContext should call Navigation.showModal', () => {
+ const showModal = jest.spyOn(Navigation, 'showModal');
+
+ const animationsEnabled = (Platform.OS === 'android').toString();
+ const showModalOverCurrentContextTitle = '';
+ const showModalOverCurrentContextOptions = {
+ modalPresentationStyle: 'overCurrentContext',
+ layout: {
+ backgroundColor: 'transparent',
+ },
+ topBar: {
+ visible: false,
+ height: 0,
+ },
+ animations: {
+ showModal: {
+ enabled: animationsEnabled,
+ alpha: {
+ from: 0,
+ to: 1,
+ duration: 250,
+ },
+ },
+ dismissModal: {
+ enabled: animationsEnabled,
+ alpha: {
+ from: 1,
+ to: 0,
+ duration: 250,
+ },
+ },
+ },
+ };
+ const showModalOptions = {
+ layout: {
+ backgroundColor: theme.centerChannelBg,
+ },
+ statusBar: {
+ visible: true,
+ },
+ topBar: {
+ animate: true,
+ visible: true,
+ backButton: {
+ color: theme.sidebarHeaderTextColor,
+ title: '',
+ },
+ background: {
+ color: theme.sidebarHeaderBg,
+ },
+ title: {
+ color: theme.sidebarHeaderTextColor,
+ text: showModalOverCurrentContextTitle,
+ },
+ leftButtonColor: theme.sidebarHeaderTextColor,
+ rightButtonColor: theme.sidebarHeaderTextColor,
+ },
+ };
+ const defaultOptions = merge(showModalOverCurrentContextOptions, options);
+
+ const expectedLayout = {
+ stack: {
+ children: [{
+ component: {
+ name,
+ passProps,
+ options: merge(showModalOptions, defaultOptions),
+ },
+ }],
+ },
+ };
+
+ NavigationActions.showModalOverCurrentContext(name, passProps, options);
+ expect(showModal).toHaveBeenCalledWith(expectedLayout);
+ });
+
+ test('showSearchModal should call Navigation.showModal', () => {
+ const showModal = jest.spyOn(Navigation, 'showModal');
+
+ const showSearchModalName = 'Search';
+ const showSearchModalTitle = '';
+ const initialValue = 'initial-value';
+ const showSearchModalPassProps = {initialValue};
+ const showSearchModalOptions = {
+ topBar: {
+ visible: false,
+ height: 0,
+ },
+ };
+ const defaultOptions = {
+ layout: {
+ backgroundColor: theme.centerChannelBg,
+ },
+ statusBar: {
+ visible: true,
+ },
+ topBar: {
+ animate: true,
+ visible: true,
+ backButton: {
+ color: theme.sidebarHeaderTextColor,
+ title: '',
+ },
+ background: {
+ color: theme.sidebarHeaderBg,
+ },
+ title: {
+ color: theme.sidebarHeaderTextColor,
+ text: showSearchModalTitle,
+ },
+ leftButtonColor: theme.sidebarHeaderTextColor,
+ rightButtonColor: theme.sidebarHeaderTextColor,
+ },
+ };
+
+ const expectedLayout = {
+ stack: {
+ children: [{
+ component: {
+ name: showSearchModalName,
+ passProps: showSearchModalPassProps,
+ options: merge(defaultOptions, showSearchModalOptions),
+ },
+ }],
+ },
+ };
+
+ NavigationActions.showSearchModal(initialValue);
+ expect(showModal).toHaveBeenCalledWith(expectedLayout);
+ });
+
+ test('dismissModal should call Navigation.dismissModal', async () => {
+ const dismissModal = jest.spyOn(Navigation, 'dismissModal');
+
+ await NavigationActions.dismissModal(options);
+ expect(dismissModal).toHaveBeenCalledWith(topComponentId, options);
+ });
+
+ test('dismissAllModals should call Navigation.dismissAllModals', async () => {
+ const dismissAllModals = jest.spyOn(Navigation, 'dismissAllModals');
+
+ await NavigationActions.dismissAllModals(options);
+ expect(dismissAllModals).toHaveBeenCalledWith(options);
+ });
+
+ test('peek should call Navigation.push', async () => {
+ const push = jest.spyOn(Navigation, 'push');
+
+ const defaultOptions = {
+ preview: {
+ commit: false,
+ },
+ };
+
+ const expectedLayout = {
+ component: {
+ name,
+ passProps,
+ options: merge(defaultOptions, options),
+ },
+ };
+
+ await NavigationActions.peek(name, passProps, options);
+ expect(push).toHaveBeenCalledWith(topComponentId, expectedLayout);
+ });
+
+ test('mergeNavigationOptions should call Navigation.mergeOptions', () => {
+ const mergeOptions = jest.spyOn(Navigation, 'mergeOptions');
+
+ NavigationActions.mergeNavigationOptions(topComponentId, options);
+ expect(mergeOptions).toHaveBeenCalledWith(topComponentId, options);
+ });
+
+ test('setButtons should call Navigation.mergeOptions', () => {
+ const mergeOptions = jest.spyOn(Navigation, 'mergeOptions');
+
+ const buttons = {
+ leftButtons: ['left-button'],
+ rightButtons: ['right-button'],
+ };
+ const setButtonsOptions = {
+ topBar: {
+ ...buttons,
+ },
+ };
+
+ NavigationActions.setButtons(topComponentId, buttons);
+ expect(mergeOptions).toHaveBeenCalledWith(topComponentId, setButtonsOptions);
+ });
+
+ test('showOverlay should call Navigation.showOverlay', () => {
+ const showOverlay = jest.spyOn(Navigation, 'showOverlay');
+
+ const defaultOptions = {
+ overlay: {
+ interceptTouchOutside: false,
+ },
+ };
+
+ const expectedLayout = {
+ component: {
+ name,
+ passProps,
+ options: merge(defaultOptions, options),
+ },
+ };
+
+ NavigationActions.showOverlay(name, passProps, options);
+ expect(showOverlay).toHaveBeenCalledWith(expectedLayout);
+ });
+
+ test('dismissOverlay should call Navigation.dismissOverlay', async () => {
+ const dismissOverlay = jest.spyOn(Navigation, 'dismissOverlay');
+
+ await NavigationActions.dismissOverlay(topComponentId);
+ expect(dismissOverlay).toHaveBeenCalledWith(topComponentId);
+ });
+});
\ No newline at end of file
diff --git a/app/actions/views/channel.js b/app/actions/views/channel.js
index 64b29a3c0..122901c02 100644
--- a/app/actions/views/channel.js
+++ b/app/actions/views/channel.js
@@ -10,7 +10,8 @@ import {
fetchMyChannelsAndMembers,
getChannelByNameAndTeamName,
markChannelAsRead,
- leaveChannel as serviceLeaveChannel, markChannelAsViewed,
+ markChannelAsViewed,
+ leaveChannel as serviceLeaveChannel,
selectChannel,
getChannelStats,
} from 'mattermost-redux/actions/channels';
diff --git a/app/actions/views/channel.test.js b/app/actions/views/channel.test.js
index 3775ad84b..3af66604b 100644
--- a/app/actions/views/channel.test.js
+++ b/app/actions/views/channel.test.js
@@ -20,6 +20,23 @@ jest.mock('mattermost-redux/selectors/entities/channels', () => ({
getMyChannelMember: () => ({data: {member: {}}}),
}));
+jest.mock('mattermost-redux/actions/channels', () => {
+ const channelActions = require.requireActual('mattermost-redux/actions/channels');
+ return {
+ ...channelActions,
+ markChannelAsRead: jest.fn(),
+ markChannelAsViewed: jest.fn(),
+ };
+});
+
+jest.mock('mattermost-redux/selectors/entities/teams', () => {
+ const teamSelectors = require.requireActual('mattermost-redux/selectors/entities/teams');
+ return {
+ ...teamSelectors,
+ getTeamByName: jest.fn(() => ({name: 'current-team-name'})),
+ };
+});
+
const mockStore = configureStore([thunk]);
describe('Actions.Views.Channel', () => {
diff --git a/app/components/__snapshots__/profile_picture_button.test.js.snap b/app/components/__snapshots__/profile_picture_button.test.js.snap
index c4205e2cf..7e959089f 100644
--- a/app/components/__snapshots__/profile_picture_button.test.js.snap
+++ b/app/components/__snapshots__/profile_picture_button.test.js.snap
@@ -1,13 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`profile_picture_button should match snapshot 1`] = `
-
`;
diff --git a/app/components/announcement_banner/announcement_banner.js b/app/components/announcement_banner/announcement_banner.js
index 2b239a8b2..2902f56ba 100644
--- a/app/components/announcement_banner/announcement_banner.js
+++ b/app/components/announcement_banner/announcement_banner.js
@@ -11,16 +11,15 @@ import {
} from 'react-native';
import {intlShape} from 'react-intl';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
+
import RemoveMarkdown from 'app/components/remove_markdown';
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
+import {goToScreen} from 'app/actions/navigation';
const {View: AnimatedView} = Animated;
export default class AnnouncementBanner extends PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- goToScreen: PropTypes.func.isRequired,
- }).isRequired,
bannerColor: PropTypes.string,
bannerDismissed: PropTypes.bool,
bannerEnabled: PropTypes.bool,
@@ -55,7 +54,6 @@ export default class AnnouncementBanner extends PureComponent {
}
handlePress = () => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'ExpandedAnnouncementBanner';
@@ -64,7 +62,7 @@ export default class AnnouncementBanner extends PureComponent {
defaultMessage: 'Announcement',
});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
};
toggleBanner = (show = true) => {
diff --git a/app/components/announcement_banner/announcement_banner.test.js b/app/components/announcement_banner/announcement_banner.test.js
index 390e62dec..7dccb5ebe 100644
--- a/app/components/announcement_banner/announcement_banner.test.js
+++ b/app/components/announcement_banner/announcement_banner.test.js
@@ -12,9 +12,6 @@ jest.useFakeTimers();
describe('AnnouncementBanner', () => {
const baseProps = {
- actions: {
- goToScreen: jest.fn(),
- },
bannerColor: '#ddd',
bannerDismissed: false,
bannerEnabled: true,
diff --git a/app/components/announcement_banner/index.js b/app/components/announcement_banner/index.js
index 346f08acc..8033244d0 100644
--- a/app/components/announcement_banner/index.js
+++ b/app/components/announcement_banner/index.js
@@ -1,13 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {goToScreen} from 'app/actions/navigation';
import {isLandscape} from 'app/selectors/device';
import AnnouncementBanner from './announcement_banner';
@@ -28,12 +26,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- goToScreen,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(AnnouncementBanner);
+export default connect(mapStateToProps)(AnnouncementBanner);
diff --git a/app/components/at_mention/at_mention.js b/app/components/at_mention/at_mention.js
index d34bae8ff..d6a985048 100644
--- a/app/components/at_mention/at_mention.js
+++ b/app/components/at_mention/at_mention.js
@@ -11,12 +11,10 @@ import {displayUsername} from 'mattermost-redux/utils/user_utils';
import CustomPropTypes from 'app/constants/custom_prop_types';
import mattermostManaged from 'app/mattermost_managed';
import BottomSheet from 'app/utils/bottom_sheet';
+import {goToScreen} from 'app/actions/navigation';
export default class AtMention extends React.PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- goToScreen: PropTypes.func.isRequired,
- }).isRequired,
isSearchResult: PropTypes.bool,
mentionName: PropTypes.string.isRequired,
mentionStyle: CustomPropTypes.Style,
@@ -50,7 +48,6 @@ export default class AtMention extends React.PureComponent {
}
goToUserProfile = () => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'UserProfile';
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
@@ -58,7 +55,7 @@ export default class AtMention extends React.PureComponent {
userId: this.state.user.id,
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
};
getUserDetailsFromMentionName(props) {
diff --git a/app/components/at_mention/index.js b/app/components/at_mention/index.js
index 857345ee5..07ac8c733 100644
--- a/app/components/at_mention/index.js
+++ b/app/components/at_mention/index.js
@@ -1,15 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getUsersByUsername} from 'mattermost-redux/selectors/entities/users';
import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {goToScreen} from 'app/actions/navigation';
-
import AtMention from './at_mention';
function mapStateToProps(state) {
@@ -20,12 +17,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- goToScreen,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(AtMention);
+export default connect(mapStateToProps)(AtMention);
diff --git a/app/components/attachment_button/__snapshots__/attachment_button.test.js.snap b/app/components/attachment_button/__snapshots__/index.test.js.snap
similarity index 100%
rename from app/components/attachment_button/__snapshots__/attachment_button.test.js.snap
rename to app/components/attachment_button/__snapshots__/index.test.js.snap
diff --git a/app/components/attachment_button/attachment_button.js b/app/components/attachment_button/attachment_button.js
deleted file mode 100644
index f6128a171..000000000
--- a/app/components/attachment_button/attachment_button.js
+++ /dev/null
@@ -1,538 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-import React, {PureComponent} from 'react';
-import PropTypes from 'prop-types';
-import {intlShape} from 'react-intl';
-import {
- Alert,
- NativeModules,
- Platform,
- StyleSheet,
-} from 'react-native';
-import RNFetchBlob from 'rn-fetch-blob';
-import DeviceInfo from 'react-native-device-info';
-import AndroidOpenSettings from 'react-native-android-open-settings';
-
-import Icon from 'react-native-vector-icons/Ionicons';
-import {DocumentPicker} from 'react-native-document-picker';
-import ImagePicker from 'react-native-image-picker';
-import Permissions from 'react-native-permissions';
-
-import {lookupMimeType} from 'mattermost-redux/utils/file_utils';
-
-import TouchableWithFeedback from 'app/components/touchable_with_feedback';
-import {PermissionTypes} from 'app/constants';
-import {changeOpacity} from 'app/utils/theme';
-import {t} from 'app/utils/i18n';
-
-const ShareExtension = NativeModules.MattermostShare;
-
-export default class AttachmentButton extends PureComponent {
- static propTypes = {
- actions: PropTypes.shape({
- showModalOverCurrentContext: PropTypes.func.isRequired,
- }).isRequired,
- blurTextBox: PropTypes.func.isRequired,
- browseFileTypes: PropTypes.string,
- validMimeTypes: PropTypes.array,
- canBrowseFiles: PropTypes.bool,
- canBrowsePhotoLibrary: PropTypes.bool,
- canBrowseVideoLibrary: PropTypes.bool,
- canTakePhoto: PropTypes.bool,
- canTakeVideo: PropTypes.bool,
- children: PropTypes.node,
- fileCount: PropTypes.number,
- maxFileCount: PropTypes.number.isRequired,
- maxFileSize: PropTypes.number.isRequired,
- onShowFileMaxWarning: PropTypes.func,
- onShowFileSizeWarning: PropTypes.func,
- onShowUnsupportedMimeTypeWarning: PropTypes.func,
- theme: PropTypes.object.isRequired,
- uploadFiles: PropTypes.func.isRequired,
- wrapper: PropTypes.bool,
- extraOptions: PropTypes.arrayOf(PropTypes.object),
- };
-
- static defaultProps = {
- browseFileTypes: Platform.OS === 'ios' ? 'public.item' : '*/*',
- validMimeTypes: [],
- canBrowseFiles: true,
- canBrowsePhotoLibrary: true,
- canBrowseVideoLibrary: true,
- canTakePhoto: true,
- canTakeVideo: true,
- maxFileCount: 5,
- extraOptions: null,
- };
-
- static contextTypes = {
- intl: intlShape.isRequired,
- };
-
- getPermissionDeniedMessage = (source, mediaType = '') => {
- const {formatMessage} = this.context.intl;
- const applicationName = DeviceInfo.getApplicationName();
- switch (source) {
- case 'camera': {
- if (mediaType === 'video') {
- return {
- title: formatMessage({
- id: 'mobile.camera_video_permission_denied_title',
- defaultMessage: '{applicationName} would like to access your camera',
- }, {applicationName}),
- text: formatMessage({
- id: 'mobile.camera_video_permission_denied_description',
- defaultMessage: 'Take videos and upload them to your Mattermost instance or save them to your device. Open Settings to grant Mattermost Read and Write access to your camera.',
- }),
- };
- }
-
- return {
- title: formatMessage({
- id: 'mobile.camera_photo_permission_denied_title',
- defaultMessage: '{applicationName} would like to access your camera',
- }, {applicationName}),
- text: formatMessage({
- id: 'mobile.camera_photo_permission_denied_description',
- defaultMessage: 'Take photos and upload them to your Mattermost instance or save them to your device. Open Settings to grant Mattermost Read and Write access to your camera.',
- }),
- };
- }
- case 'storage':
- return {
- title: formatMessage({
- id: 'mobile.storage_permission_denied_title',
- defaultMessage: '{applicationName} would like to access your files',
- }, {applicationName}),
- text: formatMessage({
- id: 'mobile.storage_permission_denied_description',
- defaultMessage: 'Upload files to your Mattermost instance. Open Settings to grant Mattermost Read and Write access to files on this device.',
- }),
- };
- case 'video':
- return {
- title: formatMessage({
- id: 'mobile.android.videos_permission_denied_title',
- defaultMessage: '{applicationName} would like to access your videos',
- }, {applicationName}),
- text: formatMessage({
- id: 'mobile.android.videos_permission_denied_description',
- defaultMessage: 'Upload videos to your Mattermost instance or save them to your device. Open Settings to grant Mattermost Read and Write access to your video library.',
- }),
- };
- case 'photo':
- default: {
- if (Platform.OS === 'android') {
- return {
- title: formatMessage({
- id: 'mobile.android.photos_permission_denied_title',
- defaultMessage: '{applicationName} would like to access your photos',
- }, {applicationName}),
- text: formatMessage({
- id: 'mobile.android.photos_permission_denied_description',
- defaultMessage: 'Upload photos to your Mattermost instance or save them to your device. Open Settings to grant Mattermost Read and Write access to your photo library.',
- }),
- };
- }
-
- return {
- title: formatMessage({
- id: 'mobile.ios.photos_permission_denied_title',
- defaultMessage: '{applicationName} would like to access your photos',
- }, {applicationName}),
- text: formatMessage({
- id: 'mobile.ios.photos_permission_denied_description',
- defaultMessage: 'Upload photos and videos to your Mattermost instance or save them to your device. Open Settings to grant Mattermost Read and Write access to your photo and video library.',
- }),
- };
- }
- }
- }
-
- attachPhotoFromCamera = () => {
- return this.attachFileFromCamera('camera', 'photo');
- };
-
- attachFileFromCamera = async (source, mediaType) => {
- const {formatMessage} = this.context.intl;
- const {title, text} = this.getPermissionDeniedMessage('camera', mediaType);
- const options = {
- quality: 0.8,
- videoQuality: 'high',
- noData: true,
- mediaType,
- storageOptions: {
- cameraRoll: true,
- waitUntilSaved: true,
- },
- permissionDenied: {
- title,
- text,
- reTryTitle: formatMessage({
- id: 'mobile.permission_denied_retry',
- defaultMessage: 'Settings',
- }),
- okTitle: formatMessage({id: 'mobile.permission_denied_dismiss', defaultMessage: 'Don\'t Allow'}),
- },
- };
-
- const hasCameraPermission = await this.hasPhotoPermission(source, mediaType);
-
- if (hasCameraPermission) {
- ImagePicker.launchCamera(options, (response) => {
- if (response.error || response.didCancel) {
- return;
- }
-
- this.uploadFiles([response]);
- });
- }
- };
-
- attachFileFromLibrary = async () => {
- const {formatMessage} = this.context.intl;
- const {title, text} = this.getPermissionDeniedMessage('photo');
- const options = {
- quality: 0.8,
- noData: true,
- permissionDenied: {
- title,
- text,
- reTryTitle: formatMessage({
- id: 'mobile.permission_denied_retry',
- defaultMessage: 'Settings',
- }),
- okTitle: formatMessage({id: 'mobile.permission_denied_dismiss', defaultMessage: 'Don\'t Allow'}),
- },
- };
-
- if (Platform.OS === 'ios') {
- options.mediaType = 'mixed';
- }
-
- const hasPhotoPermission = await this.hasPhotoPermission('photo');
-
- if (hasPhotoPermission) {
- ImagePicker.launchImageLibrary(options, (response) => {
- if (response.error || response.didCancel) {
- return;
- }
-
- this.uploadFiles([response]);
- });
- }
- };
-
- attachVideoFromCamera = () => {
- return this.attachFileFromCamera('camera', 'video');
- };
-
- attachVideoFromLibraryAndroid = () => {
- const {formatMessage} = this.context.intl;
- const {title, text} = this.getPermissionDeniedMessage('video');
- const options = {
- videoQuality: 'high',
- mediaType: 'video',
- noData: true,
- permissionDenied: {
- title,
- text,
- reTryTitle: formatMessage({
- id: 'mobile.permission_denied_retry',
- defaultMessage: 'Settings',
- }),
- okTitle: formatMessage({id: 'mobile.permission_denied_dismiss', defaultMessage: 'Don\'t Allow'}),
- },
- };
-
- ImagePicker.launchImageLibrary(options, (response) => {
- if (response.error || response.didCancel) {
- return;
- }
-
- this.uploadFiles([response]);
- });
- };
-
- attachFileFromFiles = async () => {
- const {browseFileTypes} = this.props;
- const hasPermission = await this.hasStoragePermission();
-
- if (hasPermission) {
- DocumentPicker.show({
- filetype: [browseFileTypes],
- }, async (error, res) => {
- if (error) {
- return;
- }
-
- if (Platform.OS === 'android') {
- // For android we need to retrieve the realPath in case the file being imported is from the cloud
- const newUri = await ShareExtension.getFilePath(res.uri);
- if (newUri.filePath) {
- res.uri = newUri.filePath;
- } else {
- return;
- }
- }
-
- // Decode file uri to get the actual path
- res.uri = decodeURIComponent(res.uri);
-
- this.uploadFiles([res]);
- });
- }
- };
-
- hasPhotoPermission = async (source, mediaType = '') => {
- if (Platform.OS === 'ios') {
- const {formatMessage} = this.context.intl;
- let permissionRequest;
- const targetSource = source || 'photo';
- const hasPermissionToStorage = await Permissions.check(targetSource);
-
- switch (hasPermissionToStorage) {
- case PermissionTypes.UNDETERMINED:
- permissionRequest = await Permissions.request(targetSource);
- if (permissionRequest !== PermissionTypes.AUTHORIZED) {
- return false;
- }
- break;
- case PermissionTypes.DENIED: {
- const canOpenSettings = await Permissions.canOpenSettings();
- let grantOption = null;
- if (canOpenSettings) {
- grantOption = {
- text: formatMessage({
- id: 'mobile.permission_denied_retry',
- defaultMessage: 'Settings',
- }),
- onPress: () => Permissions.openSettings(),
- };
- }
-
- const {title, text} = this.getPermissionDeniedMessage(source, mediaType);
-
- Alert.alert(
- title,
- text,
- [
- grantOption,
- {
- text: formatMessage({
- id: 'mobile.permission_denied_dismiss',
- defaultMessage: 'Don\'t Allow',
- }),
- },
- ]
- );
- return false;
- }
- }
- }
-
- return true;
- };
-
- hasStoragePermission = async () => {
- if (Platform.OS === 'android') {
- const {formatMessage} = this.context.intl;
- let permissionRequest;
- const hasPermissionToStorage = await Permissions.check('storage');
-
- switch (hasPermissionToStorage) {
- case PermissionTypes.UNDETERMINED:
- permissionRequest = await Permissions.request('storage');
- if (permissionRequest !== PermissionTypes.AUTHORIZED) {
- return false;
- }
- break;
- case PermissionTypes.DENIED: {
- const {title, text} = this.getPermissionDeniedMessage('storage');
-
- Alert.alert(
- title,
- text,
- [
- {
- text: formatMessage({
- id: 'mobile.permission_denied_dismiss',
- defaultMessage: 'Don\'t Allow',
- }),
- },
- {
- text: formatMessage({
- id: 'mobile.permission_denied_retry',
- defaultMessage: 'Settings',
- }),
- onPress: () => AndroidOpenSettings.appDetailsSettings(),
- },
- ]
- );
- return false;
- }
- }
- }
-
- return true;
- };
-
- uploadFiles = async (files) => {
- const file = files[0];
- if (!file.fileSize | !file.fileName) {
- const path = (file.path || file.uri).replace('file://', '');
- const fileInfo = await RNFetchBlob.fs.stat(path);
- file.fileSize = fileInfo.size;
- file.fileName = fileInfo.filename;
- }
-
- if (!file.type) {
- file.type = lookupMimeType(file.fileName);
- }
-
- const {validMimeTypes} = this.props;
- if (validMimeTypes.length && !validMimeTypes.includes(file.type)) {
- this.props.onShowUnsupportedMimeTypeWarning();
- } else if (file.fileSize > this.props.maxFileSize) {
- this.props.onShowFileSizeWarning(file.fileName);
- } else {
- this.props.uploadFiles(files);
- }
- };
-
- showFileAttachmentOptions = () => {
- const {
- canBrowseFiles,
- canBrowsePhotoLibrary,
- canBrowseVideoLibrary,
- canTakePhoto,
- canTakeVideo,
- fileCount,
- maxFileCount,
- onShowFileMaxWarning,
- extraOptions,
- actions,
- } = this.props;
-
- if (fileCount === maxFileCount) {
- onShowFileMaxWarning();
- return;
- }
-
- this.props.blurTextBox();
- const items = [];
-
- if (canTakePhoto) {
- items.push({
- action: this.attachPhotoFromCamera,
- text: {
- id: t('mobile.file_upload.camera_photo'),
- defaultMessage: 'Take Photo',
- },
- icon: 'camera',
- });
- }
-
- if (canTakeVideo) {
- items.push({
- action: this.attachVideoFromCamera,
- text: {
- id: t('mobile.file_upload.camera_video'),
- defaultMessage: 'Take Video',
- },
- icon: 'video-camera',
- });
- }
-
- if (canBrowsePhotoLibrary) {
- items.push({
- action: this.attachFileFromLibrary,
- text: {
- id: t('mobile.file_upload.library'),
- defaultMessage: 'Photo Library',
- },
- icon: 'photo',
- });
- }
-
- if (canBrowseVideoLibrary && Platform.OS === 'android') {
- items.push({
- action: this.attachVideoFromLibraryAndroid,
- text: {
- id: t('mobile.file_upload.video'),
- defaultMessage: 'Video Library',
- },
- icon: 'file-video-o',
- });
- }
-
- if (canBrowseFiles) {
- items.push({
- action: this.attachFileFromFiles,
- text: {
- id: t('mobile.file_upload.browse'),
- defaultMessage: 'Browse Files',
- },
- icon: 'file',
- });
- }
-
- if (extraOptions) {
- extraOptions.forEach((option) => {
- if (option !== null) {
- items.push(option);
- }
- });
- }
-
- actions.showModalOverCurrentContext('OptionsModal', {items});
- };
-
- render() {
- const {theme, wrapper, children} = this.props;
-
- if (wrapper) {
- return (
-
- {children}
-
- );
- }
-
- return (
-
-
-
- );
- }
-}
-
-const style = StyleSheet.create({
- attachIcon: {
- marginTop: Platform.select({
- ios: 2,
- android: 0,
- }),
- },
- buttonContainer: {
- height: Platform.select({
- ios: 34,
- android: 36,
- }),
- width: 45,
- alignItems: 'center',
- justifyContent: 'center',
- },
-});
diff --git a/app/components/attachment_button/index.js b/app/components/attachment_button/index.js
index d48573136..3bae7eecd 100644
--- a/app/components/attachment_button/index.js
+++ b/app/components/attachment_button/index.js
@@ -1,19 +1,535 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
+import React, {PureComponent} from 'react';
+import PropTypes from 'prop-types';
+import {intlShape} from 'react-intl';
+import {
+ Alert,
+ NativeModules,
+ Platform,
+ StyleSheet,
+} from 'react-native';
+import RNFetchBlob from 'rn-fetch-blob';
+import DeviceInfo from 'react-native-device-info';
+import AndroidOpenSettings from 'react-native-android-open-settings';
-import {bindActionCreators} from 'redux';
-import {connect} from 'react-redux';
+import Icon from 'react-native-vector-icons/Ionicons';
+import {DocumentPicker} from 'react-native-document-picker';
+import ImagePicker from 'react-native-image-picker';
+import Permissions from 'react-native-permissions';
+import {lookupMimeType} from 'mattermost-redux/utils/file_utils';
+
+import TouchableWithFeedback from 'app/components/touchable_with_feedback';
+import {PermissionTypes} from 'app/constants';
+import {changeOpacity} from 'app/utils/theme';
+import {t} from 'app/utils/i18n';
import {showModalOverCurrentContext} from 'app/actions/navigation';
-import AttachmentButton from './attachment_button';
+const ShareExtension = NativeModules.MattermostShare;
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- showModalOverCurrentContext,
- }, dispatch),
+export default class AttachmentButton extends PureComponent {
+ static propTypes = {
+ blurTextBox: PropTypes.func.isRequired,
+ browseFileTypes: PropTypes.string,
+ validMimeTypes: PropTypes.array,
+ canBrowseFiles: PropTypes.bool,
+ canBrowsePhotoLibrary: PropTypes.bool,
+ canBrowseVideoLibrary: PropTypes.bool,
+ canTakePhoto: PropTypes.bool,
+ canTakeVideo: PropTypes.bool,
+ children: PropTypes.node,
+ fileCount: PropTypes.number,
+ maxFileCount: PropTypes.number.isRequired,
+ maxFileSize: PropTypes.number.isRequired,
+ onShowFileMaxWarning: PropTypes.func,
+ onShowFileSizeWarning: PropTypes.func,
+ onShowUnsupportedMimeTypeWarning: PropTypes.func,
+ theme: PropTypes.object.isRequired,
+ uploadFiles: PropTypes.func.isRequired,
+ wrapper: PropTypes.bool,
+ extraOptions: PropTypes.arrayOf(PropTypes.object),
};
+
+ static defaultProps = {
+ browseFileTypes: Platform.OS === 'ios' ? 'public.item' : '*/*',
+ validMimeTypes: [],
+ canBrowseFiles: true,
+ canBrowsePhotoLibrary: true,
+ canBrowseVideoLibrary: true,
+ canTakePhoto: true,
+ canTakeVideo: true,
+ maxFileCount: 5,
+ extraOptions: null,
+ };
+
+ static contextTypes = {
+ intl: intlShape.isRequired,
+ };
+
+ getPermissionDeniedMessage = (source, mediaType = '') => {
+ const {formatMessage} = this.context.intl;
+ const applicationName = DeviceInfo.getApplicationName();
+ switch (source) {
+ case 'camera': {
+ if (mediaType === 'video') {
+ return {
+ title: formatMessage({
+ id: 'mobile.camera_video_permission_denied_title',
+ defaultMessage: '{applicationName} would like to access your camera',
+ }, {applicationName}),
+ text: formatMessage({
+ id: 'mobile.camera_video_permission_denied_description',
+ defaultMessage: 'Take videos and upload them to your Mattermost instance or save them to your device. Open Settings to grant Mattermost Read and Write access to your camera.',
+ }),
+ };
+ }
+
+ return {
+ title: formatMessage({
+ id: 'mobile.camera_photo_permission_denied_title',
+ defaultMessage: '{applicationName} would like to access your camera',
+ }, {applicationName}),
+ text: formatMessage({
+ id: 'mobile.camera_photo_permission_denied_description',
+ defaultMessage: 'Take photos and upload them to your Mattermost instance or save them to your device. Open Settings to grant Mattermost Read and Write access to your camera.',
+ }),
+ };
+ }
+ case 'storage':
+ return {
+ title: formatMessage({
+ id: 'mobile.storage_permission_denied_title',
+ defaultMessage: '{applicationName} would like to access your files',
+ }, {applicationName}),
+ text: formatMessage({
+ id: 'mobile.storage_permission_denied_description',
+ defaultMessage: 'Upload files to your Mattermost instance. Open Settings to grant Mattermost Read and Write access to files on this device.',
+ }),
+ };
+ case 'video':
+ return {
+ title: formatMessage({
+ id: 'mobile.android.videos_permission_denied_title',
+ defaultMessage: '{applicationName} would like to access your videos',
+ }, {applicationName}),
+ text: formatMessage({
+ id: 'mobile.android.videos_permission_denied_description',
+ defaultMessage: 'Upload videos to your Mattermost instance or save them to your device. Open Settings to grant Mattermost Read and Write access to your video library.',
+ }),
+ };
+ case 'photo':
+ default: {
+ if (Platform.OS === 'android') {
+ return {
+ title: formatMessage({
+ id: 'mobile.android.photos_permission_denied_title',
+ defaultMessage: '{applicationName} would like to access your photos',
+ }, {applicationName}),
+ text: formatMessage({
+ id: 'mobile.android.photos_permission_denied_description',
+ defaultMessage: 'Upload photos to your Mattermost instance or save them to your device. Open Settings to grant Mattermost Read and Write access to your photo library.',
+ }),
+ };
+ }
+
+ return {
+ title: formatMessage({
+ id: 'mobile.ios.photos_permission_denied_title',
+ defaultMessage: '{applicationName} would like to access your photos',
+ }, {applicationName}),
+ text: formatMessage({
+ id: 'mobile.ios.photos_permission_denied_description',
+ defaultMessage: 'Upload photos and videos to your Mattermost instance or save them to your device. Open Settings to grant Mattermost Read and Write access to your photo and video library.',
+ }),
+ };
+ }
+ }
+ }
+
+ attachPhotoFromCamera = () => {
+ return this.attachFileFromCamera('camera', 'photo');
+ };
+
+ attachFileFromCamera = async (source, mediaType) => {
+ const {formatMessage} = this.context.intl;
+ const {title, text} = this.getPermissionDeniedMessage('camera', mediaType);
+ const options = {
+ quality: 0.8,
+ videoQuality: 'high',
+ noData: true,
+ mediaType,
+ storageOptions: {
+ cameraRoll: true,
+ waitUntilSaved: true,
+ },
+ permissionDenied: {
+ title,
+ text,
+ reTryTitle: formatMessage({
+ id: 'mobile.permission_denied_retry',
+ defaultMessage: 'Settings',
+ }),
+ okTitle: formatMessage({id: 'mobile.permission_denied_dismiss', defaultMessage: 'Don\'t Allow'}),
+ },
+ };
+
+ const hasCameraPermission = await this.hasPhotoPermission(source, mediaType);
+
+ if (hasCameraPermission) {
+ ImagePicker.launchCamera(options, (response) => {
+ if (response.error || response.didCancel) {
+ return;
+ }
+
+ this.uploadFiles([response]);
+ });
+ }
+ };
+
+ attachFileFromLibrary = async () => {
+ const {formatMessage} = this.context.intl;
+ const {title, text} = this.getPermissionDeniedMessage('photo');
+ const options = {
+ quality: 0.8,
+ noData: true,
+ permissionDenied: {
+ title,
+ text,
+ reTryTitle: formatMessage({
+ id: 'mobile.permission_denied_retry',
+ defaultMessage: 'Settings',
+ }),
+ okTitle: formatMessage({id: 'mobile.permission_denied_dismiss', defaultMessage: 'Don\'t Allow'}),
+ },
+ };
+
+ if (Platform.OS === 'ios') {
+ options.mediaType = 'mixed';
+ }
+
+ const hasPhotoPermission = await this.hasPhotoPermission('photo');
+
+ if (hasPhotoPermission) {
+ ImagePicker.launchImageLibrary(options, (response) => {
+ if (response.error || response.didCancel) {
+ return;
+ }
+
+ this.uploadFiles([response]);
+ });
+ }
+ };
+
+ attachVideoFromCamera = () => {
+ return this.attachFileFromCamera('camera', 'video');
+ };
+
+ attachVideoFromLibraryAndroid = () => {
+ const {formatMessage} = this.context.intl;
+ const {title, text} = this.getPermissionDeniedMessage('video');
+ const options = {
+ videoQuality: 'high',
+ mediaType: 'video',
+ noData: true,
+ permissionDenied: {
+ title,
+ text,
+ reTryTitle: formatMessage({
+ id: 'mobile.permission_denied_retry',
+ defaultMessage: 'Settings',
+ }),
+ okTitle: formatMessage({id: 'mobile.permission_denied_dismiss', defaultMessage: 'Don\'t Allow'}),
+ },
+ };
+
+ ImagePicker.launchImageLibrary(options, (response) => {
+ if (response.error || response.didCancel) {
+ return;
+ }
+
+ this.uploadFiles([response]);
+ });
+ };
+
+ attachFileFromFiles = async () => {
+ const {browseFileTypes} = this.props;
+ const hasPermission = await this.hasStoragePermission();
+
+ if (hasPermission) {
+ DocumentPicker.show({
+ filetype: [browseFileTypes],
+ }, async (error, res) => {
+ if (error) {
+ return;
+ }
+
+ if (Platform.OS === 'android') {
+ // For android we need to retrieve the realPath in case the file being imported is from the cloud
+ const newUri = await ShareExtension.getFilePath(res.uri);
+ if (newUri.filePath) {
+ res.uri = newUri.filePath;
+ } else {
+ return;
+ }
+ }
+
+ // Decode file uri to get the actual path
+ res.uri = decodeURIComponent(res.uri);
+
+ this.uploadFiles([res]);
+ });
+ }
+ };
+
+ hasPhotoPermission = async (source, mediaType = '') => {
+ if (Platform.OS === 'ios') {
+ const {formatMessage} = this.context.intl;
+ let permissionRequest;
+ const targetSource = source || 'photo';
+ const hasPermissionToStorage = await Permissions.check(targetSource);
+
+ switch (hasPermissionToStorage) {
+ case PermissionTypes.UNDETERMINED:
+ permissionRequest = await Permissions.request(targetSource);
+ if (permissionRequest !== PermissionTypes.AUTHORIZED) {
+ return false;
+ }
+ break;
+ case PermissionTypes.DENIED: {
+ const canOpenSettings = await Permissions.canOpenSettings();
+ let grantOption = null;
+ if (canOpenSettings) {
+ grantOption = {
+ text: formatMessage({
+ id: 'mobile.permission_denied_retry',
+ defaultMessage: 'Settings',
+ }),
+ onPress: () => Permissions.openSettings(),
+ };
+ }
+
+ const {title, text} = this.getPermissionDeniedMessage(source, mediaType);
+
+ Alert.alert(
+ title,
+ text,
+ [
+ grantOption,
+ {
+ text: formatMessage({
+ id: 'mobile.permission_denied_dismiss',
+ defaultMessage: 'Don\'t Allow',
+ }),
+ },
+ ]
+ );
+ return false;
+ }
+ }
+ }
+
+ return true;
+ };
+
+ hasStoragePermission = async () => {
+ if (Platform.OS === 'android') {
+ const {formatMessage} = this.context.intl;
+ let permissionRequest;
+ const hasPermissionToStorage = await Permissions.check('storage');
+
+ switch (hasPermissionToStorage) {
+ case PermissionTypes.UNDETERMINED:
+ permissionRequest = await Permissions.request('storage');
+ if (permissionRequest !== PermissionTypes.AUTHORIZED) {
+ return false;
+ }
+ break;
+ case PermissionTypes.DENIED: {
+ const {title, text} = this.getPermissionDeniedMessage('storage');
+
+ Alert.alert(
+ title,
+ text,
+ [
+ {
+ text: formatMessage({
+ id: 'mobile.permission_denied_dismiss',
+ defaultMessage: 'Don\'t Allow',
+ }),
+ },
+ {
+ text: formatMessage({
+ id: 'mobile.permission_denied_retry',
+ defaultMessage: 'Settings',
+ }),
+ onPress: () => AndroidOpenSettings.appDetailsSettings(),
+ },
+ ]
+ );
+ return false;
+ }
+ }
+ }
+
+ return true;
+ };
+
+ uploadFiles = async (files) => {
+ const file = files[0];
+ if (!file.fileSize | !file.fileName) {
+ const path = (file.path || file.uri).replace('file://', '');
+ const fileInfo = await RNFetchBlob.fs.stat(path);
+ file.fileSize = fileInfo.size;
+ file.fileName = fileInfo.filename;
+ }
+
+ if (!file.type) {
+ file.type = lookupMimeType(file.fileName);
+ }
+
+ const {validMimeTypes} = this.props;
+ if (validMimeTypes.length && !validMimeTypes.includes(file.type)) {
+ this.props.onShowUnsupportedMimeTypeWarning();
+ } else if (file.fileSize > this.props.maxFileSize) {
+ this.props.onShowFileSizeWarning(file.fileName);
+ } else {
+ this.props.uploadFiles(files);
+ }
+ };
+
+ showFileAttachmentOptions = () => {
+ const {
+ canBrowseFiles,
+ canBrowsePhotoLibrary,
+ canBrowseVideoLibrary,
+ canTakePhoto,
+ canTakeVideo,
+ fileCount,
+ maxFileCount,
+ onShowFileMaxWarning,
+ extraOptions,
+ } = this.props;
+
+ if (fileCount === maxFileCount) {
+ onShowFileMaxWarning();
+ return;
+ }
+
+ this.props.blurTextBox();
+ const items = [];
+
+ if (canTakePhoto) {
+ items.push({
+ action: this.attachPhotoFromCamera,
+ text: {
+ id: t('mobile.file_upload.camera_photo'),
+ defaultMessage: 'Take Photo',
+ },
+ icon: 'camera',
+ });
+ }
+
+ if (canTakeVideo) {
+ items.push({
+ action: this.attachVideoFromCamera,
+ text: {
+ id: t('mobile.file_upload.camera_video'),
+ defaultMessage: 'Take Video',
+ },
+ icon: 'video-camera',
+ });
+ }
+
+ if (canBrowsePhotoLibrary) {
+ items.push({
+ action: this.attachFileFromLibrary,
+ text: {
+ id: t('mobile.file_upload.library'),
+ defaultMessage: 'Photo Library',
+ },
+ icon: 'photo',
+ });
+ }
+
+ if (canBrowseVideoLibrary && Platform.OS === 'android') {
+ items.push({
+ action: this.attachVideoFromLibraryAndroid,
+ text: {
+ id: t('mobile.file_upload.video'),
+ defaultMessage: 'Video Library',
+ },
+ icon: 'file-video-o',
+ });
+ }
+
+ if (canBrowseFiles) {
+ items.push({
+ action: this.attachFileFromFiles,
+ text: {
+ id: t('mobile.file_upload.browse'),
+ defaultMessage: 'Browse Files',
+ },
+ icon: 'file',
+ });
+ }
+
+ if (extraOptions) {
+ extraOptions.forEach((option) => {
+ if (option !== null) {
+ items.push(option);
+ }
+ });
+ }
+
+ showModalOverCurrentContext('OptionsModal', {items});
+ };
+
+ render() {
+ const {theme, wrapper, children} = this.props;
+
+ if (wrapper) {
+ return (
+
+ {children}
+
+ );
+ }
+
+ return (
+
+
+
+ );
+ }
}
-export default connect(null, mapDispatchToProps)(AttachmentButton);
+const style = StyleSheet.create({
+ attachIcon: {
+ marginTop: Platform.select({
+ ios: 2,
+ android: 0,
+ }),
+ },
+ buttonContainer: {
+ height: Platform.select({
+ ios: 34,
+ android: 36,
+ }),
+ width: 45,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+});
\ No newline at end of file
diff --git a/app/components/attachment_button/attachment_button.test.js b/app/components/attachment_button/index.test.js
similarity index 95%
rename from app/components/attachment_button/attachment_button.test.js
rename to app/components/attachment_button/index.test.js
index 1c0f52128..ceaabdd0f 100644
--- a/app/components/attachment_button/attachment_button.test.js
+++ b/app/components/attachment_button/index.test.js
@@ -9,9 +9,10 @@ import {Alert} from 'react-native';
import Preferences from 'mattermost-redux/constants/preferences';
import {VALID_MIME_TYPES} from 'app/screens/edit_profile/edit_profile';
-import AttachmentButton from './attachment_button';
import {PermissionTypes} from 'app/constants';
+import AttachmentButton from './index';
+
jest.mock('react-intl');
jest.mock('Platform', () => {
@@ -23,9 +24,6 @@ jest.mock('Platform', () => {
describe('AttachmentButton', () => {
const formatMessage = jest.fn();
const baseProps = {
- actions: {
- showModalOverCurrentContext: jest.fn(),
- },
theme: Preferences.THEMES.default,
blurTextBox: jest.fn(),
maxFileSize: 10,
@@ -99,4 +97,4 @@ describe('AttachmentButton', () => {
await wrapper.instance().hasPhotoPermission('camera');
expect(Alert.alert).toBeCalled();
});
-});
+});
\ No newline at end of file
diff --git a/app/components/autocomplete_selector/autocomplete_selector.js b/app/components/autocomplete_selector/autocomplete_selector.js
index 9b8d24754..ce9b75459 100644
--- a/app/components/autocomplete_selector/autocomplete_selector.js
+++ b/app/components/autocomplete_selector/autocomplete_selector.js
@@ -14,12 +14,12 @@ import TouchableWithFeedback from 'app/components/touchable_with_feedback';
import {preventDoubleTap} from 'app/utils/tap';
import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
import {ViewTypes} from 'app/constants';
+import {goToScreen} from 'app/actions/navigation';
export default class AutocompleteSelector extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
setAutocompleteSelector: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
}).isRequired,
label: PropTypes.string,
placeholder: PropTypes.string.isRequired,
@@ -102,7 +102,7 @@ export default class AutocompleteSelector extends PureComponent {
const title = placeholder || formatMessage({id: 'mobile.action_menu.select', defaultMessage: 'Select an option'});
actions.setAutocompleteSelector(dataSource, this.handleSelect, options);
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
});
render() {
diff --git a/app/components/autocomplete_selector/index.js b/app/components/autocomplete_selector/index.js
index afb4c445c..dc77afea9 100644
--- a/app/components/autocomplete_selector/index.js
+++ b/app/components/autocomplete_selector/index.js
@@ -6,7 +6,6 @@ import {connect} from 'react-redux';
import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {goToScreen} from 'app/actions/navigation';
import {setAutocompleteSelector} from 'app/actions/views/post';
import AutocompleteSelector from './autocomplete_selector';
@@ -22,7 +21,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
setAutocompleteSelector,
- goToScreen,
}, dispatch),
};
}
diff --git a/app/components/channel_intro/channel_intro.js b/app/components/channel_intro/channel_intro.js
index 9efe499f2..a2ae4392f 100644
--- a/app/components/channel_intro/channel_intro.js
+++ b/app/components/channel_intro/channel_intro.js
@@ -7,9 +7,10 @@ import {
Text,
View,
} from 'react-native';
+import {injectIntl, intlShape} from 'react-intl';
+
import {getFullName} from 'mattermost-redux/utils/user_utils';
import {General} from 'mattermost-redux/constants';
-import {injectIntl, intlShape} from 'react-intl';
import BotTag from 'app/components/bot_tag';
import GuestTag from 'app/components/guest_tag';
@@ -20,12 +21,10 @@ import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
import {isGuest} from 'app/utils/users';
+import {goToScreen} from 'app/actions/navigation';
class ChannelIntro extends PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- goToScreen: PropTypes.func.isRequired,
- }).isRequired,
creator: PropTypes.object,
currentChannel: PropTypes.object.isRequired,
currentChannelMembers: PropTypes.array.isRequired,
@@ -39,14 +38,14 @@ class ChannelIntro extends PureComponent {
};
goToUserProfile = (userId) => {
- const {actions, intl} = this.props;
+ const {intl} = this.props;
const screen = 'UserProfile';
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
const passProps = {
userId,
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
};
getDisplayName = (member) => {
diff --git a/app/components/channel_intro/index.js b/app/components/channel_intro/index.js
index aa10a1525..3a496f125 100644
--- a/app/components/channel_intro/index.js
+++ b/app/components/channel_intro/index.js
@@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {createSelector} from 'reselect';
@@ -11,7 +10,6 @@ import {getCurrentUserId, getUser, makeGetProfilesInChannel} from 'mattermost-re
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {isLandscape} from 'app/selectors/device';
-import {goToScreen} from 'app/actions/navigation';
import {getChannelMembersForDm} from 'app/selectors/channel';
import ChannelIntro from './channel_intro';
@@ -55,12 +53,4 @@ function makeMapStateToProps() {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- goToScreen,
- }, dispatch),
- };
-}
-
-export default connect(makeMapStateToProps, mapDispatchToProps)(ChannelIntro);
+export default connect(makeMapStateToProps)(ChannelIntro);
diff --git a/app/components/channel_link/channel_link.js b/app/components/channel_link/channel_link.js
index de35dac4e..ed1e7f70c 100644
--- a/app/components/channel_link/channel_link.js
+++ b/app/components/channel_link/channel_link.js
@@ -9,6 +9,7 @@ import {intlShape} from 'react-intl';
import CustomPropTypes from 'app/constants/custom_prop_types';
import {t} from 'app/utils/i18n';
import {alertErrorWithFallback} from 'app/utils/general';
+import {popToRoot, dismissAllModals} from 'app/actions/navigation';
import {getChannelFromChannelName} from './channel_link_utils';
@@ -23,10 +24,8 @@ export default class ChannelLink extends React.PureComponent {
textStyle: CustomPropTypes.Style,
channelsByName: PropTypes.object.isRequired,
actions: PropTypes.shape({
- dismissAllModals: PropTypes.func.isRequired,
handleSelectChannel: PropTypes.func.isRequired,
joinChannel: PropTypes.func.isRequired,
- popToRoot: PropTypes.func.isRequired,
}).isRequired,
};
@@ -75,10 +74,11 @@ export default class ChannelLink extends React.PureComponent {
}
if (channel.id) {
- const {dismissAllModals, handleSelectChannel, popToRoot} = this.props.actions;
+ const {handleSelectChannel} = this.props.actions;
handleSelectChannel(channel.id);
- dismissAllModals();
- popToRoot();
+
+ await dismissAllModals();
+ await popToRoot();
if (this.props.onChannelLinkPress) {
this.props.onChannelLinkPress(channel);
diff --git a/app/components/channel_link/channel_link.test.js b/app/components/channel_link/channel_link.test.js
index a79abfad7..e8aab6b58 100644
--- a/app/components/channel_link/channel_link.test.js
+++ b/app/components/channel_link/channel_link.test.js
@@ -11,9 +11,13 @@ import ChannelLink from './channel_link';
jest.mock('react-intl');
-jest.mock('app/utils/general', () => ({
- alertErrorWithFallback: jest.fn(),
-}));
+jest.mock('app/utils/general', () => {
+ const general = require.requireActual('app/utils/general');
+ return {
+ ...general,
+ alertErrorWithFallback: jest.fn(),
+ };
+});
describe('ChannelLink', () => {
const formatMessage = jest.fn();
@@ -30,10 +34,8 @@ describe('ChannelLink', () => {
textStyle: {color: '#3d3c40', fontSize: 15, lineHeight: 20},
channelsByName,
actions: {
- dismissAllModals: jest.fn(),
handleSelectChannel: jest.fn(),
joinChannel: jest.fn(),
- popToRoot: jest.fn(),
},
};
@@ -71,14 +73,14 @@ describe('ChannelLink', () => {
expect(innerText.props().onPress).not.toBeDefined();
});
- test('should call props.actions and onChannelLinkPress on handlePress', () => {
+ test('should call props.actions and onChannelLinkPress on handlePress', async () => {
const wrapper = shallow(
,
{context: {intl: {formatMessage}}},
);
const channel = channelsByName.firstChannel;
- wrapper.instance().handlePress();
+ await wrapper.instance().handlePress();
expect(baseProps.actions.handleSelectChannel).toHaveBeenCalledTimes(1);
expect(baseProps.actions.handleSelectChannel).toBeCalledWith(channel.id);
expect(baseProps.onChannelLinkPress).toHaveBeenCalledTimes(1);
diff --git a/app/components/channel_link/index.js b/app/components/channel_link/index.js
index 174c2b93f..6b2ed9edd 100644
--- a/app/components/channel_link/index.js
+++ b/app/components/channel_link/index.js
@@ -10,7 +10,6 @@ import {getChannelsNameMapInCurrentTeam} from 'mattermost-redux/selectors/entiti
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
-import {dismissAllModals, popToRoot} from 'app/actions/navigation';
import {handleSelectChannel} from 'app/actions/views/channel';
import ChannelLink from './channel_link';
@@ -44,10 +43,8 @@ function makeMapStateToProps() {
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
- dismissAllModals,
handleSelectChannel,
joinChannel,
- popToRoot,
}, dispatch),
};
}
diff --git a/app/components/client_upgrade_listener/client_upgrade_listener.js b/app/components/client_upgrade_listener/client_upgrade_listener.js
index d7bb98eec..e633c4aab 100644
--- a/app/components/client_upgrade_listener/client_upgrade_listener.js
+++ b/app/components/client_upgrade_listener/client_upgrade_listener.js
@@ -17,6 +17,7 @@ import FormattedText from 'app/components/formatted_text';
import {DeviceTypes} from 'app/constants';
import {checkUpgradeType, isUpgradeAvailable} from 'app/utils/client_upgrade';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+import {showModal, dismissModal} from 'app/actions/navigation';
const {View: AnimatedView} = Animated;
@@ -27,8 +28,6 @@ export default class ClientUpgradeListener extends PureComponent {
actions: PropTypes.shape({
logError: PropTypes.func.isRequired,
setLastUpgradeCheck: PropTypes.func.isRequired,
- showModal: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
}).isRequired,
currentVersion: PropTypes.string,
downloadLink: PropTypes.string,
@@ -141,10 +140,9 @@ export default class ClientUpgradeListener extends PureComponent {
};
handleLearnMore = () => {
- const {actions} = this.props;
const {intl} = this.context;
- actions.dismissModal();
+ dismissModal();
const screen = 'ClientUpgrade';
const title = intl.formatMessage({id: 'mobile.client_upgrade', defaultMessage: 'Upgrade App'});
@@ -160,7 +158,7 @@ export default class ClientUpgradeListener extends PureComponent {
},
};
- actions.showModal(screen, title, passProps, options);
+ showModal(screen, title, passProps, options);
this.toggleUpgradeMessage(false);
};
diff --git a/app/components/client_upgrade_listener/index.js b/app/components/client_upgrade_listener/index.js
index 173c14a50..8d4483fc8 100644
--- a/app/components/client_upgrade_listener/index.js
+++ b/app/components/client_upgrade_listener/index.js
@@ -6,7 +6,6 @@ import {connect} from 'react-redux';
import {logError} from 'mattermost-redux/actions/errors';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {showModal, dismissModal} from 'app/actions/navigation';
import {setLastUpgradeCheck} from 'app/actions/views/client_upgrade';
import getClientUpgrade from 'app/selectors/client_upgrade';
import {isLandscape} from 'app/selectors/device';
@@ -33,8 +32,6 @@ function mapDispatchToProps(dispatch) {
actions: bindActionCreators({
logError,
setLastUpgradeCheck,
- showModal,
- dismissModal,
}, dispatch),
};
}
diff --git a/app/components/edit_channel_info/edit_channel_info.js b/app/components/edit_channel_info/edit_channel_info.js
index 605ac2213..13bafe9b7 100644
--- a/app/components/edit_channel_info/edit_channel_info.js
+++ b/app/components/edit_channel_info/edit_channel_info.js
@@ -19,6 +19,7 @@ import FormattedText from 'app/components/formatted_text';
import Loading from 'app/components/loading';
import StatusBar from 'app/components/status_bar';
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
+import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import {
changeOpacity,
@@ -27,14 +28,10 @@ import {
} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
-import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
+import {popTopScreen, dismissModal} from 'app/actions/navigation';
export default class EditChannelInfo extends PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- dismissModal: PropTypes.func.isRequired,
- popTopScreen: PropTypes.func.isRequired,
- }),
theme: PropTypes.object.isRequired,
deviceWidth: PropTypes.number.isRequired,
deviceHeight: PropTypes.number.isRequired,
@@ -97,11 +94,10 @@ export default class EditChannelInfo extends PureComponent {
};
close = (goBack = false) => {
- const {actions} = this.props;
if (goBack) {
- actions.popTopScreen();
+ popTopScreen();
} else {
- actions.dismissModal();
+ dismissModal();
}
};
diff --git a/app/components/edit_channel_info/edit_channel_info.test.js b/app/components/edit_channel_info/edit_channel_info.test.js
index 3c395f4d1..a5d498f32 100644
--- a/app/components/edit_channel_info/edit_channel_info.test.js
+++ b/app/components/edit_channel_info/edit_channel_info.test.js
@@ -11,10 +11,6 @@ import EditChannelInfo from './edit_channel_info';
describe('EditChannelInfo', () => {
const baseProps = {
- actions: {
- dismissModal: jest.fn(),
- popTopScreen: jest.fn(),
- },
theme: Preferences.THEMES.default,
deviceWidth: 400,
deviceHeight: 600,
diff --git a/app/components/edit_channel_info/index.js b/app/components/edit_channel_info/index.js
index 156b13db2..851b7a735 100644
--- a/app/components/edit_channel_info/index.js
+++ b/app/components/edit_channel_info/index.js
@@ -1,13 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
-import {
- dismissModal,
- popTopScreen,
-} from 'app/actions/navigation';
import {isLandscape} from 'app/selectors/device';
import EditChannelInfo from './edit_channel_info';
@@ -17,13 +12,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- dismissModal,
- popTopScreen,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(EditChannelInfo);
+export default connect(mapStateToProps)(EditChannelInfo);
diff --git a/app/components/file_attachment_list/file_attachment_document/file_attachment_document.js b/app/components/file_attachment_list/file_attachment_document/file_attachment_document.js
index d6e988cc3..eb6134f89 100644
--- a/app/components/file_attachment_list/file_attachment_document/file_attachment_document.js
+++ b/app/components/file_attachment_list/file_attachment_document/file_attachment_document.js
@@ -25,6 +25,7 @@ import TouchableWithFeedback from 'app/components/touchable_with_feedback';
import {DeviceTypes} from 'app/constants/';
import mattermostBucket from 'app/mattermost_bucket';
import {changeOpacity} from 'app/utils/theme';
+import {goToScreen} from 'app/actions/navigation';
const {DOCUMENTS_PATH} = DeviceTypes;
const DOWNLOADING_OFFSET = 28;
@@ -37,9 +38,6 @@ const circularProgressWidth = 4;
export default class FileAttachmentDocument extends PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- goToScreen: PropTypes.func.isRequired,
- }).isRequired,
backgroundColor: PropTypes.string,
canDownloadFiles: PropTypes.bool.isRequired,
iconHeight: PropTypes.number,
@@ -193,13 +191,14 @@ export default class FileAttachmentDocument extends PureComponent {
};
previewTextFile = (file, delay = 2000) => {
- const {actions} = this.props;
const {data} = file;
const prefix = Platform.OS === 'android' ? 'file:/' : '';
const path = `${DOCUMENTS_PATH}/${data.id}-${file.caption}`;
const readFile = RNFetchBlob.fs.readFile(`${prefix}${path}`, 'utf8');
setTimeout(async () => {
try {
+ this.setState({downloading: false, progress: 0});
+
const content = await readFile;
const screen = 'TextPreview';
const title = file.caption;
@@ -207,8 +206,7 @@ export default class FileAttachmentDocument extends PureComponent {
content,
};
- actions.goToScreen(screen, title, passProps);
- this.setState({downloading: false, progress: 0});
+ goToScreen(screen, title, passProps);
} catch (error) {
RNFetchBlob.fs.unlink(path);
}
diff --git a/app/components/file_attachment_list/file_attachment_document/index.js b/app/components/file_attachment_list/file_attachment_document/index.js
index 31fe7d916..ff81e105d 100644
--- a/app/components/file_attachment_list/file_attachment_document/index.js
+++ b/app/components/file_attachment_list/file_attachment_document/index.js
@@ -1,19 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
-import {goToScreen} from 'app/actions/navigation';
-
import FileAttachmentDocument from './file_attachment_document';
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- goToScreen,
- }, dispatch),
- };
-}
-
-export default connect(null, mapDispatchToProps, null, {forwardRef: true})(FileAttachmentDocument);
+export default connect(null, null, null, {forwardRef: true})(FileAttachmentDocument);
diff --git a/app/components/file_attachment_list/file_attachment_list.js b/app/components/file_attachment_list/file_attachment_list.js
index c201a6ef6..fd8a15596 100644
--- a/app/components/file_attachment_list/file_attachment_list.js
+++ b/app/components/file_attachment_list/file_attachment_list.js
@@ -22,7 +22,6 @@ export default class FileAttachmentList extends Component {
static propTypes = {
actions: PropTypes.shape({
loadFilesForPostIfNecessary: PropTypes.func.isRequired,
- showModalOverCurrentContext: PropTypes.func.isRequired,
}).isRequired,
canDownloadFiles: PropTypes.bool.isRequired,
fileIds: PropTypes.array.isRequired,
@@ -122,8 +121,7 @@ export default class FileAttachmentList extends Component {
};
handlePreviewPress = preventDoubleTap((idx) => {
- const {actions} = this.props;
- previewImageAtIndex(this.items, idx, this.galleryFiles, actions.showModalOverCurrentContext);
+ previewImageAtIndex(this.items, idx, this.galleryFiles);
});
renderItems = () => {
diff --git a/app/components/file_attachment_list/file_attachment_list.test.js b/app/components/file_attachment_list/file_attachment_list.test.js
index e2035d542..245e0bac1 100644
--- a/app/components/file_attachment_list/file_attachment_list.test.js
+++ b/app/components/file_attachment_list/file_attachment_list.test.js
@@ -16,7 +16,6 @@ describe('PostAttachmentOpenGraph', () => {
const baseProps = {
actions: {
loadFilesForPostIfNecessary,
- showModalOverCurrentContext: jest.fn(),
},
canDownloadFiles: true,
deviceHeight: 680,
@@ -72,7 +71,6 @@ describe('PostAttachmentOpenGraph', () => {
files: [],
actions: {
loadFilesForPostIfNecessary: loadFilesForPostIfNecessaryMock,
- showModalOverCurrentContext: jest.fn(),
},
};
diff --git a/app/components/file_attachment_list/index.js b/app/components/file_attachment_list/index.js
index 1cd0301af..26bc64c1e 100644
--- a/app/components/file_attachment_list/index.js
+++ b/app/components/file_attachment_list/index.js
@@ -8,7 +8,6 @@ import {canDownloadFilesOnMobile} from 'mattermost-redux/selectors/entities/gene
import {makeGetFilesForPost} from 'mattermost-redux/selectors/entities/files';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {showModalOverCurrentContext} from 'app/actions/navigation';
import {loadFilesForPostIfNecessary} from 'app/actions/views/channel';
import FileAttachmentList from './file_attachment_list';
@@ -28,7 +27,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
loadFilesForPostIfNecessary,
- showModalOverCurrentContext,
}, dispatch),
};
}
diff --git a/app/components/interactive_dialog_controller/index.js b/app/components/interactive_dialog_controller/index.js
index 5d0fb3b43..0c23b8d31 100644
--- a/app/components/interactive_dialog_controller/index.js
+++ b/app/components/interactive_dialog_controller/index.js
@@ -6,8 +6,6 @@ import {connect} from 'react-redux';
import {submitInteractiveDialog} from 'mattermost-redux/actions/integrations';
-import {showModal} from 'app/actions/navigation';
-
import InteractiveDialogController from './interactive_dialog_controller';
function mapStateToProps(state) {
@@ -20,7 +18,6 @@ function mapStateToProps(state) {
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
- showModal,
submitInteractiveDialog,
}, dispatch),
};
diff --git a/app/components/interactive_dialog_controller/interactive_dialog_controller.js b/app/components/interactive_dialog_controller/interactive_dialog_controller.js
index e7565c9a7..b51baa5f0 100644
--- a/app/components/interactive_dialog_controller/interactive_dialog_controller.js
+++ b/app/components/interactive_dialog_controller/interactive_dialog_controller.js
@@ -7,10 +7,11 @@ import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import {Alert} from 'react-native';
import {intlShape} from 'react-intl';
+import {showModal} from 'app/actions/navigation';
+
export default class InteractiveDialogController extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
- showModal: PropTypes.func.isRequired,
submitInteractiveDialog: PropTypes.func.isRequired,
}).isRequired,
triggerId: PropTypes.string,
@@ -88,7 +89,7 @@ export default class InteractiveDialogController extends PureComponent {
},
};
- this.props.actions.showModal('InteractiveDialog', dialog.title, null, options);
+ showModal('InteractiveDialog', dialog.title, null, options);
}
handleCancel = (dialog, url) => {
diff --git a/app/components/interactive_dialog_controller/interactive_dialog_controller.test.js b/app/components/interactive_dialog_controller/interactive_dialog_controller.test.js
index d048b5732..604225f11 100644
--- a/app/components/interactive_dialog_controller/interactive_dialog_controller.test.js
+++ b/app/components/interactive_dialog_controller/interactive_dialog_controller.test.js
@@ -73,7 +73,6 @@ function getBaseProps(triggerId, elements, introductionText) {
return {
actions: {
- showModal: jest.fn(),
submitInteractiveDialog: jest.fn(),
},
triggerId,
diff --git a/app/components/markdown/hashtag/__snapshots__/hashtag.test.js.snap b/app/components/markdown/hashtag/__snapshots__/index.test.js.snap
similarity index 100%
rename from app/components/markdown/hashtag/__snapshots__/hashtag.test.js.snap
rename to app/components/markdown/hashtag/__snapshots__/index.test.js.snap
diff --git a/app/components/markdown/hashtag/hashtag.js b/app/components/markdown/hashtag/hashtag.js
deleted file mode 100644
index 540d49bff..000000000
--- a/app/components/markdown/hashtag/hashtag.js
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-import PropTypes from 'prop-types';
-import React from 'react';
-import {Text} from 'react-native';
-
-import CustomPropTypes from 'app/constants/custom_prop_types';
-
-export default class Hashtag extends React.PureComponent {
- static propTypes = {
- hashtag: PropTypes.string.isRequired,
- linkStyle: CustomPropTypes.Style.isRequired,
- onHashtagPress: PropTypes.func,
- actions: PropTypes.shape({
- popToRoot: PropTypes.func.isRequired,
- showSearchModal: PropTypes.func.isRequired,
- dismissAllModals: PropTypes.func.isRequired,
- }).isRequired,
- };
-
- handlePress = () => {
- const {
- onHashtagPress,
- hashtag,
- actions,
- } = this.props;
-
- if (onHashtagPress) {
- onHashtagPress(hashtag);
-
- return;
- }
-
- // Close thread view, permalink view, etc
- actions.dismissAllModals();
- actions.popToRoot();
-
- actions.showSearchModal('#' + this.props.hashtag);
- };
-
- render() {
- return (
-
- {`#${this.props.hashtag}`}
-
- );
- }
-}
diff --git a/app/components/markdown/hashtag/hashtag.test.js b/app/components/markdown/hashtag/hashtag.test.js
deleted file mode 100644
index d75fc2a74..000000000
--- a/app/components/markdown/hashtag/hashtag.test.js
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-import {shallow} from 'enzyme';
-import React from 'react';
-import {Text} from 'react-native';
-
-import Hashtag from './hashtag';
-
-describe('Hashtag', () => {
- const baseProps = {
- hashtag: 'test',
- linkStyle: {color: 'red'},
- actions: {
- showSearchModal: jest.fn(),
- dismissAllModals: jest.fn(),
- popToRoot: jest.fn(),
- },
- };
-
- test('should match snapshot', () => {
- const wrapper = shallow();
-
- expect(wrapper.getElement()).toMatchSnapshot();
- });
-
- test('should open hashtag search on click', () => {
- const props = {
- ...baseProps,
- };
-
- const wrapper = shallow();
-
- wrapper.find(Text).simulate('press');
-
- expect(props.actions.dismissAllModals).toHaveBeenCalled();
- expect(props.actions.popToRoot).toHaveBeenCalled();
- expect(props.actions.showSearchModal).toHaveBeenCalledWith('#test');
- });
-
- test('should call onHashtagPress if provided', () => {
- const props = {
- ...baseProps,
- onHashtagPress: jest.fn(),
- };
-
- const wrapper = shallow();
-
- wrapper.find(Text).simulate('press');
-
- expect(props.actions.dismissAllModals).not.toBeCalled();
- expect(props.actions.popToRoot).not.toBeCalled();
- expect(props.actions.showSearchModal).not.toBeCalled();
-
- expect(props.onHashtagPress).toBeCalled();
- });
-});
diff --git a/app/components/markdown/hashtag/index.js b/app/components/markdown/hashtag/index.js
index 9ffc10e5c..565763241 100644
--- a/app/components/markdown/hashtag/index.js
+++ b/app/components/markdown/hashtag/index.js
@@ -1,25 +1,47 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {connect} from 'react-redux';
-import {bindActionCreators} from 'redux';
+import PropTypes from 'prop-types';
+import React from 'react';
+import {Text} from 'react-native';
-import {
- popToRoot,
- showSearchModal,
- dismissAllModals,
-} from 'app/actions/navigation';
+import CustomPropTypes from 'app/constants/custom_prop_types';
+import {popToRoot, showSearchModal, dismissAllModals} from 'app/actions/navigation';
-import Hashtag from './hashtag';
-
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- popToRoot,
- showSearchModal,
- dismissAllModals,
- }, dispatch),
+export default class Hashtag extends React.PureComponent {
+ static propTypes = {
+ hashtag: PropTypes.string.isRequired,
+ linkStyle: CustomPropTypes.Style.isRequired,
+ onHashtagPress: PropTypes.func,
};
-}
-export default connect(null, mapDispatchToProps)(Hashtag);
+ handlePress = async () => {
+ const {
+ onHashtagPress,
+ hashtag,
+ } = this.props;
+
+ if (onHashtagPress) {
+ onHashtagPress(hashtag);
+
+ return;
+ }
+
+ // Close thread view, permalink view, etc
+ await dismissAllModals();
+ await popToRoot();
+
+ showSearchModal('#' + this.props.hashtag);
+ };
+
+ render() {
+ return (
+
+ {`#${this.props.hashtag}`}
+
+ );
+ }
+}
diff --git a/app/components/markdown/hashtag/index.test.js b/app/components/markdown/hashtag/index.test.js
new file mode 100644
index 000000000..6534ee263
--- /dev/null
+++ b/app/components/markdown/hashtag/index.test.js
@@ -0,0 +1,61 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {shallow} from 'enzyme';
+import React from 'react';
+
+import * as NavigationActions from 'app/actions/navigation';
+
+import Hashtag from './index';
+
+describe('Hashtag', () => {
+ const baseProps = {
+ hashtag: 'test',
+ linkStyle: {color: 'red'},
+ };
+
+ test('should match snapshot', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.getElement()).toMatchSnapshot();
+ });
+
+ test('handlePress should open hashtag search', async () => {
+ const dismissAllModals = jest.spyOn(NavigationActions, 'dismissAllModals');
+ const popToRoot = jest.spyOn(NavigationActions, 'popToRoot');
+ const showSearchModal = jest.spyOn(NavigationActions, 'showSearchModal');
+
+ const props = {
+ ...baseProps,
+ };
+
+ const wrapper = shallow();
+
+ await wrapper.instance().handlePress();
+
+ expect(dismissAllModals).toHaveBeenCalled();
+ expect(popToRoot).toHaveBeenCalled();
+ expect(showSearchModal).toHaveBeenCalledWith('#test');
+ });
+
+ test('handlePress should call onHashtagPress if provided', async () => {
+ const dismissAllModals = jest.spyOn(NavigationActions, 'dismissAllModals');
+ const popToRoot = jest.spyOn(NavigationActions, 'popToRoot');
+ const showSearchModal = jest.spyOn(NavigationActions, 'showSearchModal');
+
+ const props = {
+ ...baseProps,
+ onHashtagPress: jest.fn(),
+ };
+
+ const wrapper = shallow();
+
+ await wrapper.instance().handlePress();
+
+ expect(dismissAllModals).not.toBeCalled();
+ expect(popToRoot).not.toBeCalled();
+ expect(showSearchModal).not.toBeCalled();
+
+ expect(props.onHashtagPress).toBeCalled();
+ });
+});
diff --git a/app/components/markdown/markdown_code_block/index.js b/app/components/markdown/markdown_code_block/index.js
index 76e8cbb51..37ccabce4 100644
--- a/app/components/markdown/markdown_code_block/index.js
+++ b/app/components/markdown/markdown_code_block/index.js
@@ -1,13 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {goToScreen} from 'app/actions/navigation';
-
import MarkdownCodeBlock from './markdown_code_block';
function mapStateToProps(state) {
@@ -16,12 +13,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- goToScreen,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(MarkdownCodeBlock);
+export default connect(mapStateToProps)(MarkdownCodeBlock);
diff --git a/app/components/markdown/markdown_code_block/markdown_code_block.js b/app/components/markdown/markdown_code_block/markdown_code_block.js
index 83020acea..692cf3a6c 100644
--- a/app/components/markdown/markdown_code_block/markdown_code_block.js
+++ b/app/components/markdown/markdown_code_block/markdown_code_block.js
@@ -19,14 +19,12 @@ import {getDisplayNameForLanguage} from 'app/utils/markdown';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import mattermostManaged from 'app/mattermost_managed';
+import {goToScreen} from 'app/actions/navigation';
const MAX_LINES = 4;
export default class MarkdownCodeBlock extends React.PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- goToScreen: PropTypes.func.isRequired,
- }).isRequired,
theme: PropTypes.object.isRequired,
language: PropTypes.string,
content: PropTypes.string.isRequired,
@@ -42,7 +40,7 @@ export default class MarkdownCodeBlock extends React.PureComponent {
};
handlePress = preventDoubleTap(() => {
- const {actions, language, content} = this.props;
+ const {language, content} = this.props;
const {intl} = this.context;
const screen = 'Code';
const passProps = {
@@ -68,7 +66,7 @@ export default class MarkdownCodeBlock extends React.PureComponent {
});
}
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
});
handleLongPress = async () => {
diff --git a/app/components/markdown/markdown_image/index.js b/app/components/markdown/markdown_image/index.js
index f15c54ccc..19ef387fa 100644
--- a/app/components/markdown/markdown_image/index.js
+++ b/app/components/markdown/markdown_image/index.js
@@ -1,13 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general';
-import {showModalOverCurrentContext} from 'app/actions/navigation';
-
import {getDimensions} from 'app/selectors/device';
import MarkdownImage from './markdown_image';
@@ -19,12 +16,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- showModalOverCurrentContext,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(MarkdownImage);
+export default connect(mapStateToProps)(MarkdownImage);
diff --git a/app/components/markdown/markdown_image/markdown_image.js b/app/components/markdown/markdown_image/markdown_image.js
index 779870206..98ed272ab 100644
--- a/app/components/markdown/markdown_image/markdown_image.js
+++ b/app/components/markdown/markdown_image/markdown_image.js
@@ -34,9 +34,6 @@ const VIEWPORT_IMAGE_REPLY_OFFSET = 13;
export default class MarkdownImage extends React.Component {
static propTypes = {
- actions: PropTypes.shape({
- showModalOverCurrentContext: PropTypes.func.isRequired,
- }).isRequired,
children: PropTypes.node,
deviceHeight: PropTypes.number.isRequired,
deviceWidth: PropTypes.number.isRequired,
@@ -177,7 +174,6 @@ export default class MarkdownImage extends React.Component {
originalWidth,
uri,
} = this.state;
- const {actions} = this.props;
const link = this.getSource();
let filename = link.substring(link.lastIndexOf('/') + 1, link.indexOf('?') === -1 ? link.length : link.indexOf('?'));
const extension = filename.split('.').pop();
@@ -199,7 +195,7 @@ export default class MarkdownImage extends React.Component {
},
}];
- previewImageAtIndex([this.refs.item], 0, files, actions.showModalOverCurrentContext);
+ previewImageAtIndex([this.refs.item], 0, files);
};
loadImageSize = (source) => {
diff --git a/app/components/markdown/markdown_table/index.js b/app/components/markdown/markdown_table/index.js
index 4100a3fc4..e3e37c016 100644
--- a/app/components/markdown/markdown_table/index.js
+++ b/app/components/markdown/markdown_table/index.js
@@ -1,13 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {goToScreen} from 'app/actions/navigation';
-
import MarkdownTable from './markdown_table';
function mapStateToProps(state) {
@@ -16,12 +13,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- goToScreen,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(MarkdownTable);
+export default connect(mapStateToProps)(MarkdownTable);
diff --git a/app/components/markdown/markdown_table/markdown_table.js b/app/components/markdown/markdown_table/markdown_table.js
index de97017ba..f84573979 100644
--- a/app/components/markdown/markdown_table/markdown_table.js
+++ b/app/components/markdown/markdown_table/markdown_table.js
@@ -11,17 +11,16 @@ import {
import LinearGradient from 'react-native-linear-gradient';
import {CELL_WIDTH} from 'app/components/markdown/markdown_table_cell/markdown_table_cell';
+
import TouchableWithFeedback from 'app/components/touchable_with_feedback';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+import {goToScreen} from 'app/actions/navigation';
const MAX_HEIGHT = 300;
export default class MarkdownTable extends React.PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- goToScreen: PropTypes.func.isRequired,
- }).isRequired,
children: PropTypes.node.isRequired,
numColumns: PropTypes.number.isRequired,
theme: PropTypes.object.isRequired,
@@ -46,7 +45,6 @@ export default class MarkdownTable extends React.PureComponent {
};
handlePress = preventDoubleTap(() => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'Table';
const title = intl.formatMessage({
@@ -58,7 +56,7 @@ export default class MarkdownTable extends React.PureComponent {
tableWidth: this.getTableWidth(),
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
});
handleContainerLayout = (e) => {
diff --git a/app/components/markdown/markdown_table_image/index.js b/app/components/markdown/markdown_table_image/index.js
index c1eea7804..96174a3a8 100644
--- a/app/components/markdown/markdown_table_image/index.js
+++ b/app/components/markdown/markdown_table_image/index.js
@@ -1,14 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {goToScreen} from 'app/actions/navigation';
-
import MarkdownTableImage from './markdown_table_image';
function mapStateToProps(state) {
@@ -18,12 +15,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- goToScreen,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(MarkdownTableImage);
+export default connect(mapStateToProps)(MarkdownTableImage);
diff --git a/app/components/markdown/markdown_table_image/markdown_table_image.js b/app/components/markdown/markdown_table_image/markdown_table_image.js
index 1a21d0e52..a13e3c717 100644
--- a/app/components/markdown/markdown_table_image/markdown_table_image.js
+++ b/app/components/markdown/markdown_table_image/markdown_table_image.js
@@ -9,12 +9,10 @@ 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';
+import {goToScreen} from 'app/actions/navigation';
export default class MarkdownTableImage extends React.PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- goToScreen: PropTypes.func.isRequired,
- }).isRequired,
children: PropTypes.node.isRequired,
source: PropTypes.string.isRequired,
textStyle: CustomPropTypes.Style.isRequired,
@@ -27,7 +25,6 @@ export default class MarkdownTableImage extends React.PureComponent {
};
handlePress = preventDoubleTap(() => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'TableImage';
const title = intl.formatMessage({
@@ -38,7 +35,7 @@ export default class MarkdownTableImage extends React.PureComponent {
imageSource: this.getImageSource(),
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
});
getImageSource = async () => {
diff --git a/app/components/markdown/transform.js b/app/components/markdown/transform.js
index bb1502118..48b0847a9 100644
--- a/app/components/markdown/transform.js
+++ b/app/components/markdown/transform.js
@@ -258,8 +258,6 @@ function getLastSibling(node) {
export function highlightMentions(ast, mentionKeys) {
const walker = ast.walker();
- // console.warn(mentionKeys);
-
let e;
while ((e = walker.next())) {
if (!e.entering) {
diff --git a/app/components/markdown/transform.test.js b/app/components/markdown/transform.test.js
index 88a2e1fb6..1e83e9ed6 100644
--- a/app/components/markdown/transform.test.js
+++ b/app/components/markdown/transform.test.js
@@ -2966,28 +2966,28 @@ describe('Components.Markdown.transform', () => {
// Confirms that all parent, child, and sibling linkages are correct and go both ways.
function verifyAst(node) {
if (node.prev && node.prev.next !== node) {
- console.error('node is not linked properly to prev');
+ console.error('node is not linked properly to prev'); //eslint-disable-line no-console
return false;
}
if (node.next && node.next.prev !== node) {
- console.error('node is not linked properly to next');
+ console.error('node is not linked properly to next'); //eslint-disable-line no-console
return false;
}
if (!node.firstChild && node.lastChild) {
- console.error('node has children, but is not linked to first child');
+ console.error('node has children, but is not linked to first child'); //eslint-disable-line no-console
return false;
}
if (node.firstChild && !node.lastChild) {
- console.error('node has children, but is not linked to last child');
+ console.error('node has children, but is not linked to last child'); //eslint-disable-line no-console
return false;
}
for (let child = node.firstChild; child; child = child.next) {
if (child.parent !== node) {
- console.error('node is not linked properly to child');
+ console.error('node is not linked properly to child'); //eslint-disable-line no-console
return false;
}
@@ -2996,18 +2996,18 @@ function verifyAst(node) {
}
if (!child.next && child !== node.lastChild) {
- console.error('node children are not linked correctly');
+ console.error('node children are not linked correctly'); //eslint-disable-line no-console
return false;
}
}
if (node.firstChild && node.firstChild.prev) {
- console.error('node\'s first child has previous sibling');
+ console.error('node\'s first child has previous sibling'); //eslint-disable-line no-console
return false;
}
if (node.lastChild && node.lastChild.next) {
- console.error('node\'s last child has next sibling');
+ console.error('node\'s last child has next sibling'); //eslint-disable-line no-console
return false;
}
diff --git a/app/components/message_attachments/attachment_image/__snapshots__/attachment_image.test.js.snap b/app/components/message_attachments/attachment_image/__snapshots__/index.test.js.snap
similarity index 100%
rename from app/components/message_attachments/attachment_image/__snapshots__/attachment_image.test.js.snap
rename to app/components/message_attachments/attachment_image/__snapshots__/index.test.js.snap
diff --git a/app/components/message_attachments/attachment_image/attachment_image.js b/app/components/message_attachments/attachment_image/attachment_image.js
deleted file mode 100644
index 45d75fe47..000000000
--- a/app/components/message_attachments/attachment_image/attachment_image.js
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-import React, {PureComponent} from 'react';
-import PropTypes from 'prop-types';
-import {Image, View} from 'react-native';
-
-import ProgressiveImage from 'app/components/progressive_image';
-import TouchableWithFeedback from 'app/components/touchable_with_feedback';
-import {isGifTooLarge, previewImageAtIndex, calculateDimensions} from 'app/utils/images';
-import ImageCacheManager from 'app/utils/image_cache_manager';
-import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
-
-const VIEWPORT_IMAGE_OFFSET = 100;
-const VIEWPORT_IMAGE_CONTAINER_OFFSET = 10;
-
-export default class AttachmentImage extends PureComponent {
- static propTypes = {
- actions: PropTypes.shape({
- showModalOverCurrentContext: PropTypes.func.isRequired,
- }).isRequired,
- deviceHeight: PropTypes.number.isRequired,
- deviceWidth: PropTypes.number.isRequired,
- imageMetadata: PropTypes.object,
- imageUrl: PropTypes.string,
- theme: PropTypes.object.isRequired,
- };
-
- constructor(props) {
- super(props);
-
- this.state = {
- hasImage: Boolean(props.imageUrl),
- imageUri: null,
- };
- }
-
- componentDidMount() {
- this.mounted = true;
- const {imageUrl, imageMetadata} = this.props;
-
- this.setViewPortMaxWidth();
- if (imageMetadata) {
- this.setImageDimensionsFromMeta(null, imageMetadata);
- }
-
- if (imageUrl) {
- ImageCacheManager.cache(null, imageUrl, this.setImageUrl);
- }
- }
-
- componentDidUpdate(prevProps) {
- if (this.props.imageUrl && (prevProps.imageUrl !== this.props.imageUrl)) {
- ImageCacheManager.cache(null, this.props.imageUrl, this.setImageUrl);
- }
- }
-
- handlePreviewImage = () => {
- const {actions, imageUrl} = this.props;
- const {
- imageUri: uri,
- originalHeight,
- originalWidth,
- } = this.state;
-
- let filename = imageUrl.substring(imageUrl.lastIndexOf('/') + 1, imageUrl.indexOf('?') === -1 ? imageUrl.length : imageUrl.indexOf('?'));
- const extension = filename.split('.').pop();
-
- if (extension === filename) {
- const ext = filename.indexOf('.') === -1 ? '.png' : filename.substring(filename.lastIndexOf('.'));
- filename = `${filename}${ext}`;
- }
-
- const files = [{
- caption: filename,
- dimensions: {
- height: originalHeight,
- width: originalWidth,
- },
- source: {uri},
- data: {
- localPath: uri,
- },
- }];
- previewImageAtIndex([this.refs.item], 0, files, actions.showModalOverCurrentContext);
- };
-
- setImageDimensions = (imageUri, dimensions, originalWidth, originalHeight) => {
- if (this.mounted) {
- this.setState({
- ...dimensions,
- originalWidth,
- originalHeight,
- imageUri,
- });
- }
- };
-
- setImageDimensionsFromMeta = (imageUri, imageMetadata) => {
- const dimensions = calculateDimensions(imageMetadata.height, imageMetadata.width, this.maxImageWidth);
- this.setImageDimensions(imageUri, dimensions, imageMetadata.width, imageMetadata.height);
- };
-
- setImageUrl = (imageURL) => {
- const {imageMetadata} = this.props;
-
- if (imageMetadata) {
- this.setImageDimensionsFromMeta(imageURL, imageMetadata);
- return;
- }
-
- Image.getSize(imageURL, (width, height) => {
- const dimensions = calculateDimensions(height, width, this.maxImageWidth);
- this.setImageDimensions(imageURL, dimensions, width, height);
- }, () => null);
- };
-
- setViewPortMaxWidth = () => {
- const {deviceWidth, deviceHeight} = this.props;
- const viewPortWidth = deviceWidth > deviceHeight ? deviceHeight : deviceWidth;
- this.maxImageWidth = viewPortWidth - VIEWPORT_IMAGE_OFFSET;
- };
-
- render() {
- const {imageMetadata, theme} = this.props;
- const {hasImage, height, imageUri, width} = this.state;
-
- if (!hasImage || isGifTooLarge(imageMetadata)) {
- return null;
- }
-
- const style = getStyleSheet(theme);
-
- let progressiveImage;
- if (imageUri) {
- progressiveImage = (
-
- );
- } else {
- progressiveImage = ();
- }
-
- return (
-
-
- {progressiveImage}
-
-
- );
- }
-}
-
-const getStyleSheet = makeStyleSheetFromTheme((theme) => {
- return {
- container: {
- marginTop: 5,
- },
- imageContainer: {
- borderColor: changeOpacity(theme.centerChannelColor, 0.1),
- borderWidth: 1,
- borderRadius: 2,
- flex: 1,
- padding: 5,
- },
- };
-});
diff --git a/app/components/message_attachments/attachment_image/index.js b/app/components/message_attachments/attachment_image/index.js
index 17bfedae3..6948c3206 100644
--- a/app/components/message_attachments/attachment_image/index.js
+++ b/app/components/message_attachments/attachment_image/index.js
@@ -1,19 +1,175 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
-import {connect} from 'react-redux';
+import React, {PureComponent} from 'react';
+import PropTypes from 'prop-types';
+import {Image, View} from 'react-native';
-import {showModalOverCurrentContext} from 'app/actions/navigation';
+import ProgressiveImage from 'app/components/progressive_image';
+import TouchableWithFeedback from 'app/components/touchable_with_feedback';
+import {isGifTooLarge, previewImageAtIndex, calculateDimensions} from 'app/utils/images';
+import ImageCacheManager from 'app/utils/image_cache_manager';
+import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
-import AttachmentImage from './attachment_image';
+const VIEWPORT_IMAGE_OFFSET = 100;
+const VIEWPORT_IMAGE_CONTAINER_OFFSET = 10;
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- showModalOverCurrentContext,
- }, dispatch),
+export default class AttachmentImage extends PureComponent {
+ static propTypes = {
+ deviceHeight: PropTypes.number.isRequired,
+ deviceWidth: PropTypes.number.isRequired,
+ imageMetadata: PropTypes.object,
+ imageUrl: PropTypes.string,
+ theme: PropTypes.object.isRequired,
};
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ hasImage: Boolean(props.imageUrl),
+ imageUri: null,
+ };
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ const {imageUrl, imageMetadata} = this.props;
+
+ this.setViewPortMaxWidth();
+ if (imageMetadata) {
+ this.setImageDimensionsFromMeta(null, imageMetadata);
+ }
+
+ if (imageUrl) {
+ ImageCacheManager.cache(null, imageUrl, this.setImageUrl);
+ }
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.imageUrl && (prevProps.imageUrl !== this.props.imageUrl)) {
+ ImageCacheManager.cache(null, this.props.imageUrl, this.setImageUrl);
+ }
+ }
+
+ handlePreviewImage = () => {
+ const {imageUrl} = this.props;
+ const {
+ imageUri: uri,
+ originalHeight,
+ originalWidth,
+ } = this.state;
+
+ let filename = imageUrl.substring(imageUrl.lastIndexOf('/') + 1, imageUrl.indexOf('?') === -1 ? imageUrl.length : imageUrl.indexOf('?'));
+ const extension = filename.split('.').pop();
+
+ if (extension === filename) {
+ const ext = filename.indexOf('.') === -1 ? '.png' : filename.substring(filename.lastIndexOf('.'));
+ filename = `${filename}${ext}`;
+ }
+
+ const files = [{
+ caption: filename,
+ dimensions: {
+ height: originalHeight,
+ width: originalWidth,
+ },
+ source: {uri},
+ data: {
+ localPath: uri,
+ },
+ }];
+ previewImageAtIndex([this.refs.item], 0, files);
+ };
+
+ setImageDimensions = (imageUri, dimensions, originalWidth, originalHeight) => {
+ if (this.mounted) {
+ this.setState({
+ ...dimensions,
+ originalWidth,
+ originalHeight,
+ imageUri,
+ });
+ }
+ };
+
+ setImageDimensionsFromMeta = (imageUri, imageMetadata) => {
+ const dimensions = calculateDimensions(imageMetadata.height, imageMetadata.width, this.maxImageWidth);
+ this.setImageDimensions(imageUri, dimensions, imageMetadata.width, imageMetadata.height);
+ };
+
+ setImageUrl = (imageURL) => {
+ const {imageMetadata} = this.props;
+
+ if (imageMetadata) {
+ this.setImageDimensionsFromMeta(imageURL, imageMetadata);
+ return;
+ }
+
+ Image.getSize(imageURL, (width, height) => {
+ const dimensions = calculateDimensions(height, width, this.maxImageWidth);
+ this.setImageDimensions(imageURL, dimensions, width, height);
+ }, () => null);
+ };
+
+ setViewPortMaxWidth = () => {
+ const {deviceWidth, deviceHeight} = this.props;
+ const viewPortWidth = deviceWidth > deviceHeight ? deviceHeight : deviceWidth;
+ this.maxImageWidth = viewPortWidth - VIEWPORT_IMAGE_OFFSET;
+ };
+
+ render() {
+ const {imageMetadata, theme} = this.props;
+ const {hasImage, height, imageUri, width} = this.state;
+
+ if (!hasImage || isGifTooLarge(imageMetadata)) {
+ return null;
+ }
+
+ const style = getStyleSheet(theme);
+
+ let progressiveImage;
+ if (imageUri) {
+ progressiveImage = (
+
+ );
+ } else {
+ progressiveImage = ();
+ }
+
+ return (
+
+
+ {progressiveImage}
+
+
+ );
+ }
}
-export default connect(null, mapDispatchToProps)(AttachmentImage);
+const getStyleSheet = makeStyleSheetFromTheme((theme) => {
+ return {
+ container: {
+ marginTop: 5,
+ },
+ imageContainer: {
+ borderColor: changeOpacity(theme.centerChannelColor, 0.1),
+ borderWidth: 1,
+ borderRadius: 2,
+ flex: 1,
+ padding: 5,
+ },
+ };
+});
diff --git a/app/components/message_attachments/attachment_image/attachment_image.test.js b/app/components/message_attachments/attachment_image/index.test.js
similarity index 97%
rename from app/components/message_attachments/attachment_image/attachment_image.test.js
rename to app/components/message_attachments/attachment_image/index.test.js
index 3661ec762..da1557e7f 100644
--- a/app/components/message_attachments/attachment_image/attachment_image.test.js
+++ b/app/components/message_attachments/attachment_image/index.test.js
@@ -11,13 +11,10 @@ const originalGetSizeFn = Image.getSize;
import Preferences from 'mattermost-redux/constants/preferences';
-import AttachmentImage from './attachment_image';
+import AttachmentImage from './index';
describe('AttachmentImage', () => {
const baseProps = {
- actions: {
- showModalOverCurrentContext: jest.fn(),
- },
deviceHeight: 256,
deviceWidth: 128,
imageMetadata: {width: 32, height: 32},
diff --git a/app/components/post/index.js b/app/components/post/index.js
index df48aeaa0..342fff76a 100644
--- a/app/components/post/index.js
+++ b/app/components/post/index.js
@@ -12,7 +12,6 @@ import {getUser, getCurrentUserId} from 'mattermost-redux/selectors/entities/use
import {getMyPreferences, getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {isPostFlagged, isSystemMessage} from 'mattermost-redux/utils/post_utils';
-import {goToScreen, showModalOverCurrentContext} from 'app/actions/navigation';
import {insertToDraft, setPostTooltipVisible} from 'app/actions/views/channel';
import {isLandscape} from 'app/selectors/device';
@@ -96,8 +95,6 @@ function mapDispatchToProps(dispatch) {
removePost,
setPostTooltipVisible,
insertToDraft,
- goToScreen,
- showModalOverCurrentContext,
}, dispatch),
};
}
diff --git a/app/components/post/post.js b/app/components/post/post.js
index 00b3be652..2ba71ef42 100644
--- a/app/components/post/post.js
+++ b/app/components/post/post.js
@@ -11,6 +11,10 @@ import {
} from 'react-native';
import {intlShape} from 'react-intl';
+import {Posts} from 'mattermost-redux/constants';
+import EventEmitter from 'mattermost-redux/utils/event_emitter';
+import {isPostEphemeral, isPostPendingOrFailed, isSystemMessage} from 'mattermost-redux/utils/post_utils';
+
import PostBody from 'app/components/post_body';
import PostHeader from 'app/components/post_header';
import PostPreHeader from 'app/components/post_header/post_pre_header';
@@ -22,10 +26,7 @@ import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
-
-import {Posts} from 'mattermost-redux/constants';
-import EventEmitter from 'mattermost-redux/utils/event_emitter';
-import {isPostEphemeral, isPostPendingOrFailed, isSystemMessage} from 'mattermost-redux/utils/post_utils';
+import {goToScreen, showModalOverCurrentContext} from 'app/actions/navigation';
import Config from 'assets/config';
@@ -35,8 +36,6 @@ export default class Post extends PureComponent {
createPost: PropTypes.func.isRequired,
insertToDraft: PropTypes.func.isRequired,
removePost: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
- showModalOverCurrentContext: PropTypes.func.isRequired,
}).isRequired,
channelIsReadOnly: PropTypes.bool,
currentUserId: PropTypes.string.isRequired,
@@ -91,7 +90,7 @@ export default class Post extends PureComponent {
goToUserProfile = () => {
const {intl} = this.context;
- const {actions, post} = this.props;
+ const {post} = this.props;
const screen = 'UserProfile';
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
const passProps = {
@@ -100,7 +99,7 @@ export default class Post extends PureComponent {
Keyboard.dismiss();
requestAnimationFrame(() => {
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
});
};
@@ -141,7 +140,7 @@ export default class Post extends PureComponent {
}],
};
- this.props.actions.showModalOverCurrentContext(screen, passProps);
+ showModalOverCurrentContext(screen, passProps);
};
handlePress = preventDoubleTap(() => {
diff --git a/app/components/post_attachment_opengraph/index.js b/app/components/post_attachment_opengraph/index.js
index d18e1585b..f422ddf5e 100644
--- a/app/components/post_attachment_opengraph/index.js
+++ b/app/components/post_attachment_opengraph/index.js
@@ -6,8 +6,6 @@ import {bindActionCreators} from 'redux';
import {getOpenGraphMetadata} from 'mattermost-redux/actions/posts';
-import {showModalOverCurrentContext} from 'app/actions/navigation';
-
import {getDimensions} from 'app/selectors/device';
import PostAttachmentOpenGraph from './post_attachment_opengraph';
@@ -22,7 +20,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
getOpenGraphMetadata,
- showModalOverCurrentContext,
}, dispatch),
};
}
diff --git a/app/components/post_attachment_opengraph/post_attachment_opengraph.js b/app/components/post_attachment_opengraph/post_attachment_opengraph.js
index e80f1b732..ec66e3cc2 100644
--- a/app/components/post_attachment_opengraph/post_attachment_opengraph.js
+++ b/app/components/post_attachment_opengraph/post_attachment_opengraph.js
@@ -27,7 +27,6 @@ export default class PostAttachmentOpenGraph extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
getOpenGraphMetadata: PropTypes.func.isRequired,
- showModalOverCurrentContext: PropTypes.func.isRequired,
}).isRequired,
deviceHeight: PropTypes.number.isRequired,
deviceWidth: PropTypes.number.isRequired,
@@ -194,7 +193,6 @@ export default class PostAttachmentOpenGraph extends PureComponent {
originalWidth,
originalHeight,
} = this.state;
- const {actions} = this.props;
const filename = this.getFilename(link);
const files = [{
@@ -209,7 +207,7 @@ export default class PostAttachmentOpenGraph extends PureComponent {
},
}];
- previewImageAtIndex([this.refs.item], 0, files, actions.showModalOverCurrentContext);
+ previewImageAtIndex([this.refs.item], 0, files);
};
renderDescription = () => {
diff --git a/app/components/post_attachment_opengraph/post_attachment_opengraph.test.js b/app/components/post_attachment_opengraph/post_attachment_opengraph.test.js
index dca90cac2..b67bff4bf 100644
--- a/app/components/post_attachment_opengraph/post_attachment_opengraph.test.js
+++ b/app/components/post_attachment_opengraph/post_attachment_opengraph.test.js
@@ -22,7 +22,6 @@ describe('PostAttachmentOpenGraph', () => {
const baseProps = {
actions: {
getOpenGraphMetadata: jest.fn(),
- showModalOverCurrentContext: jest.fn(),
},
deviceHeight: 600,
deviceWidth: 400,
diff --git a/app/components/post_body/index.js b/app/components/post_body/index.js
index 5c6d21f27..7c73a1ca7 100644
--- a/app/components/post_body/index.js
+++ b/app/components/post_body/index.js
@@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {General, Posts} from 'mattermost-redux/constants';
@@ -22,8 +21,6 @@ import {
} from 'mattermost-redux/utils/post_utils';
import {isAdmin as checkIsAdmin, isSystemAdmin as checkIsSystemAdmin} from 'mattermost-redux/utils/user_utils';
-import {showModalOverCurrentContext} from 'app/actions/navigation';
-
import {getDimensions} from 'app/selectors/device';
import {hasEmojisOnly} from 'app/utils/emoji_utils';
@@ -106,12 +103,4 @@ export function makeMapStateToProps() {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- showModalOverCurrentContext,
- }, dispatch),
- };
-}
-
-export default connect(makeMapStateToProps, mapDispatchToProps, null, {forwardRef: true})(PostBody);
+export default connect(makeMapStateToProps, null, null, {forwardRef: true})(PostBody);
diff --git a/app/components/post_body/post_body.js b/app/components/post_body/post_body.js
index d5c5c1d0f..618a313dd 100644
--- a/app/components/post_body/post_body.js
+++ b/app/components/post_body/post_body.js
@@ -22,6 +22,7 @@ import {emptyFunction} from 'app/utils/general';
import {getMarkdownTextStyles, getMarkdownBlockStyles} from 'app/utils/markdown';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+import {showModalOverCurrentContext} from 'app/actions/navigation';
import telemetry from 'app/telemetry';
@@ -34,9 +35,6 @@ const SHOW_MORE_HEIGHT = 60;
export default class PostBody extends PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- showModalOverCurrentContext: PropTypes.func.isRequired,
- }).isRequired,
canDelete: PropTypes.bool,
channelIsReadOnly: PropTypes.bool.isRequired,
deviceHeight: PropTypes.number.isRequired,
@@ -133,7 +131,6 @@ export default class PostBody extends PureComponent {
onHashtagPress,
onPermalinkPress,
post,
- actions,
} = this.props;
const screen = 'LongPost';
const passProps = {
@@ -148,7 +145,7 @@ export default class PostBody extends PureComponent {
},
};
- actions.showModalOverCurrentContext(screen, passProps, options);
+ showModalOverCurrentContext(screen, passProps, options);
});
showPostOptions = () => {
@@ -165,7 +162,6 @@ export default class PostBody extends PureComponent {
post,
showAddReaction,
location,
- actions,
} = this.props;
if (isSystemMessage && (!canDelete || hasBeenDeleted)) {
@@ -191,7 +187,7 @@ export default class PostBody extends PureComponent {
Keyboard.dismiss();
requestAnimationFrame(() => {
- actions.showModalOverCurrentContext(screen, passProps);
+ showModalOverCurrentContext(screen, passProps);
});
};
diff --git a/app/components/post_body/post_body.test.js b/app/components/post_body/post_body.test.js
index 94dcbc48c..3c299f9ec 100644
--- a/app/components/post_body/post_body.test.js
+++ b/app/components/post_body/post_body.test.js
@@ -12,9 +12,6 @@ import PostBody from './post_body.js';
describe('PostBody', () => {
const baseProps = {
- actions: {
- showModalOverCurrentContext: jest.fn(),
- },
canDelete: true,
channelIsReadOnly: false,
deviceHeight: 1920,
diff --git a/app/components/post_body_additional_content/index.js b/app/components/post_body_additional_content/index.js
index e4ace1dc0..115ecb98a 100644
--- a/app/components/post_body_additional_content/index.js
+++ b/app/components/post_body_additional_content/index.js
@@ -10,7 +10,6 @@ import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {getOpenGraphMetadataForUrl} from 'mattermost-redux/selectors/entities/posts';
import {getBool, getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {showModalOverCurrentContext} from 'app/actions/navigation';
import {ViewTypes} from 'app/constants';
import {getDimensions} from 'app/selectors/device';
import {extractFirstLink} from 'app/utils/url';
@@ -76,7 +75,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
getRedirectLocation,
- showModalOverCurrentContext,
}, dispatch),
};
}
diff --git a/app/components/post_body_additional_content/post_body_additional_content.js b/app/components/post_body_additional_content/post_body_additional_content.js
index 13c88735f..58d3f5b06 100644
--- a/app/components/post_body_additional_content/post_body_additional_content.js
+++ b/app/components/post_body_additional_content/post_body_additional_content.js
@@ -38,7 +38,6 @@ export default class PostBodyAdditionalContent extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
getRedirectLocation: PropTypes.func.isRequired,
- showModalOverCurrentContext: PropTypes.func.isRequired,
}).isRequired,
baseTextStyle: CustomPropTypes.Style,
blockStyles: PropTypes.object,
@@ -412,7 +411,6 @@ export default class PostBodyAdditionalContent extends PureComponent {
handlePreviewImage = (imageRef) => {
const {shortenedLink} = this.state;
let {link} = this.props;
- const {actions} = this.props;
if (shortenedLink) {
link = shortenedLink;
}
@@ -434,7 +432,7 @@ export default class PostBodyAdditionalContent extends PureComponent {
},
}];
- previewImageAtIndex([imageRef], 0, files, actions.showModalOverCurrentContext);
+ previewImageAtIndex([imageRef], 0, files);
};
playYouTubeVideo = () => {
diff --git a/app/components/post_list/index.js b/app/components/post_list/index.js
index 1c8f430ef..92168b4ba 100644
--- a/app/components/post_list/index.js
+++ b/app/components/post_list/index.js
@@ -9,7 +9,6 @@ import {getConfig, getCurrentUrl} from 'mattermost-redux/selectors/entities/gene
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {makePreparePostIdsForPostList, START_OF_NEW_MESSAGES} from 'mattermost-redux/utils/post_list';
-import {showModalOverCurrentContext} from 'app/actions/navigation';
import {handleSelectChannelByName, loadChannelsByTeamName, refreshChannelWithRetry} from 'app/actions/views/channel';
import {setDeepLinkURL} from 'app/actions/views/root';
@@ -43,7 +42,6 @@ function mapDispatchToProps(dispatch) {
refreshChannelWithRetry,
selectFocusedPostId,
setDeepLinkURL,
- showModalOverCurrentContext,
}, dispatch),
};
}
diff --git a/app/components/post_list/post_list.js b/app/components/post_list/post_list.js
index fb5a9c7a6..2ec97afee 100644
--- a/app/components/post_list/post_list.js
+++ b/app/components/post_list/post_list.js
@@ -16,6 +16,7 @@ import {makeExtraData} from 'app/utils/list_view';
import {changeOpacity} from 'app/utils/theme';
import {matchDeepLink} from 'app/utils/url';
import telemetry from 'app/telemetry';
+import {showModalOverCurrentContext} from 'app/actions/navigation';
import DateHeader from './date_header';
import NewMessagesDivider from './new_messages_divider';
@@ -41,7 +42,6 @@ export default class PostList extends PureComponent {
refreshChannelWithRetry: PropTypes.func.isRequired,
selectFocusedPostId: PropTypes.func.isRequired,
setDeepLinkURL: PropTypes.func.isRequired,
- showModalOverCurrentContext: PropTypes.func.isRequired,
}).isRequired,
channelId: PropTypes.string,
deepLinkURL: PropTypes.string,
@@ -353,7 +353,7 @@ export default class PostList extends PureComponent {
};
this.showingPermalink = true;
- actions.showModalOverCurrentContext(screen, passProps, options);
+ showModalOverCurrentContext(screen, passProps, options);
}
};
diff --git a/app/components/post_list/post_list.test.js b/app/components/post_list/post_list.test.js
index fe7c684b7..6935a50bb 100644
--- a/app/components/post_list/post_list.test.js
+++ b/app/components/post_list/post_list.test.js
@@ -4,9 +4,11 @@
import React from 'react';
import {shallow} from 'enzyme';
-import PostList from './post_list';
import Preferences from 'mattermost-redux/constants/preferences';
+import * as NavigationActions from 'app/actions/navigation';
+import PostList from './post_list';
+
jest.useFakeTimers();
describe('PostList', () => {
@@ -18,7 +20,6 @@ describe('PostList', () => {
refreshChannelWithRetry: jest.fn(),
selectFocusedPostId: jest.fn(),
setDeepLinkURL: jest.fn(),
- showModalOverCurrentContext: jest.fn(),
},
deepLinkURL: '',
lastPostIndex: -1,
@@ -42,10 +43,12 @@ describe('PostList', () => {
});
test('setting permalink deep link', () => {
+ const showModalOverCurrentContext = jest.spyOn(NavigationActions, 'showModalOverCurrentContext');
+
wrapper.setProps({deepLinkURL: deepLinks.permalink});
expect(baseProps.actions.setDeepLinkURL).toHaveBeenCalled();
expect(baseProps.actions.selectFocusedPostId).toHaveBeenCalled();
- expect(baseProps.actions.showModalOverCurrentContext).toHaveBeenCalled();
+ expect(showModalOverCurrentContext).toHaveBeenCalled();
expect(wrapper.getElement()).toMatchSnapshot();
});
diff --git a/app/components/post_textbox/__snapshots__/post_textbox.test.js.snap b/app/components/post_textbox/__snapshots__/post_textbox.test.js.snap
index 4ce8b6f59..924bdf35f 100644
--- a/app/components/post_textbox/__snapshots__/post_textbox.test.js.snap
+++ b/app/components/post_textbox/__snapshots__/post_textbox.test.js.snap
@@ -19,8 +19,15 @@ exports[`PostTextBox should match, full snapshot 1`] = `
]
}
>
-
{
- const {actions, theme} = this.props;
+ const {theme} = this.props;
const {formatMessage} = this.context.intl;
const screen = 'AddReaction';
const title = formatMessage({id: 'mobile.post_info.add_reaction', defaultMessage: 'Add Reaction'});
@@ -62,7 +62,7 @@ export default class Reactions extends PureComponent {
onEmojiPress: this.handleAddReactionToPost,
};
- actions.showModal(screen, title, passProps);
+ showModal(screen, title, passProps);
});
});
@@ -86,7 +86,7 @@ export default class Reactions extends PureComponent {
};
showReactionList = () => {
- const {actions, postId} = this.props;
+ const {postId} = this.props;
const screen = 'ReactionList';
const passProps = {
@@ -94,7 +94,7 @@ export default class Reactions extends PureComponent {
};
if (!this.onPressDetected) {
- actions.showModalOverCurrentContext(screen, passProps);
+ showModalOverCurrentContext(screen, passProps);
}
};
diff --git a/app/components/root/index.js b/app/components/root/index.js
index 7f4b28031..410e264bc 100644
--- a/app/components/root/index.js
+++ b/app/components/root/index.js
@@ -1,13 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {resetToTeams} from 'app/actions/navigation';
import {getCurrentLocale} from 'app/selectors/i18n';
import {removeProtocol} from 'app/utils/url';
@@ -23,12 +21,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- resetToTeams,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(Root);
+export default connect(mapStateToProps)(Root);
diff --git a/app/components/root/root.js b/app/components/root/root.js
index 95e46af44..d3f271886 100644
--- a/app/components/root/root.js
+++ b/app/components/root/root.js
@@ -9,14 +9,12 @@ import {Platform} from 'react-native';
import {Client4} from 'mattermost-redux/client';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
+import {resetToTeams} from 'app/actions/navigation';
import {NavigationTypes} from 'app/constants';
import {getTranslations} from 'app/i18n';
export default class Root extends PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- resetToTeams: PropTypes.func.isRequired,
- }).isRequired,
children: PropTypes.node,
excludeEvents: PropTypes.bool,
currentUrl: PropTypes.string,
@@ -63,7 +61,7 @@ export default class Root extends PureComponent {
}
navigateToTeamsPage = (screen) => {
- const {currentUrl, theme, actions} = this.props;
+ const {currentUrl, theme} = this.props;
const {intl} = this.refs.provider.getChildContext();
let passProps = {theme};
@@ -93,7 +91,7 @@ export default class Root extends PureComponent {
const title = intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'});
- actions.resetToTeams(screen, title, passProps, options);
+ resetToTeams(screen, title, passProps, options);
}
render() {
diff --git a/app/components/sidebars/main/channels_list/list/index.js b/app/components/sidebars/main/channels_list/list/index.js
index 82f450b12..fc7ab6c66 100644
--- a/app/components/sidebars/main/channels_list/list/index.js
+++ b/app/components/sidebars/main/channels_list/list/index.js
@@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {General} from 'mattermost-redux/constants';
@@ -20,7 +19,6 @@ import {getConfig, getLicense, hasNewPermissions} from 'mattermost-redux/selecto
import {haveITeamPermission} from 'mattermost-redux/selectors/entities/roles';
import Permissions from 'mattermost-redux/constants/permissions';
-import {showModal} from 'app/actions/navigation';
import {isLandscape} from 'app/selectors/device';
import {DeviceTypes, ViewTypes} from 'app/constants';
@@ -77,14 +75,6 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- showModal,
- }, dispatch),
- };
-}
-
function areStatesEqual(next, prev) {
const equalRoles = getCurrentUserRoles(prev) === getCurrentUserRoles(next);
const equalChannels = next.entities.channels === prev.entities.channels;
@@ -95,4 +85,4 @@ function areStatesEqual(next, prev) {
return equalChannels && equalConfig && equalRoles && equalUsers && equalFav;
}
-export default connect(mapStateToProps, mapDispatchToProps, null, {pure: true, areStatesEqual})(List);
+export default connect(mapStateToProps, null, null, {pure: true, areStatesEqual})(List);
diff --git a/app/components/sidebars/main/channels_list/list/list.js b/app/components/sidebars/main/channels_list/list/list.js
index 9b78a9e56..da9af34b6 100644
--- a/app/components/sidebars/main/channels_list/list/list.js
+++ b/app/components/sidebars/main/channels_list/list/list.js
@@ -21,14 +21,14 @@ import {General} from 'mattermost-redux/constants';
import {debounce} from 'mattermost-redux/actions/helpers';
import ChannelItem from 'app/components/sidebars/main/channels_list/channel_item';
+import {paddingLeft as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import {DeviceTypes, ListTypes} from 'app/constants';
import {SidebarSectionTypes} from 'app/constants/view';
import BottomSheet from 'app/utils/bottom_sheet';
import {t} from 'app/utils/i18n';
import {preventDoubleTap} from 'app/utils/tap';
-
-import {paddingLeft as padding} from 'app/components/safe_area_view/iphone_x_spacing';
+import {showModal} from 'app/actions/navigation';
const VIEWABILITY_CONFIG = {
...ListTypes.VISIBILITY_CONFIG_DEFAULTS,
@@ -50,9 +50,6 @@ export default class List extends PureComponent {
orderedChannelIds: PropTypes.array.isRequired,
previewChannel: PropTypes.func,
isLandscape: PropTypes.bool.isRequired,
- actions: PropTypes.shape({
- showModal: PropTypes.func.isRequired,
- }).isRequired,
};
static contextTypes = {
@@ -224,7 +221,6 @@ export default class List extends PureComponent {
};
goToCreatePublicChannel = preventDoubleTap(() => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'CreateChannel';
const title = intl.formatMessage({id: 'mobile.create_channel.public', defaultMessage: 'New Public Channel'});
@@ -233,11 +229,10 @@ export default class List extends PureComponent {
closeButton: this.closeButton,
};
- actions.showModal(screen, title, passProps);
+ showModal(screen, title, passProps);
});
goToCreatePrivateChannel = preventDoubleTap(() => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'CreateChannel';
const title = intl.formatMessage({id: 'mobile.create_channel.private', defaultMessage: 'New Private Channel'});
@@ -246,11 +241,10 @@ export default class List extends PureComponent {
closeButton: this.closeButton,
};
- actions.showModal(screen, title, passProps);
+ showModal(screen, title, passProps);
});
goToDirectMessages = preventDoubleTap(() => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'MoreDirectMessages';
const title = intl.formatMessage({id: 'mobile.more_dms.title', defaultMessage: 'New Conversation'});
@@ -264,11 +258,10 @@ export default class List extends PureComponent {
},
};
- actions.showModal(screen, title, passProps, options);
+ showModal(screen, title, passProps, options);
});
goToMoreChannels = preventDoubleTap(() => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'MoreChannels';
const title = intl.formatMessage({id: 'more_channels.title', defaultMessage: 'More Channels'});
@@ -276,7 +269,7 @@ export default class List extends PureComponent {
closeButton: this.closeButton,
};
- actions.showModal(screen, title, passProps);
+ showModal(screen, title, passProps);
});
keyExtractor = (item) => item.id || item;
diff --git a/app/components/sidebars/main/teams_list/index.js b/app/components/sidebars/main/teams_list/index.js
index f72990601..8d595920b 100644
--- a/app/components/sidebars/main/teams_list/index.js
+++ b/app/components/sidebars/main/teams_list/index.js
@@ -7,7 +7,6 @@ import {connect} from 'react-redux';
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';
@@ -28,7 +27,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
handleTeamChange,
- showModal,
}, dispatch),
};
}
diff --git a/app/components/sidebars/main/teams_list/teams_list.js b/app/components/sidebars/main/teams_list/teams_list.js
index b139e7fa6..b882e9806 100644
--- a/app/components/sidebars/main/teams_list/teams_list.js
+++ b/app/components/sidebars/main/teams_list/teams_list.js
@@ -23,6 +23,7 @@ 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';
+import {showModal} from 'app/actions/navigation';
import TeamsListItem from './teams_list_item';
@@ -36,7 +37,6 @@ export default class TeamsList extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
handleTeamChange: PropTypes.func.isRequired,
- showModal: PropTypes.func.isRequired,
}).isRequired,
closeChannelDrawer: PropTypes.func.isRequired,
currentTeamId: PropTypes.string.isRequired,
@@ -86,7 +86,7 @@ export default class TeamsList extends PureComponent {
goToSelectTeam = preventDoubleTap(async () => {
const {intl} = this.context;
- const {theme, actions} = this.props;
+ const {theme} = this.props;
const {serverUrl} = this.state;
const screen = 'SelectTeam';
const title = intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'});
@@ -103,7 +103,7 @@ export default class TeamsList extends PureComponent {
},
};
- actions.showModal(screen, title, passProps, options);
+ showModal(screen, title, passProps, options);
});
keyExtractor = (item) => {
diff --git a/app/components/sidebars/settings/index.js b/app/components/sidebars/settings/index.js
index 57c408b26..db0d83db5 100644
--- a/app/components/sidebars/settings/index.js
+++ b/app/components/sidebars/settings/index.js
@@ -8,12 +8,6 @@ import {logout, setStatus} from 'mattermost-redux/actions/users';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentUser, getStatusForUserId} from 'mattermost-redux/selectors/entities/users';
-import {
- showModal,
- showModalOverCurrentContext,
- dismissModal,
-} from 'app/actions/navigation';
-
import SettingsSidebar from './settings_sidebar';
function mapStateToProps(state) {
@@ -32,9 +26,6 @@ function mapDispatchToProps(dispatch) {
actions: bindActionCreators({
logout,
setStatus,
- showModal,
- showModalOverCurrentContext,
- dismissModal,
}, dispatch),
};
}
diff --git a/app/components/sidebars/settings/settings_sidebar.js b/app/components/sidebars/settings/settings_sidebar.js
index ea50c0678..f7d3b4531 100644
--- a/app/components/sidebars/settings/settings_sidebar.js
+++ b/app/components/sidebars/settings/settings_sidebar.js
@@ -25,6 +25,7 @@ import {confirmOutOfOfficeDisabled} from 'app/utils/status';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
+import {showModal, showModalOverCurrentContext, dismissModal} from 'app/actions/navigation';
import DrawerItem from './drawer_item';
import UserInfo from './user_info';
@@ -35,9 +36,6 @@ export default class SettingsDrawer extends PureComponent {
actions: PropTypes.shape({
logout: PropTypes.func.isRequired,
setStatus: PropTypes.func.isRequired,
- showModal: PropTypes.func.isRequired,
- showModalOverCurrentContext: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
}).isRequired,
blurPostTextBox: PropTypes.func.isRequired,
children: PropTypes.node,
@@ -127,7 +125,6 @@ export default class SettingsDrawer extends PureComponent {
};
handleSetStatus = preventDoubleTap(() => {
- const {actions} = this.props;
const items = [{
action: () => this.setStatus(General.ONLINE),
text: {
@@ -154,7 +151,7 @@ export default class SettingsDrawer extends PureComponent {
},
}];
- actions.showModalOverCurrentContext('OptionsModal', {items});
+ showModalOverCurrentContext('OptionsModal', {items});
});
goToEditProfile = preventDoubleTap(() => {
@@ -216,7 +213,6 @@ export default class SettingsDrawer extends PureComponent {
openModal = (screen, title, passProps) => {
this.closeSettingsSidebar();
- const {actions} = this.props;
const options = {
topBar: {
leftButtons: [{
@@ -227,7 +223,7 @@ export default class SettingsDrawer extends PureComponent {
};
InteractionManager.runAfterInteractions(() => {
- actions.showModal(screen, title, passProps, options);
+ showModal(screen, title, passProps, options);
});
};
@@ -354,10 +350,10 @@ export default class SettingsDrawer extends PureComponent {
};
setStatus = (status) => {
- const {status: currentUserStatus, actions} = this.props;
+ const {status: currentUserStatus} = this.props;
if (currentUserStatus === General.OUT_OF_OFFICE) {
- actions.dismissModal();
+ dismissModal();
this.closeSettingsSidebar();
this.confirmReset(status);
return;
diff --git a/app/init/global_event_handler.js b/app/init/global_event_handler.js
index 9f477ec6e..dc3c099d4 100644
--- a/app/init/global_event_handler.js
+++ b/app/init/global_event_handler.js
@@ -247,7 +247,7 @@ class GlobalEventHandler {
handleInAppNotification = (notification) => {
const {data} = notification;
- const {dispatch, getState} = this.store;
+ const {getState} = this.store;
const state = getState();
const currentChannelId = getCurrentChannelId(state);
@@ -258,7 +258,7 @@ class GlobalEventHandler {
};
EventEmitter.emit(NavigationTypes.NAVIGATION_SHOW_OVERLAY);
- dispatch(showOverlay(screen, passProps));
+ showOverlay(screen, passProps);
}
};
}
diff --git a/app/mattermost.js b/app/mattermost.js
index f1a35084b..6c9c9f5df 100644
--- a/app/mattermost.js
+++ b/app/mattermost.js
@@ -9,21 +9,19 @@ import {loadMe} from 'mattermost-redux/actions/users';
import {resetToChannel, resetToSelectServer} from 'app/actions/navigation';
import {setDeepLinkURL} from 'app/actions/views/root';
-import initialState from 'app/initial_state';
import {getAppCredentials} from 'app/init/credentials';
import emmProvider from 'app/init/emm_provider';
import 'app/init/device';
import 'app/init/fetch';
import globalEventHandler from 'app/init/global_event_handler';
import {registerScreens} from 'app/screens';
-import configureStore from 'app/store';
+import store from 'app/store';
import EphemeralStore from 'app/store/ephemeral_store';
import telemetry from 'app/telemetry';
import pushNotificationsUtils from 'app/utils/push_notifications';
const {MattermostShare} = NativeModules;
const sharedExtensionStarted = Platform.OS === 'android' && MattermostShare.isOpened;
-export const store = configureStore(initialState);
const init = async () => {
const credentials = await getAppCredentials();
@@ -57,9 +55,9 @@ const launchApp = async (credentials) => {
if (credentials) {
store.dispatch(loadMe());
- store.dispatch(resetToChannel({skipMetrics: true}));
+ resetToChannel({skipMetrics: true});
} else {
- store.dispatch(resetToSelectServer(emmProvider.allowOtherServers));
+ resetToSelectServer(emmProvider.allowOtherServers);
}
telemetry.startSinceLaunch(['start:splash_screen']);
diff --git a/app/screens/add_reaction/add_reaction.js b/app/screens/add_reaction/add_reaction.js
index e03ddbf85..86ab62b88 100644
--- a/app/screens/add_reaction/add_reaction.js
+++ b/app/screens/add_reaction/add_reaction.js
@@ -12,13 +12,10 @@ import {Navigation} from 'react-native-navigation';
import EmojiPicker from 'app/components/emoji_picker';
import {emptyFunction} from 'app/utils/general';
import {setNavigatorStyles} from 'app/utils/theme';
+import {dismissModal, setButtons} from 'app/actions/navigation';
export default class AddReaction extends PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- dismissModal: PropTypes.func.isRequired,
- setButtons: PropTypes.func.isRequired,
- }).isRequired,
componentId: PropTypes.string,
closeButton: PropTypes.object,
onEmojiPress: PropTypes.func,
@@ -36,7 +33,7 @@ export default class AddReaction extends PureComponent {
constructor(props) {
super(props);
- props.actions.setButtons(props.componentId, {
+ setButtons(props.componentId, {
leftButtons: [{...this.leftButton, icon: props.closeButton}],
});
}
@@ -58,7 +55,7 @@ export default class AddReaction extends PureComponent {
}
close = () => {
- this.props.actions.dismissModal();
+ dismissModal();
};
handleEmojiPress = (emoji) => {
diff --git a/app/screens/add_reaction/index.js b/app/screens/add_reaction/index.js
index eb24346b2..1c3d7556b 100644
--- a/app/screens/add_reaction/index.js
+++ b/app/screens/add_reaction/index.js
@@ -1,13 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {dismissModal, setButtons} from 'app/actions/navigation';
-
import AddReaction from './add_reaction';
function mapStateToProps(state) {
@@ -16,13 +13,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- dismissModal,
- setButtons,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(AddReaction);
+export default connect(mapStateToProps)(AddReaction);
diff --git a/app/screens/channel/channel.ios.js b/app/screens/channel/channel.ios.js
index 6c9c6c5f5..c9ee1a9ae 100644
--- a/app/screens/channel/channel.ios.js
+++ b/app/screens/channel/channel.ios.js
@@ -13,6 +13,7 @@ import PostTextbox from 'app/components/post_textbox';
import SafeAreaView from 'app/components/safe_area_view';
import StatusBar from 'app/components/status_bar';
import {DeviceTypes} from 'app/constants';
+import {peek} from 'app/actions/navigation';
import LocalConfig from 'assets/config';
@@ -26,10 +27,9 @@ const CHANNEL_POST_TEXTBOX_VALUE_CHANGE = 'onChannelTextBoxValueChange';
export default class ChannelIOS extends ChannelBase {
previewChannel = (passProps, options) => {
- const {actions} = this.props;
const screen = 'ChannelPeek';
- actions.peek(screen, passProps, options);
+ peek(screen, passProps, options);
};
optionalProps = {previewChannel: this.previewChannel};
diff --git a/app/screens/channel/channel_base.js b/app/screens/channel/channel_base.js
index 076c7aea4..acaca1cf5 100644
--- a/app/screens/channel/channel_base.js
+++ b/app/screens/channel/channel_base.js
@@ -9,7 +9,6 @@ import {
StyleSheet,
View,
} from 'react-native';
-import {Navigation} from 'react-native-navigation';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
@@ -25,6 +24,11 @@ import PushNotifications from 'app/push_notifications';
import EphemeralStore from 'app/store/ephemeral_store';
import tracker from 'app/utils/time_tracker';
import telemetry from 'app/telemetry';
+import {
+ goToScreen,
+ showModalOverCurrentContext,
+ mergeNavigationOptions,
+} from 'app/actions/navigation';
import LocalConfig from 'assets/config';
@@ -38,9 +42,6 @@ export default class ChannelBase extends PureComponent {
selectDefaultTeam: PropTypes.func.isRequired,
selectInitialChannel: PropTypes.func.isRequired,
recordLoadTime: PropTypes.func.isRequired,
- peek: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
- showModalOverCurrentContext: PropTypes.func.isRequired,
getChannelStats: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string.isRequired,
@@ -68,11 +69,12 @@ export default class ChannelBase extends PureComponent {
this.postTextbox = React.createRef();
this.keyboardTracker = React.createRef();
- Navigation.mergeOptions(props.componentId, {
+ const options = {
layout: {
backgroundColor: props.theme.centerChannelBg,
},
- });
+ };
+ mergeNavigationOptions(props.componentId, options);
if (LocalConfig.EnableMobileClientUpgrade && !ClientUpgradeListener) {
ClientUpgradeListener = require('app/components/client_upgrade_listener').default;
@@ -113,11 +115,12 @@ export default class ChannelBase extends PureComponent {
componentWillReceiveProps(nextProps) {
if (this.props.theme !== nextProps.theme) {
- Navigation.mergeOptions(this.props.componentId, {
+ const options = {
layout: {
backgroundColor: nextProps.theme.centerChannelBg,
},
- });
+ };
+ mergeNavigationOptions(this.props.componentId, options);
}
if (nextProps.currentTeamId && this.props.currentTeamId !== nextProps.currentTeamId) {
@@ -177,7 +180,7 @@ export default class ChannelBase extends PureComponent {
showTermsOfServiceModal = async () => {
const {intl} = this.context;
- const {actions, theme} = this.props;
+ const {theme} = this.props;
const closeButton = await MaterialIcon.getImageSource('close', 20, theme.sidebarHeaderTextColor);
const screen = 'TermsOfService';
const passProps = {closeButton};
@@ -196,11 +199,10 @@ export default class ChannelBase extends PureComponent {
},
};
- actions.showModalOverCurrentContext(screen, passProps, options);
+ showModalOverCurrentContext(screen, passProps, options);
};
goToChannelInfo = preventDoubleTap(() => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'ChannelInfo';
const title = intl.formatMessage({id: 'mobile.routes.channelInfo', defaultMessage: 'Info'});
@@ -208,7 +210,7 @@ export default class ChannelBase extends PureComponent {
Keyboard.dismiss();
requestAnimationFrame(() => {
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
});
});
diff --git a/app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js b/app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js
index 554d14f55..c2ac871e1 100644
--- a/app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js
+++ b/app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js
@@ -12,12 +12,12 @@ import AwesomeIcon from 'react-native-vector-icons/FontAwesome';
import {preventDoubleTap} from 'app/utils/tap';
import {makeStyleSheetFromTheme} from 'app/utils/theme';
+import {showSearchModal} from 'app/actions/navigation';
export default class ChannelSearchButton extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
clearSearch: PropTypes.func.isRequired,
- showSearchModal: PropTypes.func.isRequired,
}).isRequired,
theme: PropTypes.object,
};
@@ -27,7 +27,7 @@ export default class ChannelSearchButton extends PureComponent {
Keyboard.dismiss();
await actions.clearSearch();
- await actions.showSearchModal();
+ showSearchModal();
});
render() {
diff --git a/app/screens/channel/channel_nav_bar/channel_search_button/index.js b/app/screens/channel/channel_nav_bar/channel_search_button/index.js
index 8bbb62cfb..920322f7d 100644
--- a/app/screens/channel/channel_nav_bar/channel_search_button/index.js
+++ b/app/screens/channel/channel_nav_bar/channel_search_button/index.js
@@ -6,15 +6,12 @@ import {connect} from 'react-redux';
import {clearSearch} from 'mattermost-redux/actions/search';
-import {showSearchModal} from 'app/actions/navigation';
-
import ChannelSearchButton from './channel_search_button';
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
clearSearch,
- showSearchModal,
}, dispatch),
};
}
diff --git a/app/screens/channel/channel_post_list/channel_post_list.js b/app/screens/channel/channel_post_list/channel_post_list.js
index 82fab1e02..40b66c292 100644
--- a/app/screens/channel/channel_post_list/channel_post_list.js
+++ b/app/screens/channel/channel_post_list/channel_post_list.js
@@ -19,6 +19,7 @@ import {ViewTypes} from 'app/constants';
import tracker from 'app/utils/time_tracker';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import telemetry from 'app/telemetry';
+import {goToScreen} from 'app/actions/navigation';
let ChannelIntro = null;
let LoadMorePosts = null;
@@ -32,7 +33,6 @@ export default class ChannelPostList extends PureComponent {
selectPost: PropTypes.func.isRequired,
recordLoadTime: PropTypes.func.isRequired,
refreshChannelWithRetry: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
}).isRequired,
channelId: PropTypes.string.isRequired,
channelRefreshingFailed: PropTypes.bool,
@@ -119,7 +119,7 @@ export default class ChannelPostList extends PureComponent {
};
requestAnimationFrame(() => {
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
});
};
diff --git a/app/screens/channel/channel_post_list/index.js b/app/screens/channel/channel_post_list/index.js
index 1f1d27ceb..b3689b36c 100644
--- a/app/screens/channel/channel_post_list/index.js
+++ b/app/screens/channel/channel_post_list/index.js
@@ -10,7 +10,6 @@ import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels'
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {goToScreen} from 'app/actions/navigation';
import {loadPostsIfNecessaryWithRetry, loadThreadIfNecessary, increasePostVisibility, refreshChannelWithRetry} from 'app/actions/views/channel';
import {recordLoadTime} from 'app/actions/views/root';
import {isLandscape} from 'app/selectors/device';
@@ -45,7 +44,6 @@ function mapDispatchToProps(dispatch) {
selectPost,
recordLoadTime,
refreshChannelWithRetry,
- goToScreen,
}, dispatch),
};
}
diff --git a/app/screens/channel/index.js b/app/screens/channel/index.js
index cbfd5bc02..228353389 100644
--- a/app/screens/channel/index.js
+++ b/app/screens/channel/index.js
@@ -20,7 +20,6 @@ import {
import {connection} from 'app/actions/device';
import {recordLoadTime} from 'app/actions/views/root';
import {selectDefaultTeam} from 'app/actions/views/select_team';
-import {peek, goToScreen, showModalOverCurrentContext} from 'app/actions/navigation';
import {isLandscape} from 'app/selectors/device';
import Channel from './channel';
@@ -51,9 +50,6 @@ function mapDispatchToProps(dispatch) {
recordLoadTime,
startPeriodicStatusUpdates,
stopPeriodicStatusUpdates,
- peek,
- goToScreen,
- showModalOverCurrentContext,
}, dispatch),
};
}
diff --git a/app/screens/channel_add_members/channel_add_members.js b/app/screens/channel_add_members/channel_add_members.js
index d9918ad54..58ec88b2f 100644
--- a/app/screens/channel_add_members/channel_add_members.js
+++ b/app/screens/channel_add_members/channel_add_members.js
@@ -14,6 +14,7 @@ import {Navigation} from 'react-native-navigation';
import {debounce} from 'mattermost-redux/actions/helpers';
import {General} from 'mattermost-redux/constants';
import {filterProfilesMatchingTerm} from 'mattermost-redux/utils/user_utils';
+
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import Loading from 'app/components/loading';
import CustomList, {FLATLIST, SECTIONLIST} from 'app/components/custom_list';
@@ -30,6 +31,7 @@ import {
setNavigatorStyles,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
+import {popTopScreen, setButtons} from 'app/actions/navigation';
export default class ChannelAddMembers extends PureComponent {
static propTypes = {
@@ -38,8 +40,6 @@ export default class ChannelAddMembers extends PureComponent {
getProfilesNotInChannel: PropTypes.func.isRequired,
handleAddChannelMembers: PropTypes.func.isRequired,
searchProfiles: PropTypes.func.isRequired,
- setButtons: PropTypes.func.isRequired,
- popTopScreen: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
currentChannelId: PropTypes.string.isRequired,
@@ -82,7 +82,7 @@ export default class ChannelAddMembers extends PureComponent {
showAsAction: 'always',
};
- props.actions.setButtons(props.componentId, {
+ setButtons(props.componentId, {
rightButtons: [this.addButton],
});
}
@@ -121,12 +121,12 @@ export default class ChannelAddMembers extends PureComponent {
};
close = () => {
- this.props.actions.popTopScreen();
+ popTopScreen();
};
enableAddOption = (enabled) => {
- const {actions, componentId} = this.props;
- actions.setButtons(componentId, {
+ const {componentId} = this.props;
+ setButtons(componentId, {
rightButtons: [{...this.addButton, enabled}],
});
};
diff --git a/app/screens/channel_add_members/channel_add_members.test.js b/app/screens/channel_add_members/channel_add_members.test.js
index c5df7b106..8d83487dd 100644
--- a/app/screens/channel_add_members/channel_add_members.test.js
+++ b/app/screens/channel_add_members/channel_add_members.test.js
@@ -5,6 +5,7 @@ import React from 'react';
import {Preferences} from 'mattermost-redux/constants';
+import * as NavigationActions from 'app/actions/navigation';
import {shallowWithIntl} from 'test/intl-test-helper';
import ChannelAddMembers from './channel_add_members';
@@ -16,8 +17,6 @@ describe('ChannelAddMembers', () => {
getProfilesNotInChannel: jest.fn().mockResolvedValue({}),
handleAddChannelMembers: jest.fn().mockResolvedValue({}),
searchProfiles: jest.fn().mockResolvedValue({data: []}),
- setButtons: jest.fn(),
- popTopScreen: jest.fn(),
},
currentChannelId: 'current_channel_id',
currentTeamId: 'current_team_id',
@@ -29,15 +28,17 @@ describe('ChannelAddMembers', () => {
};
test('should render without error and call functions on mount', () => {
+ const setButtons = jest.spyOn(NavigationActions, 'setButtons');
+
shallowWithIntl();
expect(baseProps.actions.getTeamStats).toBeCalledTimes(1);
expect(baseProps.actions.getTeamStats).toBeCalledWith(baseProps.currentTeamId);
const button = {enabled: false, id: 'add-members', text: 'Add', showAsAction: 'always'};
- expect(baseProps.actions.setButtons).toBeCalledTimes(2);
- expect(baseProps.actions.setButtons.mock.calls[0][0]).toEqual(baseProps.componentId, {rightButtons: [button]});
- expect(baseProps.actions.setButtons.mock.calls[1][0]).toEqual(baseProps.componentId, {rightButtons: [button]});
+ expect(setButtons).toBeCalledTimes(2);
+ expect(setButtons.mock.calls[0][0]).toEqual(baseProps.componentId, {rightButtons: [button]});
+ expect(setButtons.mock.calls[1][0]).toEqual(baseProps.componentId, {rightButtons: [button]});
});
test('should match state on clearSearch', () => {
@@ -49,11 +50,13 @@ describe('ChannelAddMembers', () => {
wrapper.setState({term: '', searchResults: []});
});
- test('should call props.popTopScreen on close', () => {
+ test('should call popTopScreen on close', () => {
+ const popTopScreen = jest.spyOn(NavigationActions, 'popTopScreen');
+
const wrapper = shallowWithIntl();
wrapper.instance().close();
- expect(baseProps.actions.popTopScreen).toBeCalledTimes(1);
+ expect(popTopScreen).toBeCalledTimes(1);
});
test('should match state on onProfilesLoaded', () => {
diff --git a/app/screens/channel_add_members/index.js b/app/screens/channel_add_members/index.js
index 431db3cea..8d4582477 100644
--- a/app/screens/channel_add_members/index.js
+++ b/app/screens/channel_add_members/index.js
@@ -11,7 +11,6 @@ import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentUserId, getProfilesNotInCurrentChannel} from 'mattermost-redux/selectors/entities/users';
-import {setButtons, popTopScreen} from 'app/actions/navigation';
import {handleAddChannelMembers} from 'app/actions/views/channel_add_members';
import {isLandscape} from 'app/selectors/device';
import ChannelAddMembers from './channel_add_members';
@@ -37,8 +36,6 @@ function mapDispatchToProps(dispatch) {
getProfilesNotInChannel,
handleAddChannelMembers,
searchProfiles,
- setButtons,
- popTopScreen,
}, dispatch),
};
}
diff --git a/app/screens/channel_info/__snapshots__/channel_info.test.js.snap b/app/screens/channel_info/__snapshots__/channel_info.test.js.snap
index ea562ae80..c2003eb01 100644
--- a/app/screens/channel_info/__snapshots__/channel_info.test.js.snap
+++ b/app/screens/channel_info/__snapshots__/channel_info.test.js.snap
@@ -29,7 +29,6 @@ exports[`channel_info should match snapshot 1`] = `
isGroupConstrained={false}
memberCount={2}
onPermalinkPress={[Function]}
- popToRoot={[MockFunction]}
purpose="Purpose"
status="status"
theme={
diff --git a/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap b/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap
index 3bb6582f1..1c745ee44 100644
--- a/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap
+++ b/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap
@@ -283,6 +283,7 @@ exports[`channel_info_header should match snapshot 1`] = `
},
}
}
+ onChannelLinkPress={[MockFunction]}
onPermalinkPress={[MockFunction]}
textStyles={
Object {
@@ -718,6 +719,7 @@ exports[`channel_info_header should match snapshot when DM and hasGuests 1`] = `
},
}
}
+ onChannelLinkPress={[MockFunction]}
onPermalinkPress={[MockFunction]}
textStyles={
Object {
@@ -1153,6 +1155,7 @@ exports[`channel_info_header should match snapshot when GM and hasGuests 1`] = `
},
}
}
+ onChannelLinkPress={[MockFunction]}
onPermalinkPress={[MockFunction]}
textStyles={
Object {
@@ -1560,6 +1563,7 @@ exports[`channel_info_header should match snapshot when is group constrained 1`]
},
}
}
+ onChannelLinkPress={[MockFunction]}
onPermalinkPress={[MockFunction]}
textStyles={
Object {
@@ -2016,6 +2020,7 @@ exports[`channel_info_header should match snapshot when public channel and hasGu
},
}
}
+ onChannelLinkPress={[MockFunction]}
onPermalinkPress={[MockFunction]}
textStyles={
Object {
diff --git a/app/screens/channel_info/channel_info.js b/app/screens/channel_info/channel_info.js
index ff9385a0b..f24d61c7f 100644
--- a/app/screens/channel_info/channel_info.js
+++ b/app/screens/channel_info/channel_info.js
@@ -17,6 +17,8 @@ import {preventDoubleTap} from 'app/utils/tap';
import {alertErrorWithFallback} from 'app/utils/general';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
+import {goToScreen, popTopScreen, showModalOverCurrentContext} from 'app/actions/navigation';
+
import pinIcon from 'assets/images/channel_info/pin.png';
import ChannelInfoHeader from './channel_info_header';
@@ -42,10 +44,6 @@ export default class ChannelInfo extends PureComponent {
selectPenultimateChannel: PropTypes.func.isRequired,
handleSelectChannel: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired,
- popTopScreen: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- showModalOverCurrentContext: PropTypes.func.isRequired,
}),
componentId: PropTypes.string,
viewArchivedChannels: PropTypes.bool.isRequired,
@@ -122,31 +120,30 @@ export default class ChannelInfo extends PureComponent {
actions.setChannelDisplayName('');
}
- actions.popTopScreen();
+ popTopScreen();
};
goToChannelAddMembers = preventDoubleTap(() => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'ChannelAddMembers';
const title = intl.formatMessage({id: 'channel_header.addMembers', defaultMessage: 'Add Members'});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
});
goToChannelMembers = preventDoubleTap(() => {
- const {actions, canManageUsers} = this.props;
+ const {canManageUsers} = this.props;
const {intl} = this.context;
const id = canManageUsers ? t('channel_header.manageMembers') : t('channel_header.viewMembers');
const defaultMessage = canManageUsers ? 'Manage Members' : 'View Members';
const screen = 'ChannelMembers';
const title = intl.formatMessage({id, defaultMessage});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
});
goToPinnedPosts = preventDoubleTap(() => {
- const {actions, currentChannel} = this.props;
+ const {currentChannel} = this.props;
const {formatMessage} = this.context.intl;
const id = t('channel_header.pinnedPosts');
const defaultMessage = 'Pinned Posts';
@@ -156,18 +153,17 @@ export default class ChannelInfo extends PureComponent {
currentChannelId: currentChannel.id,
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
});
handleChannelEdit = preventDoubleTap(() => {
- const {actions} = this.props;
const {intl} = this.context;
const id = t('mobile.channel_info.edit');
const defaultMessage = 'Edit Channel';
const screen = 'EditChannel';
const title = intl.formatMessage({id, defaultMessage});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
});
handleLeave = () => {
@@ -379,7 +375,7 @@ export default class ChannelInfo extends PureComponent {
actions.selectFocusedPostId(postId);
this.showingPermalink = true;
- actions.showModalOverCurrentContext(screen, passProps, options);
+ showModalOverCurrentContext(screen, passProps, options);
};
renderViewOrManageMembersRow = () => {
@@ -564,7 +560,6 @@ export default class ChannelInfo extends PureComponent {
theme,
isBot,
isLandscape,
- actions: {popToRoot},
} = this.props;
const style = getStyleSheet(theme);
@@ -605,7 +600,6 @@ export default class ChannelInfo extends PureComponent {
isBot={isBot}
hasGuests={currentChannelGuestCount > 0}
isGroupConstrained={currentChannel.group_constrained}
- popToRoot={popToRoot}
/>
}
diff --git a/app/screens/channel_info/channel_info.test.js b/app/screens/channel_info/channel_info.test.js
index 71d0ce1be..3aac0d3dc 100644
--- a/app/screens/channel_info/channel_info.test.js
+++ b/app/screens/channel_info/channel_info.test.js
@@ -79,11 +79,6 @@ describe('channel_info', () => {
selectPenultimateChannel: jest.fn(),
setChannelDisplayName: jest.fn(),
handleSelectChannel: jest.fn(),
- popTopScreen: jest.fn(),
- goToScreen: jest.fn(),
- popToRoot: jest.fn(),
- dismissModal: jest.fn(),
- showModalOverCurrentContext: jest.fn(),
},
};
diff --git a/app/screens/channel_info/channel_info_header.js b/app/screens/channel_info/channel_info_header.js
index cf607b4a6..9389822eb 100644
--- a/app/screens/channel_info/channel_info_header.js
+++ b/app/screens/channel_info/channel_info_header.js
@@ -24,6 +24,7 @@ import BottomSheet from 'app/utils/bottom_sheet';
import {getMarkdownTextStyles, getMarkdownBlockStyles} from 'app/utils/markdown';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
+import {popToRoot} from 'app/actions/navigation';
export default class ChannelInfoHeader extends React.PureComponent {
static propTypes = {
@@ -42,7 +43,6 @@ export default class ChannelInfoHeader extends React.PureComponent {
hasGuests: PropTypes.bool.isRequired,
isGroupConstrained: PropTypes.bool,
timeZone: PropTypes.string,
- popToRoot: PropTypes.func,
};
static contextTypes = {
@@ -138,7 +138,6 @@ export default class ChannelInfoHeader extends React.PureComponent {
isBot,
isGroupConstrained,
timeZone,
- popToRoot,
} = this.props;
const style = getStyleSheet(theme);
diff --git a/app/screens/channel_info/index.js b/app/screens/channel_info/index.js
index bbf55994a..03cfd499f 100644
--- a/app/screens/channel_info/index.js
+++ b/app/screens/channel_info/index.js
@@ -33,13 +33,6 @@ import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general
import {isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone';
import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils';
-import {
- popTopScreen,
- goToScreen,
- popToRoot,
- dismissModal,
- showModalOverCurrentContext,
-} from 'app/actions/navigation';
import {
closeDMChannel,
closeGMChannel,
@@ -148,11 +141,6 @@ function mapDispatchToProps(dispatch) {
selectPenultimateChannel,
setChannelDisplayName,
handleSelectChannel,
- popTopScreen,
- goToScreen,
- popToRoot,
- dismissModal,
- showModalOverCurrentContext,
}, dispatch),
};
}
diff --git a/app/screens/channel_members/channel_members.js b/app/screens/channel_members/channel_members.js
index 173ec925c..d1a8bad2e 100644
--- a/app/screens/channel_members/channel_members.js
+++ b/app/screens/channel_members/channel_members.js
@@ -14,6 +14,7 @@ import {Navigation} from 'react-native-navigation';
import {debounce} from 'mattermost-redux/actions/helpers';
import {General} from 'mattermost-redux/constants';
import {filterProfilesMatchingTerm} from 'mattermost-redux/utils/user_utils';
+
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import Loading from 'app/components/loading';
import CustomList, {FLATLIST, SECTIONLIST} from 'app/components/custom_list';
@@ -30,6 +31,7 @@ import {
setNavigatorStyles,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
+import {popTopScreen, setButtons} from 'app/actions/navigation';
export default class ChannelMembers extends PureComponent {
static propTypes = {
@@ -37,8 +39,6 @@ export default class ChannelMembers extends PureComponent {
getProfilesInChannel: PropTypes.func.isRequired,
handleRemoveChannelMembers: PropTypes.func.isRequired,
searchProfiles: PropTypes.func.isRequired,
- setButtons: PropTypes.func.isRequired,
- popTopScreen: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
canManageUsers: PropTypes.bool.isRequired,
@@ -78,7 +78,7 @@ export default class ChannelMembers extends PureComponent {
};
if (props.canManageUsers) {
- props.actions.setButtons(props.componentId, {
+ setButtons(props.componentId, {
rightButtons: [this.removeButton],
});
}
@@ -113,13 +113,13 @@ export default class ChannelMembers extends PureComponent {
};
close = () => {
- this.props.actions.popTopScreen();
+ popTopScreen();
};
enableRemoveOption = (enabled) => {
- const {actions, canManageUsers, componentId} = this.props;
+ const {canManageUsers, componentId} = this.props;
if (canManageUsers) {
- actions.setButtons(componentId, {
+ setButtons(componentId, {
rightButtons: [{...this.removeButton, enabled}],
});
}
diff --git a/app/screens/channel_members/channel_members.test.js b/app/screens/channel_members/channel_members.test.js
index 1c4aa2e29..90cbdf641 100644
--- a/app/screens/channel_members/channel_members.test.js
+++ b/app/screens/channel_members/channel_members.test.js
@@ -19,8 +19,6 @@ describe('ChannelMembers', () => {
getProfilesInChannel: jest.fn().mockImplementation(() => Promise.resolve()),
handleRemoveChannelMembers: jest.fn(),
searchProfiles: jest.fn(),
- setButtons: jest.fn(),
- popTopScreen: jest.fn(),
},
componentId: 'component-id',
isLandscape: false,
diff --git a/app/screens/channel_members/index.js b/app/screens/channel_members/index.js
index 06d383feb..96f6d0362 100644
--- a/app/screens/channel_members/index.js
+++ b/app/screens/channel_members/index.js
@@ -9,7 +9,6 @@ import {getCurrentChannel, canManageChannelMembers} from 'mattermost-redux/selec
import {makeGetProfilesInChannel} from 'mattermost-redux/selectors/entities/users';
import {getProfilesInChannel, searchProfiles} from 'mattermost-redux/actions/users';
-import {setButtons, popTopScreen} from 'app/actions/navigation';
import {handleRemoveChannelMembers} from 'app/actions/views/channel_members';
import {isLandscape} from 'app/selectors/device';
import ChannelMembers from './channel_members';
@@ -43,8 +42,6 @@ function mapDispatchToProps(dispatch) {
getProfilesInChannel,
handleRemoveChannelMembers,
searchProfiles,
- setButtons,
- popTopScreen,
}, dispatch),
};
}
diff --git a/app/screens/client_upgrade/client_upgrade.js b/app/screens/client_upgrade/client_upgrade.js
index c0768a3a5..c4de963f5 100644
--- a/app/screens/client_upgrade/client_upgrade.js
+++ b/app/screens/client_upgrade/client_upgrade.js
@@ -20,14 +20,13 @@ import {UpgradeTypes} from 'app/constants';
import logo from 'assets/images/logo.png';
import {checkUpgradeType, isUpgradeAvailable} from 'app/utils/client_upgrade';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
+import {popTopScreen, dismissModal} from 'app/actions/navigation';
export default class ClientUpgrade extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
logError: PropTypes.func.isRequired,
setLastUpgradeCheck: PropTypes.func.isRequired,
- popTopScreen: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
currentVersion: PropTypes.string,
@@ -68,7 +67,7 @@ export default class ClientUpgrade extends PureComponent {
navigationButtonPressed({buttonId}) {
if (buttonId === 'close-upgrade') {
- this.props.actions.dismissModal();
+ dismissModal();
}
}
@@ -97,15 +96,14 @@ export default class ClientUpgrade extends PureComponent {
const {
closeAction,
userCheckedForUpgrade,
- actions,
} = this.props;
if (closeAction) {
closeAction();
} else if (userCheckedForUpgrade) {
- actions.popTopScreen();
+ popTopScreen();
} else {
- actions.dismissModal();
+ dismissModal();
}
};
diff --git a/app/screens/client_upgrade/index.js b/app/screens/client_upgrade/index.js
index 14f67f586..b0984934c 100644
--- a/app/screens/client_upgrade/index.js
+++ b/app/screens/client_upgrade/index.js
@@ -5,7 +5,6 @@ import {connect} from 'react-redux';
import {logError} from 'mattermost-redux/actions/errors';
-import {popTopScreen, dismissModal} from 'app/actions/navigation';
import {setLastUpgradeCheck} from 'app/actions/views/client_upgrade';
import getClientUpgrade from 'app/selectors/client_upgrade';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
@@ -30,8 +29,6 @@ function mapDispatchToProps(dispatch) {
actions: bindActionCreators({
logError,
setLastUpgradeCheck,
- popTopScreen,
- dismissModal,
}, dispatch),
};
}
diff --git a/app/screens/code/code.js b/app/screens/code/code.js
index ef3e5e7b5..0e839adf7 100644
--- a/app/screens/code/code.js
+++ b/app/screens/code/code.js
@@ -20,12 +20,10 @@ import {
setNavigatorStyles,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
+import {popTopScreen} from 'app/actions/navigation';
export default class Code extends React.PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- popTopScreen: PropTypes.func.isRequired,
- }).isRequired,
componentId: PropTypes.string,
theme: PropTypes.object.isRequired,
content: PropTypes.string.isRequired,
@@ -46,7 +44,7 @@ export default class Code extends React.PureComponent {
}
handleAndroidBack = () => {
- this.props.actions.popTopScreen();
+ popTopScreen();
return true;
};
diff --git a/app/screens/code/index.js b/app/screens/code/index.js
index c833020be..4b0abdcd9 100644
--- a/app/screens/code/index.js
+++ b/app/screens/code/index.js
@@ -1,13 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {popTopScreen} from 'app/actions/navigation';
-
import Code from './code';
function mapStateToProps(state) {
@@ -16,12 +13,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- popTopScreen,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(Code);
\ No newline at end of file
+export default connect(mapStateToProps)(Code);
\ No newline at end of file
diff --git a/app/screens/create_channel/create_channel.js b/app/screens/create_channel/create_channel.js
index e4aaf99d0..2a55c286d 100644
--- a/app/screens/create_channel/create_channel.js
+++ b/app/screens/create_channel/create_channel.js
@@ -15,6 +15,7 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter';
import EditChannelInfo from 'app/components/edit_channel_info';
import {setNavigatorStyles} from 'app/utils/theme';
+import {popTopScreen, dismissModal, setButtons} from 'app/actions/navigation';
export default class CreateChannel extends PureComponent {
static propTypes = {
@@ -27,9 +28,6 @@ export default class CreateChannel extends PureComponent {
closeButton: PropTypes.object,
actions: PropTypes.shape({
handleCreateChannel: PropTypes.func.isRequired,
- setButtons: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- popTopScreen: PropTypes.func.isRequired,
}),
};
@@ -116,16 +114,15 @@ export default class CreateChannel extends PureComponent {
}
close = (goBack = false) => {
- const {actions} = this.props;
if (goBack) {
- actions.popTopScreen();
+ popTopScreen();
} else {
- actions.dismissModal();
+ dismissModal();
}
};
emitCanCreateChannel = (enabled) => {
- const {actions, componentId} = this.props;
+ const {componentId} = this.props;
const buttons = {
rightButtons: [{...this.rightButton, enabled}],
};
@@ -134,11 +131,11 @@ export default class CreateChannel extends PureComponent {
buttons.leftButtons = [this.left];
}
- actions.setButtons(componentId, buttons);
+ setButtons(componentId, buttons);
};
emitCreating = (loading) => {
- const {actions, componentId} = this.props;
+ const {componentId} = this.props;
const buttons = {
rightButtons: [{...this.rightButton, enabled: !loading}],
};
@@ -147,7 +144,7 @@ export default class CreateChannel extends PureComponent {
buttons.leftButtons = [this.left];
}
- actions.setButtons(componentId, buttons);
+ setButtons(componentId, buttons);
};
onCreateChannel = () => {
diff --git a/app/screens/create_channel/index.js b/app/screens/create_channel/index.js
index b08d294bf..e3111d7cb 100644
--- a/app/screens/create_channel/index.js
+++ b/app/screens/create_channel/index.js
@@ -6,11 +6,6 @@ import {connect} from 'react-redux';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {
- setButtons,
- dismissModal,
- popTopScreen,
-} from 'app/actions/navigation';
import {handleCreateChannel} from 'app/actions/views/create_channel';
import {getDimensions} from 'app/selectors/device';
@@ -32,9 +27,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
handleCreateChannel,
- setButtons,
- dismissModal,
- popTopScreen,
}, dispatch),
};
}
diff --git a/app/screens/edit_channel/edit_channel.js b/app/screens/edit_channel/edit_channel.js
index 667e0ade1..22aa7f883 100644
--- a/app/screens/edit_channel/edit_channel.js
+++ b/app/screens/edit_channel/edit_channel.js
@@ -18,6 +18,7 @@ import {ViewTypes} from 'app/constants';
import {setNavigatorStyles} from 'app/utils/theme';
import {cleanUpUrlable} from 'app/utils/url';
import {t} from 'app/utils/i18n';
+import {popTopScreen, setButtons} from 'app/actions/navigation';
const messages = {
display_name_required: {
@@ -56,8 +57,6 @@ export default class EditChannel extends PureComponent {
patchChannel: PropTypes.func.isRequired,
getChannel: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired,
- setButtons: PropTypes.func.isRequired,
- popTopScreen: PropTypes.func.isRequired,
}),
componentId: PropTypes.string,
theme: PropTypes.object.isRequired,
@@ -106,7 +105,7 @@ export default class EditChannel extends PureComponent {
rightButtons: [this.rightButton],
};
- props.actions.setButtons(props.componentId, buttons);
+ setButtons(props.componentId, buttons);
}
componentDidMount() {
@@ -163,25 +162,25 @@ export default class EditChannel extends PureComponent {
this.props.actions.setChannelDisplayName(this.state.displayName);
}
- this.props.actions.popTopScreen();
+ popTopScreen();
};
emitCanUpdateChannel = (enabled) => {
- const {actions, componentId} = this.props;
+ const {componentId} = this.props;
const buttons = {
rightButtons: [{...this.rightButton, enabled}],
};
- actions.setButtons(componentId, buttons);
+ setButtons(componentId, buttons);
};
emitUpdating = (loading) => {
- const {actions, componentId} = this.props;
+ const {componentId} = this.props;
const buttons = {
rightButtons: [{...this.rightButton, enabled: !loading}],
};
- actions.setButtons(componentId, buttons);
+ setButtons(componentId, buttons);
};
validateDisplayName = (displayName) => {
diff --git a/app/screens/edit_channel/index.js b/app/screens/edit_channel/index.js
index b8a87fb6f..4a0eb2182 100644
--- a/app/screens/edit_channel/index.js
+++ b/app/screens/edit_channel/index.js
@@ -9,7 +9,6 @@ import {getCurrentTeamUrl} from 'mattermost-redux/selectors/entities/teams';
import {patchChannel, getChannel} from 'mattermost-redux/actions/channels';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {setButtons, popTopScreen} from 'app/actions/navigation';
import {setChannelDisplayName} from 'app/actions/views/channel';
import {getDimensions} from 'app/selectors/device';
@@ -36,8 +35,6 @@ function mapDispatchToProps(dispatch) {
patchChannel,
getChannel,
setChannelDisplayName,
- setButtons,
- popTopScreen,
}, dispatch),
};
}
diff --git a/app/screens/edit_post/edit_post.js b/app/screens/edit_post/edit_post.js
index acfd7a155..686fb8885 100644
--- a/app/screens/edit_post/edit_post.js
+++ b/app/screens/edit_post/edit_post.js
@@ -9,10 +9,13 @@ import {
} from 'react-native';
import {Navigation} from 'react-native-navigation';
+import {RequestStatus} from 'mattermost-redux/constants';
+
import ErrorText from 'app/components/error_text';
import Loading from 'app/components/loading';
import StatusBar from 'app/components/status_bar';
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
+import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import {
changeOpacity,
makeStyleSheetFromTheme,
@@ -20,16 +23,12 @@ import {
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
-import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
-
-import {RequestStatus} from 'mattermost-redux/constants';
+import {dismissModal, setButtons} from 'app/actions/navigation';
export default class EditPost extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
editPost: PropTypes.func.isRequired,
- setButtons: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
}),
componentId: PropTypes.string,
closeButton: PropTypes.object,
@@ -61,7 +60,7 @@ export default class EditPost extends PureComponent {
this.rightButton.color = props.theme.sidebarHeaderTextColor;
this.rightButton.text = context.intl.formatMessage({id: 'edit_post.save', defaultMessage: 'Save'});
- props.actions.setButtons(props.componentId, {
+ setButtons(props.componentId, {
leftButtons: [{...this.leftButton, icon: props.closeButton}],
rightButtons: [this.rightButton],
});
@@ -111,20 +110,20 @@ export default class EditPost extends PureComponent {
}
close = () => {
- this.props.actions.dismissModal();
+ dismissModal();
};
emitCanEditPost = (enabled) => {
- const {actions, componentId} = this.props;
- actions.setButtons(componentId, {
+ const {componentId} = this.props;
+ setButtons(componentId, {
leftButtons: [{...this.leftButton, icon: this.props.closeButton}],
rightButtons: [{...this.rightButton, enabled}],
});
};
emitEditing = (loading) => {
- const {actions, componentId} = this.props;
- actions.setButtons(componentId, {
+ const {componentId} = this.props;
+ setButtons(componentId, {
leftButtons: [{...this.leftButton, icon: this.props.closeButton}],
rightButtons: [{...this.rightButton, enabled: !loading}],
});
diff --git a/app/screens/edit_post/index.js b/app/screens/edit_post/index.js
index 7a14d69e0..877c089ba 100644
--- a/app/screens/edit_post/index.js
+++ b/app/screens/edit_post/index.js
@@ -7,8 +7,6 @@ import {connect} from 'react-redux';
import {editPost} from 'mattermost-redux/actions/posts';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {setButtons, dismissModal} from 'app/actions/navigation';
-
import {getDimensions, isLandscape} from 'app/selectors/device';
import EditPost from './edit_post';
@@ -29,8 +27,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
editPost,
- setButtons,
- dismissModal,
}, dispatch),
};
}
diff --git a/app/screens/edit_profile/edit_profile.js b/app/screens/edit_profile/edit_profile.js
index 056a59075..cfd96e528 100644
--- a/app/screens/edit_profile/edit_profile.js
+++ b/app/screens/edit_profile/edit_profile.js
@@ -9,7 +9,9 @@ import RNFetchBlob from 'rn-fetch-blob';
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
import {DocumentPickerUtil} from 'react-native-document-picker';
import {Navigation} from 'react-native-navigation';
+
import {Client4} from 'mattermost-redux/client';
+import {getFormattedFileSize} from 'mattermost-redux/utils/file_utils';
import {buildFileUploadData, encodeHeaderURIStringToUTF8} from 'app/utils/file';
import {emptyFunction} from 'app/utils/general';
@@ -24,8 +26,7 @@ import StatusBar from 'app/components/status_bar/index';
import ProfilePictureButton from 'app/components/profile_picture_button';
import ProfilePicture from 'app/components/profile_picture';
import mattermostBucket from 'app/mattermost_bucket';
-
-import {getFormattedFileSize} from 'mattermost-redux/utils/file_utils';
+import {popTopScreen, dismissModal, setButtons} from 'app/actions/navigation';
const MAX_SIZE = 20 * 1024 * 1024;
export const VALID_MIME_TYPES = [
@@ -87,9 +88,6 @@ export default class EditProfile extends PureComponent {
setProfileImageUri: PropTypes.func.isRequired,
removeProfileImage: PropTypes.func.isRequired,
updateUser: PropTypes.func.isRequired,
- popTopScreen: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- setButtons: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
currentUser: PropTypes.object.isRequired,
@@ -122,7 +120,7 @@ export default class EditProfile extends PureComponent {
this.rightButton.color = props.theme.sidebarHeaderTextColor;
this.rightButton.text = context.intl.formatMessage({id: t('mobile.account.settings.save'), defaultMessage: 'Save'});
- props.actions.setButtons(props.componentId, buttons);
+ setButtons(props.componentId, buttons);
this.state = {
email,
@@ -180,21 +178,21 @@ export default class EditProfile extends PureComponent {
};
close = () => {
- const {commandType, actions} = this.props;
+ const {commandType} = this.props;
if (commandType === 'Push') {
- actions.popTopScreen();
+ popTopScreen();
} else {
- actions.dismissModal();
+ dismissModal();
}
};
emitCanUpdateAccount = (enabled) => {
- const {actions, componentId} = this.props;
+ const {componentId} = this.props;
const buttons = {
rightButtons: [{...this.rightButton, enabled}],
};
- actions.setButtons(componentId, buttons);
+ setButtons(componentId, buttons);
};
handleRequestError = (error) => {
diff --git a/app/screens/edit_profile/edit_profile.test.js b/app/screens/edit_profile/edit_profile.test.js
index 681980db0..726cd2f61 100644
--- a/app/screens/edit_profile/edit_profile.test.js
+++ b/app/screens/edit_profile/edit_profile.test.js
@@ -21,9 +21,6 @@ describe('edit_profile', () => {
updateUser: jest.fn(),
setProfileImageUri: jest.fn(),
removeProfileImage: jest.fn(),
- popTopScreen: jest.fn(),
- dismissModal: jest.fn(),
- setButtons: jest.fn(),
};
const baseProps = {
diff --git a/app/screens/edit_profile/index.js b/app/screens/edit_profile/index.js
index 70f3db182..420166ec5 100644
--- a/app/screens/edit_profile/index.js
+++ b/app/screens/edit_profile/index.js
@@ -8,7 +8,6 @@ import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers';
import {isLandscape} from 'app/selectors/device';
-import {popTopScreen, dismissModal, setButtons} from 'app/actions/navigation';
import {setProfileImageUri, removeProfileImage, updateUser} from 'app/actions/views/edit_profile';
import EditProfile from './edit_profile';
@@ -51,9 +50,6 @@ function mapDispatchToProps(dispatch) {
setProfileImageUri,
removeProfileImage,
updateUser,
- popTopScreen,
- dismissModal,
- setButtons,
}, dispatch),
};
}
diff --git a/app/screens/error_teams_list/error_teams_list.js b/app/screens/error_teams_list/error_teams_list.js
index defc11faa..43267140d 100644
--- a/app/screens/error_teams_list/error_teams_list.js
+++ b/app/screens/error_teams_list/error_teams_list.js
@@ -12,6 +12,7 @@ import {Navigation} from 'react-native-navigation';
import FailedNetworkAction from 'app/components/failed_network_action';
import Loading from 'app/components/loading';
import StatusBar from 'app/components/status_bar';
+import {resetToChannel} from 'app/actions/navigation';
import {makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
export default class ErrorTeamsList extends PureComponent {
@@ -21,7 +22,6 @@ export default class ErrorTeamsList extends PureComponent {
connection: PropTypes.func.isRequired,
logout: PropTypes.func.isRequired,
selectDefaultTeam: PropTypes.func.isRequired,
- resetToChannel: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
theme: PropTypes.object,
@@ -56,7 +56,7 @@ export default class ErrorTeamsList extends PureComponent {
const passProps = {
disableTermsModal: true,
};
- this.props.actions.resetToChannel(passProps);
+ resetToChannel(passProps);
};
getUserInfo = async () => {
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 229ec1f42..3f36a4736 100644
--- a/app/screens/error_teams_list/error_teams_list.test.js
+++ b/app/screens/error_teams_list/error_teams_list.test.js
@@ -22,7 +22,6 @@ describe('ErrorTeamsList', () => {
connection: () => {}, // eslint-disable-line no-empty-function
logout: () => {}, // eslint-disable-line no-empty-function
selectDefaultTeam: () => {}, // eslint-disable-line no-empty-function
- resetToChannel: jest.fn(),
},
componentId: 'component-id',
theme: Preferences.THEMES.default,
diff --git a/app/screens/error_teams_list/index.js b/app/screens/error_teams_list/index.js
index 981e62733..31424ed03 100644
--- a/app/screens/error_teams_list/index.js
+++ b/app/screens/error_teams_list/index.js
@@ -8,8 +8,6 @@ import {logout, loadMe} from 'mattermost-redux/actions/users';
import {connection} from 'app/actions/device';
import {selectDefaultTeam} from 'app/actions/views/select_team';
-import {resetToChannel} from 'app/actions/navigation';
-
import ErrorTeamsList from './error_teams_list.js';
function mapDispatchToProps(dispatch) {
@@ -19,7 +17,6 @@ function mapDispatchToProps(dispatch) {
selectDefaultTeam,
connection,
loadMe,
- resetToChannel,
}, dispatch),
};
}
diff --git a/app/screens/expanded_announcement_banner/expanded_announcement_banner.js b/app/screens/expanded_announcement_banner/expanded_announcement_banner.js
index 0b10ea863..54ea66cfc 100644
--- a/app/screens/expanded_announcement_banner/expanded_announcement_banner.js
+++ b/app/screens/expanded_announcement_banner/expanded_announcement_banner.js
@@ -5,17 +5,18 @@ import PropTypes from 'prop-types';
import React from 'react';
import {ScrollView, View} from 'react-native';
import Button from 'react-native-button';
+
import FormattedText from 'app/components/formatted_text';
import Markdown from 'app/components/markdown';
import SafeAreaView from 'app/components/safe_area_view';
import {getMarkdownTextStyles, getMarkdownBlockStyles} from 'app/utils/markdown';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+import {popTopScreen} from 'app/actions/navigation';
export default class ExpandedAnnouncementBanner extends React.PureComponent {
static propTypes = {
actions: PropTypes.shape({
dismissBanner: PropTypes.func.isRequired,
- popTopScreen: PropTypes.func.isRequired,
}).isRequired,
allowDismissal: PropTypes.bool.isRequired,
bannerText: PropTypes.string.isRequired,
@@ -23,7 +24,7 @@ export default class ExpandedAnnouncementBanner extends React.PureComponent {
}
close = () => {
- this.props.actions.popTopScreen();
+ popTopScreen();
};
dismissBanner = () => {
diff --git a/app/screens/expanded_announcement_banner/index.js b/app/screens/expanded_announcement_banner/index.js
index 64c8b17d0..83837c9c3 100644
--- a/app/screens/expanded_announcement_banner/index.js
+++ b/app/screens/expanded_announcement_banner/index.js
@@ -7,7 +7,6 @@ import {connect} from 'react-redux';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {popTopScreen} from 'app/actions/navigation';
import {dismissBanner} from 'app/actions/views/announcement';
import ExpandedAnnouncementBanner from './expanded_announcement_banner';
@@ -26,7 +25,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
dismissBanner,
- popTopScreen,
}, dispatch),
};
}
diff --git a/app/screens/flagged_posts/flagged_posts.js b/app/screens/flagged_posts/flagged_posts.js
index e5d757006..b648ecb86 100644
--- a/app/screens/flagged_posts/flagged_posts.js
+++ b/app/screens/flagged_posts/flagged_posts.js
@@ -24,6 +24,12 @@ import mattermostManaged from 'app/mattermost_managed';
import SearchResultPost from 'app/screens/search/search_result_post';
import ChannelDisplayName from 'app/screens/search/channel_display_name';
import {changeOpacity} from 'app/utils/theme';
+import {
+ goToScreen,
+ showModalOverCurrentContext,
+ showSearchModal,
+ dismissModal,
+} from 'app/actions/navigation';
export default class FlaggedPosts extends PureComponent {
static propTypes = {
@@ -34,9 +40,6 @@ export default class FlaggedPosts extends PureComponent {
getFlaggedPosts: PropTypes.func.isRequired,
selectFocusedPostId: PropTypes.func.isRequired,
selectPost: PropTypes.func.isRequired,
- showSearchModal: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- showModalOverCurrentContext: PropTypes.func.isRequired,
}).isRequired,
didFail: PropTypes.bool,
isLoading: PropTypes.bool,
@@ -65,7 +68,7 @@ export default class FlaggedPosts extends PureComponent {
navigationButtonPressed({buttonId}) {
if (buttonId === 'close-settings') {
- this.props.actions.dismissModal();
+ dismissModal();
}
}
@@ -83,7 +86,7 @@ export default class FlaggedPosts extends PureComponent {
Keyboard.dismiss();
actions.loadThreadIfNecessary(rootId);
actions.selectPost(rootId);
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
};
handleClosePermalink = () => {
@@ -98,11 +101,8 @@ export default class FlaggedPosts extends PureComponent {
};
handleHashtagPress = async (hashtag) => {
- const {actions} = this.props;
-
- await actions.dismissModal();
-
- actions.showSearchModal('#' + hashtag);
+ dismissModal();
+ showSearchModal('#' + hashtag);
};
keyExtractor = (item) => item;
@@ -186,7 +186,7 @@ export default class FlaggedPosts extends PureComponent {
};
this.showingPermalink = true;
- actions.showModalOverCurrentContext(screen, passProps, options);
+ showModalOverCurrentContext(screen, passProps, options);
}
};
diff --git a/app/screens/flagged_posts/index.js b/app/screens/flagged_posts/index.js
index d48a92c54..57a9337f2 100644
--- a/app/screens/flagged_posts/index.js
+++ b/app/screens/flagged_posts/index.js
@@ -9,12 +9,6 @@ import {clearSearch, getFlaggedPosts} from 'mattermost-redux/actions/search';
import {RequestStatus} from 'mattermost-redux/constants';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {
- dismissModal,
- goToScreen,
- showSearchModal,
- showModalOverCurrentContext,
-} from 'app/actions/navigation';
import {loadChannelsByTeamName, loadThreadIfNecessary} from 'app/actions/views/channel';
import {makePreparePostIdsForSearchPosts} from 'app/selectors/post_list';
@@ -46,10 +40,6 @@ function mapDispatchToProps(dispatch) {
getFlaggedPosts,
selectFocusedPostId,
selectPost,
- showSearchModal,
- dismissModal,
- goToScreen,
- showModalOverCurrentContext,
}, dispatch),
};
}
diff --git a/app/screens/image_preview/image_preview.js b/app/screens/image_preview/image_preview.js
index 1d5d1a2e2..ba719c327 100644
--- a/app/screens/image_preview/image_preview.js
+++ b/app/screens/image_preview/image_preview.js
@@ -22,7 +22,6 @@ import {intlShape} from 'react-intl';
import Permissions from 'react-native-permissions';
import Gallery from 'react-native-image-gallery';
import DeviceInfo from 'react-native-device-info';
-import {Navigation} from 'react-native-navigation';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
@@ -33,6 +32,11 @@ import {getLocalFilePathFromFile, isDocument, isVideo} from 'app/utils/file';
import {emptyFunction} from 'app/utils/general';
import {calculateDimensions} from 'app/utils/images';
import {t} from 'app/utils/i18n';
+import {
+ showModalOverCurrentContext,
+ dismissModal,
+ mergeNavigationOptions,
+} from 'app/actions/navigation';
import Downloader from './downloader';
import VideoPreview from './video_preview';
@@ -45,10 +49,6 @@ const ANIM_CONFIG = {duration: 300};
export default class ImagePreview extends PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- dismissModal: PropTypes.func.isRequired,
- showModalOverCurrentContext: PropTypes.func.isRequired,
- }).isRequired,
componentId: PropTypes.string.isRequired,
canDownloadFiles: PropTypes.bool.isRequired,
deviceHeight: PropTypes.number.isRequired,
@@ -72,11 +72,12 @@ export default class ImagePreview extends PureComponent {
constructor(props) {
super(props);
- Navigation.mergeOptions(props.componentId, {
+ const options = {
layout: {
backgroundColor: '#000',
},
- });
+ };
+ mergeNavigationOptions(props.componentId, options);
this.openAnim = new Animated.Value(0);
this.headerFooterAnim = new Animated.Value(1);
@@ -111,15 +112,16 @@ export default class ImagePreview extends PureComponent {
};
close = () => {
- const {actions, getItemMeasures, componentId} = this.props;
+ const {getItemMeasures, componentId} = this.props;
const {index} = this.state;
this.setState({animating: true});
- Navigation.mergeOptions(componentId, {
+ const options = {
layout: {
backgroundColor: 'transparent',
},
- });
+ };
+ mergeNavigationOptions(componentId, options);
getItemMeasures(index, (origin) => {
if (origin) {
@@ -127,7 +129,7 @@ export default class ImagePreview extends PureComponent {
}
this.animateOpenAnimToValue(0, () => {
- actions.dismissModal();
+ dismissModal();
});
});
};
@@ -455,7 +457,6 @@ export default class ImagePreview extends PureComponent {
};
showDownloadOptionsIOS = async () => {
- const {actions} = this.props;
const {formatMessage} = this.context.intl;
const file = this.getCurrentFile();
const items = [];
@@ -533,7 +534,7 @@ export default class ImagePreview extends PureComponent {
onCancelPress: () => this.setHeaderAndFooterVisible(true),
};
- actions.showModalOverCurrentContext(screen, passProps);
+ showModalOverCurrentContext(screen, passProps);
}
};
diff --git a/app/screens/image_preview/image_preview.test.js b/app/screens/image_preview/image_preview.test.js
index 90ac21ef9..b02aa3352 100644
--- a/app/screens/image_preview/image_preview.test.js
+++ b/app/screens/image_preview/image_preview.test.js
@@ -6,10 +6,11 @@ import {shallow} from 'enzyme';
import {
TouchableOpacity,
} from 'react-native';
-import {Navigation} from 'react-native-navigation';
import Preferences from 'mattermost-redux/constants/preferences';
+import * as NavigationActions from 'app/actions/navigation';
+
import ImagePreview from './image_preview';
jest.useFakeTimers();
@@ -19,13 +20,6 @@ jest.mock('react-native-doc-viewer', () => {
OpenFile: jest.fn(),
};
});
-jest.mock('react-native-navigation', () => ({
- Navigation: {
- mergeOptions: jest.fn(),
- },
-}));
-
-Navigation.mergeOptions = jest.fn();
describe('ImagePreview', () => {
const baseProps = {
@@ -42,10 +36,6 @@ describe('ImagePreview', () => {
target: {},
theme: Preferences.THEMES.default,
componentId: 'component-id',
- actions: {
- dismissModal: jest.fn(),
- showModalOverCurrentContext: jest.fn(),
- },
};
test('should match snapshot', () => {
@@ -94,7 +84,9 @@ describe('ImagePreview', () => {
expect(wrapper.state('index')).toEqual(1);
});
- test('should match call getItemMeasures & Navigation.mergeOptions on close', () => {
+ test('should match call getItemMeasures & mergeNavigationOptions on close', () => {
+ const mergeNavigationOptions = jest.spyOn(NavigationActions, 'mergeNavigationOptions');
+
const getItemMeasures = jest.fn();
const wrapper = shallow(
{
wrapper.instance().close();
- expect(Navigation.mergeOptions).toHaveBeenCalledTimes(2);
- expect(Navigation.mergeOptions).toHaveBeenCalledWith(
+ expect(mergeNavigationOptions).toHaveBeenCalledTimes(2);
+ expect(mergeNavigationOptions).toHaveBeenCalledWith(
baseProps.componentId,
{
layout: {
diff --git a/app/screens/image_preview/index.js b/app/screens/image_preview/index.js
index e72f70bff..c526f3475 100644
--- a/app/screens/image_preview/index.js
+++ b/app/screens/image_preview/index.js
@@ -1,13 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {canDownloadFilesOnMobile} from 'mattermost-redux/selectors/entities/general';
-import {dismissModal, showModalOverCurrentContext} from 'app/actions/navigation';
import {getDimensions} from 'app/selectors/device';
import ImagePreview from './image_preview';
@@ -20,13 +18,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- dismissModal,
- showModalOverCurrentContext,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(ImagePreview);
+export default connect(mapStateToProps)(ImagePreview);
diff --git a/app/screens/interactive_dialog/index.js b/app/screens/interactive_dialog/index.js
index 6b4fa1672..9c8bf17a7 100644
--- a/app/screens/interactive_dialog/index.js
+++ b/app/screens/interactive_dialog/index.js
@@ -7,8 +7,6 @@ import {connect} from 'react-redux';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {submitInteractiveDialog} from 'mattermost-redux/actions/integrations';
-import {dismissModal} from 'app/actions/navigation';
-
import InteractiveDialog from './interactive_dialog';
function mapStateToProps(state) {
@@ -32,7 +30,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
submitInteractiveDialog,
- dismissModal,
}, dispatch),
};
}
diff --git a/app/screens/interactive_dialog/interactive_dialog.js b/app/screens/interactive_dialog/interactive_dialog.js
index 8ac992323..b00c23dda 100644
--- a/app/screens/interactive_dialog/interactive_dialog.js
+++ b/app/screens/interactive_dialog/interactive_dialog.js
@@ -8,11 +8,12 @@ import {Navigation} from 'react-native-navigation';
import {checkDialogElementForError, checkIfErrorsMatchElements} from 'mattermost-redux/utils/integration_utils';
-import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
-
import StatusBar from 'app/components/status_bar';
import FormattedText from 'app/components/formatted_text';
+import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+import {dismissModal} from 'app/actions/navigation';
+
import DialogElement from './dialog_element.js';
import DialogIntroductionText from './dialog_introduction_text.js';
@@ -27,7 +28,6 @@ export default class InteractiveDialog extends PureComponent {
theme: PropTypes.object,
actions: PropTypes.shape({
submitInteractiveDialog: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
}).isRequired,
};
@@ -158,7 +158,7 @@ export default class InteractiveDialog extends PureComponent {
}
handleHide = () => {
- this.props.actions.dismissModal();
+ dismissModal();
}
onChange = (name, value) => {
diff --git a/app/screens/interactive_dialog/interactive_dialog.test.js b/app/screens/interactive_dialog/interactive_dialog.test.js
index c4a6085c2..dafcd2ec3 100644
--- a/app/screens/interactive_dialog/interactive_dialog.test.js
+++ b/app/screens/interactive_dialog/interactive_dialog.test.js
@@ -19,7 +19,6 @@ describe('InteractiveDialog', () => {
theme: Preferences.THEMES.default,
actions: {
submitInteractiveDialog: jest.fn(),
- dismissModal: jest.fn(),
},
componentId: 'component-id',
};
diff --git a/app/screens/login/index.js b/app/screens/login/index.js
index 077d7744b..8b1165225 100644
--- a/app/screens/login/index.js
+++ b/app/screens/login/index.js
@@ -8,7 +8,6 @@ import {login} from 'mattermost-redux/actions/users';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
import {isLandscape} from 'app/selectors/device';
-import {resetToChannel, goToScreen} from 'app/actions/navigation';
import LoginActions from 'app/actions/views/login';
import Login from './login.js';
@@ -32,8 +31,6 @@ function mapDispatchToProps(dispatch) {
actions: bindActionCreators({
...LoginActions,
login,
- resetToChannel,
- goToScreen,
}, dispatch),
};
}
diff --git a/app/screens/login/login.js b/app/screens/login/login.js
index 0ab5e4386..2b009d6e0 100644
--- a/app/screens/login/login.js
+++ b/app/screens/login/login.js
@@ -20,16 +20,18 @@ import Button from 'react-native-button';
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
import {RequestStatus} from 'mattermost-redux/constants';
+
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import ErrorText from 'app/components/error_text';
import FormattedText from 'app/components/formatted_text';
import StatusBar from 'app/components/status_bar';
-import {GlobalStyles} from 'app/styles';
+import {resetToChannel, goToScreen} from 'app/actions/navigation';
import {preventDoubleTap} from 'app/utils/tap';
import tracker from 'app/utils/time_tracker';
import {t} from 'app/utils/i18n';
import {setMfaPreflightDone, getMfaPreflightDone} from 'app/utils/security';
import {changeOpacity} from 'app/utils/theme';
+import {GlobalStyles} from 'app/styles';
import telemetry from 'app/telemetry';
@@ -43,8 +45,6 @@ export default class Login extends PureComponent {
handleSuccessfulLogin: PropTypes.func.isRequired,
scheduleExpiredNotification: PropTypes.func.isRequired,
login: PropTypes.func.isRequired,
- resetToChannel: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
}).isRequired,
theme: PropTypes.object,
config: PropTypes.object.isRequired,
@@ -92,16 +92,15 @@ export default class Login extends PureComponent {
this.scheduleSessionExpiredNotification();
- this.props.actions.resetToChannel();
+ resetToChannel();
};
goToMfa = () => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'MFA';
const title = intl.formatMessage({id: 'mobile.routes.mfa', defaultMessage: 'Multi-factor Authentication'});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
};
blur = () => {
@@ -288,12 +287,11 @@ export default class Login extends PureComponent {
};
forgotPassword = () => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'ForgotPassword';
const title = intl.formatMessage({id: 'password_form.title', defaultMessage: 'Password Reset'});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
}
render() {
diff --git a/app/screens/login/login.test.js b/app/screens/login/login.test.js
index fda355286..c99ac6b2f 100644
--- a/app/screens/login/login.test.js
+++ b/app/screens/login/login.test.js
@@ -9,6 +9,8 @@ import FormattedText from 'app/components/formatted_text';
import {shallowWithIntl} from 'test/intl-test-helper';
+import * as NavigationActions from 'app/actions/navigation';
+
import {mfaExpectedErrors} from 'app/screens/login/login';
import Login from './login';
@@ -30,8 +32,6 @@ describe('Login', () => {
handleSuccessfulLogin: jest.fn(),
scheduleExpiredNotification: jest.fn(),
login: jest.fn(),
- resetToChannel: jest.fn(),
- goToScreen: jest.fn(),
},
isLandscape: false,
};
@@ -80,6 +80,8 @@ describe('Login', () => {
});
test('should send the user to the login screen after login', (done) => {
+ const resetToChannel = jest.spyOn(NavigationActions, 'resetToChannel').mockImplementation(() => done());
+
let props = {
...baseProps,
loginRequest: {
@@ -88,13 +90,10 @@ describe('Login', () => {
};
props.actions.handleSuccessfulLogin.mockImplementation(() => Promise.resolve());
- props.actions.resetToChannel.mockImplementation(() => {
- done();
- });
const wrapper = shallowWithIntl();
- expect(props.actions.resetToChannel).not.toHaveBeenCalled();
+ expect(resetToChannel).not.toHaveBeenCalled();
props = {
...props,
@@ -104,7 +103,7 @@ describe('Login', () => {
};
wrapper.setProps(props);
- expect(props.actions.resetToChannel).not.toHaveBeenCalled();
+ expect(resetToChannel).not.toHaveBeenCalled();
props = {
...props,
@@ -118,6 +117,8 @@ describe('Login', () => {
});
test('should go to MFA screen when login response returns MFA error', () => {
+ const goToScreen = jest.spyOn(NavigationActions, 'goToScreen');
+
const mfaError = {
error: {
server_error_id: mfaExpectedErrors[0],
@@ -127,7 +128,7 @@ describe('Login', () => {
const wrapper = shallowWithIntl();
wrapper.instance().checkLoginResponse(mfaError);
- expect(baseProps.actions.goToScreen).
+ expect(goToScreen).
toHaveBeenCalledWith(
'MFA',
'Multi-factor Authentication',
@@ -135,10 +136,12 @@ describe('Login', () => {
});
test('should go to ForgotPassword screen when forgotPassword is called', () => {
+ const goToScreen = jest.spyOn(NavigationActions, 'goToScreen');
+
const wrapper = shallowWithIntl();
wrapper.instance().forgotPassword();
- expect(baseProps.actions.goToScreen).
+ expect(goToScreen).
toHaveBeenCalledWith(
'ForgotPassword',
'Password Reset',
diff --git a/app/screens/login_options/index.js b/app/screens/login_options/index.js
index d7ac6975b..f42ee03b5 100644
--- a/app/screens/login_options/index.js
+++ b/app/screens/login_options/index.js
@@ -1,13 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
-import {goToScreen} from 'app/actions/navigation';
import {isLandscape} from 'app/selectors/device';
import LoginOptions from './login_options';
@@ -22,12 +20,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- goToScreen,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(LoginOptions);
+export default connect(mapStateToProps)(LoginOptions);
diff --git a/app/screens/login_options/login_options.js b/app/screens/login_options/login_options.js
index ca078eb43..b76b849ac 100644
--- a/app/screens/login_options/login_options.js
+++ b/app/screens/login_options/login_options.js
@@ -13,22 +13,20 @@ import {
} from 'react-native';
import Button from 'react-native-button';
-import {ViewTypes} from 'app/constants';
import FormattedText from 'app/components/formatted_text';
import StatusBar from 'app/components/status_bar';
+import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import {GlobalStyles} from 'app/styles';
import {preventDoubleTap} from 'app/utils/tap';
+import {ViewTypes} from 'app/constants';
+import {goToScreen} from 'app/actions/navigation';
import LocalConfig from 'assets/config';
import gitlab from 'assets/images/gitlab.png';
import logo from 'assets/images/logo.png';
-import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
export default class LoginOptions extends PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- goToScreen: PropTypes.func.isRequired,
- }).isRequired,
config: PropTypes.object.isRequired,
license: PropTypes.object.isRequired,
isLandscape: PropTypes.bool.isRequired,
@@ -47,21 +45,19 @@ export default class LoginOptions extends PureComponent {
}
goToLogin = preventDoubleTap(() => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'Login';
const title = intl.formatMessage({id: 'mobile.routes.login', defaultMessage: 'Login'});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
});
goToSSO = (ssoType) => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'SSO';
const title = intl.formatMessage({id: 'mobile.routes.sso', defaultMessage: 'Single Sign-On'});
- actions.goToScreen(screen, title, {ssoType});
+ goToScreen(screen, title, {ssoType});
};
orientationDidChange = () => {
diff --git a/app/screens/long_post/index.js b/app/screens/long_post/index.js
index 03ea3f150..6113c3e09 100644
--- a/app/screens/long_post/index.js
+++ b/app/screens/long_post/index.js
@@ -9,7 +9,6 @@ import {makeGetChannel} from 'mattermost-redux/selectors/entities/channels';
import {getPost, makeGetReactionsForPost} from 'mattermost-redux/selectors/entities/posts';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {dismissModal, goToScreen} from 'app/actions/navigation';
import {loadThreadIfNecessary} from 'app/actions/views/channel';
import {isLandscape} from 'app/selectors/device';
import LongPost from './long_post';
@@ -39,8 +38,6 @@ function mapDispatchToProps(dispatch) {
actions: bindActionCreators({
loadThreadIfNecessary,
selectPost,
- dismissModal,
- goToScreen,
}, dispatch),
};
}
diff --git a/app/screens/long_post/long_post.js b/app/screens/long_post/long_post.js
index 9ef136f1c..ccc427a14 100644
--- a/app/screens/long_post/long_post.js
+++ b/app/screens/long_post/long_post.js
@@ -18,10 +18,11 @@ import FormattedText from 'app/components/formatted_text';
import Post from 'app/components/post';
import Reactions from 'app/components/reactions';
import SafeAreaView from 'app/components/safe_area_view';
+import {marginHorizontal as margin} from 'app/components/safe_area_view/iphone_x_spacing';
import {emptyFunction} from 'app/utils/general';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
-import {marginHorizontal as margin} from 'app/components/safe_area_view/iphone_x_spacing';
+import {goToScreen, dismissModal} from 'app/actions/navigation';
Animatable.initializeRegistryWithDefinitions({
growOut: {
@@ -45,8 +46,6 @@ export default class LongPost extends PureComponent {
actions: PropTypes.shape({
loadThreadIfNecessary: PropTypes.func.isRequired,
selectPost: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
}).isRequired,
channelName: PropTypes.string,
fileIds: PropTypes.array,
@@ -93,14 +92,13 @@ export default class LongPost extends PureComponent {
actions.loadThreadIfNecessary(rootId);
actions.selectPost(rootId);
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
});
handleClose = () => {
- const {actions} = this.props;
if (this.refs.view) {
this.refs.view.zoomOut().then(() => {
- actions.dismissModal();
+ dismissModal();
});
}
};
diff --git a/app/screens/mfa/index.js b/app/screens/mfa/index.js
index 4dea96eb2..4707c0bd2 100644
--- a/app/screens/mfa/index.js
+++ b/app/screens/mfa/index.js
@@ -6,8 +6,6 @@ import {connect} from 'react-redux';
import {login} from 'mattermost-redux/actions/users';
-import {popTopScreen} from 'app/actions/navigation';
-
import Mfa from './mfa';
function mapStateToProps(state) {
@@ -24,7 +22,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
login,
- popTopScreen,
}, dispatch),
};
}
diff --git a/app/screens/mfa/mfa.js b/app/screens/mfa/mfa.js
index f530f783b..f5a3cbdea 100644
--- a/app/screens/mfa/mfa.js
+++ b/app/screens/mfa/mfa.js
@@ -25,12 +25,12 @@ import {GlobalStyles} from 'app/styles';
import {preventDoubleTap} from 'app/utils/tap';
import {t} from 'app/utils/i18n';
import {setMfaPreflightDone} from 'app/utils/security';
+import {popTopScreen} from 'app/actions/navigation';
export default class Mfa extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
login: PropTypes.func.isRequired,
- popTopScreen: PropTypes.func.isRequired,
}).isRequired,
loginId: PropTypes.string.isRequired,
password: PropTypes.string.isRequired,
@@ -56,7 +56,7 @@ export default class Mfa extends PureComponent {
// In case the login is successful the previous scene (login) will take care of the transition
if (this.props.loginRequest.status === RequestStatus.STARTED &&
nextProps.loginRequest.status === RequestStatus.FAILURE) {
- this.props.actions.popTopScreen();
+ popTopScreen();
}
}
diff --git a/app/screens/more_channels/index.js b/app/screens/more_channels/index.js
index 2edac7278..9859af2ed 100644
--- a/app/screens/more_channels/index.js
+++ b/app/screens/more_channels/index.js
@@ -15,7 +15,6 @@ import {isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
-import {setButtons, dismissModal, goToScreen} from 'app/actions/navigation';
import {handleSelectChannel, setChannelDisplayName} from 'app/actions/views/channel';
import MoreChannels from './more_channels';
@@ -55,9 +54,6 @@ function mapDispatchToProps(dispatch) {
getChannels,
searchChannels,
setChannelDisplayName,
- setButtons,
- dismissModal,
- goToScreen,
}, dispatch),
};
}
diff --git a/app/screens/more_channels/more_channels.js b/app/screens/more_channels/more_channels.js
index e8a0b4525..8574541cf 100644
--- a/app/screens/more_channels/more_channels.js
+++ b/app/screens/more_channels/more_channels.js
@@ -10,6 +10,7 @@ import {Navigation} from 'react-native-navigation';
import {debounce} from 'mattermost-redux/actions/helpers';
import {General} from 'mattermost-redux/constants';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
+
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import CustomList from 'app/components/custom_list';
import ChannelListRow from 'app/components/custom_list/channel_list_row';
@@ -19,6 +20,7 @@ import Loading from 'app/components/loading';
import SearchBar from 'app/components/search_bar';
import StatusBar from 'app/components/status_bar';
import {alertErrorWithFallback} from 'app/utils/general';
+import {goToScreen, dismissModal, setButtons} from 'app/actions/navigation';
import {
changeOpacity,
makeStyleSheetFromTheme,
@@ -34,9 +36,6 @@ export default class MoreChannels extends PureComponent {
getChannels: PropTypes.func.isRequired,
searchChannels: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired,
- setButtons: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
canCreateChannels: PropTypes.bool.isRequired,
@@ -136,7 +135,7 @@ export default class MoreChannels extends PureComponent {
};
close = () => {
- this.props.actions.dismissModal();
+ dismissModal();
};
doGetChannels = () => {
@@ -164,7 +163,7 @@ export default class MoreChannels extends PureComponent {
getChannels = debounce(this.doGetChannels, 100);
setHeaderButtons = (createEnabled) => {
- const {actions, canCreateChannels, componentId} = this.props;
+ const {canCreateChannels, componentId} = this.props;
const buttons = {
leftButtons: [this.leftButton],
};
@@ -173,7 +172,7 @@ export default class MoreChannels extends PureComponent {
buttons.rightButtons = [{...this.rightButton, enabled: createEnabled}];
}
- actions.setButtons(componentId, buttons);
+ setButtons(componentId, buttons);
};
loadedChannels = ({data}) => {
@@ -228,7 +227,6 @@ export default class MoreChannels extends PureComponent {
};
onCreateChannel = () => {
- const {actions} = this.props;
const {formatMessage} = this.context.intl;
const screen = 'CreateChannel';
@@ -237,7 +235,7 @@ export default class MoreChannels extends PureComponent {
channelType: General.OPEN_CHANNEL,
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
};
renderLoading = () => {
diff --git a/app/screens/more_channels/more_channels.test.js b/app/screens/more_channels/more_channels.test.js
index 52086ada5..c2ebd9628 100644
--- a/app/screens/more_channels/more_channels.test.js
+++ b/app/screens/more_channels/more_channels.test.js
@@ -6,6 +6,8 @@ import {shallow} from 'enzyme';
import Preferences from 'mattermost-redux/constants/preferences';
+import * as NavigationActions from 'app/actions/navigation';
+
import MoreChannels from './more_channels.js';
jest.mock('react-intl');
@@ -17,9 +19,6 @@ describe('MoreChannels', () => {
getChannels: jest.fn().mockResolvedValue({data: [{id: 'id2', name: 'name2', display_name: 'display_name2'}]}),
searchChannels: jest.fn(),
setChannelDisplayName: jest.fn(),
- setButtons: jest.fn(),
- dismissModal: jest.fn(),
- goToScreen: jest.fn(),
};
const baseProps = {
@@ -43,25 +42,29 @@ describe('MoreChannels', () => {
expect(wrapper.getElement()).toMatchSnapshot();
});
- test('should call props.actions.dismissModal on close', () => {
+ test('should call dismissModal on close', () => {
+ const dismissModal = jest.spyOn(NavigationActions, 'dismissModal');
+
const wrapper = shallow(
,
{context: {intl: {formatMessage: jest.fn()}}},
);
wrapper.instance().close();
- expect(baseProps.actions.dismissModal).toHaveBeenCalledTimes(1);
+ expect(dismissModal).toHaveBeenCalledTimes(1);
});
- test('should call props.actions.setButtons on setHeaderButtons', () => {
+ test('should call setButtons on setHeaderButtons', () => {
+ const setButtons = jest.spyOn(NavigationActions, 'setButtons');
+
const wrapper = shallow(
,
{context: {intl: {formatMessage: jest.fn()}}},
);
- expect(baseProps.actions.setButtons).toHaveBeenCalledTimes(1);
+ expect(setButtons).toHaveBeenCalledTimes(1);
wrapper.instance().setHeaderButtons(true);
- expect(baseProps.actions.setButtons).toHaveBeenCalledTimes(2);
+ expect(setButtons).toHaveBeenCalledTimes(2);
});
test('should match return value of filterChannels', () => {
diff --git a/app/screens/more_dms/index.js b/app/screens/more_dms/index.js
index dc0f17866..d1a7760d9 100644
--- a/app/screens/more_dms/index.js
+++ b/app/screens/more_dms/index.js
@@ -14,8 +14,6 @@ import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selector
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentUserId, getUsers} from 'mattermost-redux/selectors/entities/users';
-import {dismissModal, setButtons} from 'app/actions/navigation';
-
import MoreDirectMessages from './more_dms';
function mapStateToProps(state) {
@@ -43,8 +41,6 @@ function mapDispatchToProps(dispatch) {
getProfilesInTeam,
searchProfiles,
setChannelDisplayName,
- dismissModal,
- setButtons,
}, dispatch),
};
}
diff --git a/app/screens/more_dms/more_dms.js b/app/screens/more_dms/more_dms.js
index 26c50f90a..c7489bfc1 100644
--- a/app/screens/more_dms/more_dms.js
+++ b/app/screens/more_dms/more_dms.js
@@ -12,8 +12,8 @@ import {General} from 'mattermost-redux/constants';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {getGroupDisplayNameFromUserIds} from 'mattermost-redux/utils/channel_utils';
import {displayUsername, filterProfilesMatchingTerm} from 'mattermost-redux/utils/user_utils';
-import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
+import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import CustomList, {FLATLIST, SECTIONLIST} from 'app/components/custom_list';
import UserListRow from 'app/components/custom_list/user_list_row';
import FormattedText from 'app/components/formatted_text';
@@ -30,6 +30,7 @@ import {
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
+import {dismissModal, setButtons} from 'app/actions/navigation';
import SelectedUsers from './selected_users';
@@ -45,8 +46,6 @@ export default class MoreDirectMessages extends PureComponent {
getProfilesInTeam: PropTypes.func.isRequired,
searchProfiles: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- setButtons: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
allProfiles: PropTypes.object.isRequired,
@@ -115,7 +114,7 @@ export default class MoreDirectMessages extends PureComponent {
}
close = () => {
- this.props.actions.dismissModal();
+ dismissModal();
};
clearSearch = () => {
@@ -332,9 +331,9 @@ export default class MoreDirectMessages extends PureComponent {
};
updateNavigationButtons = (startEnabled, context = this.context) => {
- const {actions, componentId, theme} = this.props;
+ const {componentId, theme} = this.props;
const {formatMessage} = context.intl;
- actions.setButtons(componentId, {
+ setButtons(componentId, {
rightButtons: [{
color: theme.sidebarHeaderTextColor,
id: START_BUTTON,
diff --git a/app/screens/notification/index.js b/app/screens/notification/index.js
index 47d22b173..7674cf74a 100644
--- a/app/screens/notification/index.js
+++ b/app/screens/notification/index.js
@@ -12,8 +12,6 @@ import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selector
import {getUser} from 'mattermost-redux/selectors/entities/users';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
-import {dismissOverlay, dismissAllModals, popToRoot} from 'app/actions/navigation';
-
import Notification from './notification';
function mapStateToProps(state, ownProps) {
@@ -44,9 +42,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
loadFromPushNotification,
- dismissOverlay,
- dismissAllModals,
- popToRoot,
}, dispatch),
};
}
diff --git a/app/screens/notification/notification.js b/app/screens/notification/notification.js
index 4ae79b031..00c504b64 100644
--- a/app/screens/notification/notification.js
+++ b/app/screens/notification/notification.js
@@ -25,6 +25,7 @@ import FormattedText from 'app/components/formatted_text';
import ProfilePicture from 'app/components/profile_picture';
import {changeOpacity} from 'app/utils/theme';
import {NavigationTypes} from 'app/constants';
+import {popToRoot, dismissAllModals, dismissOverlay} from 'app/actions/navigation';
import logo from 'assets/images/icon.png';
import webhookIcon from 'assets/images/icons/webhook.jpg';
@@ -36,9 +37,6 @@ export default class Notification extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
loadFromPushNotification: PropTypes.func.isRequired,
- dismissOverlay: PropTypes.func.isRequired,
- dismissAllModals: PropTypes.func.isRequired,
- popToRoot: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string.isRequired,
channel: PropTypes.object,
@@ -91,11 +89,10 @@ export default class Notification extends PureComponent {
}
setDidDisappearListener = () => {
- this.didDismissListener = Navigation.events().registerComponentDidDisappearListener(({componentId}) => {
+ this.didDismissListener = Navigation.events().registerComponentDidDisappearListener(async ({componentId}) => {
if (componentId === this.props.componentId && this.tapped) {
- const {actions} = this.props;
- actions.dismissAllModals();
- actions.popToRoot();
+ await dismissAllModals();
+ await popToRoot();
}
});
}
@@ -120,8 +117,8 @@ export default class Notification extends PureComponent {
dismissOverlay = () => {
this.clearDismissTimer();
- const {actions, componentId} = this.props;
- actions.dismissOverlay(componentId);
+ const {componentId} = this.props;
+ dismissOverlay(componentId);
}
animateDismissOverlay = () => {
diff --git a/app/screens/options_modal/index.js b/app/screens/options_modal/index.js
index 5ba6a8b79..acecbd45a 100644
--- a/app/screens/options_modal/index.js
+++ b/app/screens/options_modal/index.js
@@ -1,11 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
-import {dismissModal} from 'app/actions/navigation';
-
import {getDimensions, isLandscape} from 'app/selectors/device';
import OptionsModal from './options_modal';
@@ -17,12 +14,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- dismissModal,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(OptionsModal);
+export default connect(mapStateToProps)(OptionsModal);
diff --git a/app/screens/options_modal/options_modal.js b/app/screens/options_modal/options_modal.js
index b1935b768..22880bf4d 100644
--- a/app/screens/options_modal/options_modal.js
+++ b/app/screens/options_modal/options_modal.js
@@ -15,6 +15,7 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {NavigationTypes} from 'app/constants';
import {emptyFunction} from 'app/utils/general';
+import {dismissModal} from 'app/actions/navigation';
import OptionsModalList from './options_modal_list';
@@ -23,9 +24,6 @@ const DURATION = 200;
export default class OptionsModal extends PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- dismissModal: PropTypes.func.isRequired,
- }).isRequired,
items: PropTypes.array.isRequired,
deviceHeight: PropTypes.number.isRequired,
deviceWidth: PropTypes.number.isRequired,
@@ -71,7 +69,7 @@ export default class OptionsModal extends PureComponent {
toValue: this.props.deviceHeight,
duration: DURATION,
}).start(() => {
- this.props.actions.dismissModal();
+ dismissModal();
});
};
@@ -79,7 +77,7 @@ export default class OptionsModal extends PureComponent {
if (Platform.OS === 'android') {
this.close();
} else {
- this.props.actions.dismissModal();
+ dismissModal();
}
};
diff --git a/app/screens/permalink/index.js b/app/screens/permalink/index.js
index df9453faf..d97525ec9 100644
--- a/app/screens/permalink/index.js
+++ b/app/screens/permalink/index.js
@@ -17,12 +17,6 @@ import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import {isLandscape} from 'app/selectors/device';
-import {
- goToScreen,
- dismissModal,
- dismissAllModals,
- resetToChannel,
-} from 'app/actions/navigation';
import {
handleSelectChannel,
loadThreadIfNecessary,
@@ -81,10 +75,6 @@ function mapDispatchToProps(dispatch) {
selectPost,
setChannelDisplayName,
setChannelLoading,
- goToScreen,
- dismissModal,
- dismissAllModals,
- resetToChannel,
}, dispatch),
};
}
diff --git a/app/screens/permalink/permalink.js b/app/screens/permalink/permalink.js
index 3520f67eb..7d6bb3aeb 100644
--- a/app/screens/permalink/permalink.js
+++ b/app/screens/permalink/permalink.js
@@ -24,9 +24,18 @@ import Loading from 'app/components/loading';
import PostList from 'app/components/post_list';
import PostListRetry from 'app/components/post_list_retry';
import SafeAreaView from 'app/components/safe_area_view';
+import {marginHorizontal as margin} from 'app/components/safe_area_view/iphone_x_spacing';
+
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
-import {marginHorizontal as margin} from 'app/components/safe_area_view/iphone_x_spacing';
+
+import {
+ resetToChannel,
+ goToScreen,
+ dismissModal,
+ dismissAllModals,
+ popToRoot,
+} from 'app/actions/navigation';
Animatable.initializeRegistryWithDefinitions({
growOut: {
@@ -58,10 +67,6 @@ export default class Permalink extends PureComponent {
selectPost: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired,
setChannelLoading: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- dismissAllModals: PropTypes.func.isRequired,
- resetToChannel: PropTypes.func.isRequired,
}).isRequired,
channelId: PropTypes.string,
channelIsArchived: PropTypes.bool,
@@ -189,7 +194,7 @@ export default class Permalink extends PureComponent {
actions.loadThreadIfNecessary(rootId);
actions.selectPost(rootId);
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
});
handleClose = () => {
@@ -198,7 +203,7 @@ export default class Permalink extends PureComponent {
this.mounted = false;
this.refs.view.zoomOut().then(() => {
actions.selectPost('');
- actions.dismissModal();
+ dismissModal();
if (onClose) {
onClose();
@@ -225,7 +230,7 @@ export default class Permalink extends PureComponent {
}
};
- jumpToChannel = (channelId, channelDisplayName) => {
+ jumpToChannel = async (channelId, channelDisplayName) => {
if (channelId) {
const {actions, channelTeamId, currentTeamId, onClose} = this.props;
const currentChannelId = this.props.channelId;
@@ -238,17 +243,18 @@ export default class Permalink extends PureComponent {
actions.selectPost('');
+ await dismissAllModals();
+ await popToRoot();
+
if (channelId === currentChannelId) {
EventEmitter.emit('reset_channel');
} else {
const passProps = {
disableTermsModal: true,
};
- actions.resetToChannel(passProps);
+ resetToChannel(passProps);
}
- actions.dismissAllModals();
-
if (onClose) {
onClose();
}
diff --git a/app/screens/permalink/permalink.test.js b/app/screens/permalink/permalink.test.js
index ff7654cbc..0d8edbe6b 100644
--- a/app/screens/permalink/permalink.test.js
+++ b/app/screens/permalink/permalink.test.js
@@ -22,10 +22,6 @@ describe('Permalink', () => {
selectPost: jest.fn(),
setChannelDisplayName: jest.fn(),
setChannelLoading: jest.fn(),
- goToScreen: jest.fn(),
- dismissModal: jest.fn(),
- dismissAllModals: jest.fn(),
- resetToChannel: jest.fn(),
};
const baseProps = {
diff --git a/app/screens/pinned_posts/index.js b/app/screens/pinned_posts/index.js
index 30f18d440..4cfb165c6 100644
--- a/app/screens/pinned_posts/index.js
+++ b/app/screens/pinned_posts/index.js
@@ -9,12 +9,6 @@ import {clearSearch, getPinnedPosts} from 'mattermost-redux/actions/search';
import {RequestStatus} from 'mattermost-redux/constants';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {
- dismissModal,
- goToScreen,
- showSearchModal,
- showModalOverCurrentContext,
-} from 'app/actions/navigation';
import {loadChannelsByTeamName, loadThreadIfNecessary} from 'app/actions/views/channel';
import {makePreparePostIdsForSearchPosts} from 'app/selectors/post_list';
@@ -48,10 +42,6 @@ function mapDispatchToProps(dispatch) {
getPinnedPosts,
selectFocusedPostId,
selectPost,
- dismissModal,
- goToScreen,
- showSearchModal,
- showModalOverCurrentContext,
}, dispatch),
};
}
diff --git a/app/screens/pinned_posts/pinned_posts.js b/app/screens/pinned_posts/pinned_posts.js
index 8197e1a6e..7aa59937e 100644
--- a/app/screens/pinned_posts/pinned_posts.js
+++ b/app/screens/pinned_posts/pinned_posts.js
@@ -23,6 +23,13 @@ import StatusBar from 'app/components/status_bar';
import mattermostManaged from 'app/mattermost_managed';
import SearchResultPost from 'app/screens/search/search_result_post';
import {changeOpacity} from 'app/utils/theme';
+import {
+ goToScreen,
+ showModalOverCurrentContext,
+ showSearchModal,
+ dismissModal,
+} from 'app/actions/navigation';
+
import noResultsImage from 'assets/images/no_results/pin.png';
export default class PinnedPosts extends PureComponent {
@@ -34,10 +41,6 @@ export default class PinnedPosts extends PureComponent {
getPinnedPosts: PropTypes.func.isRequired,
selectFocusedPostId: PropTypes.func.isRequired,
selectPost: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
- showSearchModal: PropTypes.func.isRequired,
- showModalOverCurrentContext: PropTypes.func.isRequired,
}).isRequired,
currentChannelId: PropTypes.string.isRequired,
didFail: PropTypes.bool,
@@ -64,7 +67,7 @@ export default class PinnedPosts extends PureComponent {
navigationButtonPressed({buttonId}) {
if (buttonId === 'close-settings') {
- this.props.actions.dismissModal();
+ dismissModal();
}
}
@@ -81,7 +84,7 @@ export default class PinnedPosts extends PureComponent {
Keyboard.dismiss();
actions.loadThreadIfNecessary(rootId);
actions.selectPost(rootId);
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
};
handleClosePermalink = () => {
@@ -96,11 +99,8 @@ export default class PinnedPosts extends PureComponent {
};
handleHashtagPress = async (hashtag) => {
- const {actions} = this.props;
-
- await actions.dismissModal();
-
- actions.showSearchModal('#' + hashtag);
+ dismissModal();
+ showSearchModal('#' + hashtag);
};
keyExtractor = (item) => item;
@@ -182,7 +182,7 @@ export default class PinnedPosts extends PureComponent {
};
this.showingPermalink = true;
- actions.showModalOverCurrentContext(screen, passProps, options);
+ showModalOverCurrentContext(screen, passProps, options);
}
};
diff --git a/app/screens/post_options/index.js b/app/screens/post_options/index.js
index cbfa74ec5..f326b45e5 100644
--- a/app/screens/post_options/index.js
+++ b/app/screens/post_options/index.js
@@ -22,7 +22,6 @@ import {getCurrentTeamId, getCurrentTeamUrl} from 'mattermost-redux/selectors/en
import {canEditPost} from 'mattermost-redux/utils/post_utils';
import {THREAD} from 'app/constants/screen';
-import {dismissModal, showModal} from 'app/actions/navigation';
import {addReaction} from 'app/actions/views/emoji';
import {getDimensions, isLandscape} from 'app/selectors/device';
@@ -123,8 +122,6 @@ function mapDispatchToProps(dispatch) {
removePost,
unflagPost,
unpinPost,
- dismissModal,
- showModal,
}, dispatch),
};
}
diff --git a/app/screens/post_options/post_options.js b/app/screens/post_options/post_options.js
index 3d623bc32..0a62175bf 100644
--- a/app/screens/post_options/post_options.js
+++ b/app/screens/post_options/post_options.js
@@ -12,6 +12,7 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter';
import SlideUpPanel from 'app/components/slide_up_panel';
import {BOTTOM_MARGIN} from 'app/components/slide_up_panel/slide_up_panel';
import {t} from 'app/utils/i18n';
+import {showModal, dismissModal} from 'app/actions/navigation';
import {OPTION_HEIGHT, getInitialPosition} from './post_options_utils';
import PostOption from './post_option';
@@ -26,8 +27,6 @@ export default class PostOptions extends PureComponent {
removePost: PropTypes.func.isRequired,
unflagPost: PropTypes.func.isRequired,
unpinPost: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- showModal: PropTypes.func.isRequired,
}).isRequired,
canAddReaction: PropTypes.bool,
canReply: PropTypes.bool,
@@ -52,7 +51,7 @@ export default class PostOptions extends PureComponent {
};
close = async (cb) => {
- await this.props.actions.dismissModal();
+ dismissModal();
if (typeof cb === 'function') {
setTimeout(cb, 300);
@@ -262,7 +261,7 @@ export default class PostOptions extends PureComponent {
};
handleAddReaction = () => {
- const {actions, theme} = this.props;
+ const {theme} = this.props;
const {formatMessage} = this.context.intl;
this.close(() => {
@@ -274,7 +273,7 @@ export default class PostOptions extends PureComponent {
onEmojiPress: this.handleAddReactionToPost,
};
- actions.showModal(screen, title, passProps);
+ showModal(screen, title, passProps);
});
});
};
@@ -352,7 +351,7 @@ export default class PostOptions extends PureComponent {
};
handlePostEdit = () => {
- const {actions, theme, post} = this.props;
+ const {theme, post} = this.props;
const {intl} = this.context;
this.close(() => {
@@ -364,7 +363,7 @@ export default class PostOptions extends PureComponent {
closeButton: source,
};
- actions.showModal(screen, title, passProps);
+ showModal(screen, title, passProps);
});
});
};
diff --git a/app/screens/post_options/post_options.test.js b/app/screens/post_options/post_options.test.js
index 56c380d7c..6be5ab8e1 100644
--- a/app/screens/post_options/post_options.test.js
+++ b/app/screens/post_options/post_options.test.js
@@ -26,8 +26,6 @@ describe('PostOptions', () => {
removePost: jest.fn(),
unflagPost: jest.fn(),
unpinPost: jest.fn(),
- dismissModal: jest.fn(),
- showModal: jest.fn(),
};
const post = {
diff --git a/app/screens/reaction_list/index.js b/app/screens/reaction_list/index.js
index 70fc1b742..337daf32f 100644
--- a/app/screens/reaction_list/index.js
+++ b/app/screens/reaction_list/index.js
@@ -9,7 +9,6 @@ import {makeGetReactionsForPost} from 'mattermost-redux/selectors/entities/posts
import {getCurrentUserId, makeGetProfilesByIdsAndUsernames} from 'mattermost-redux/selectors/entities/users';
import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {dismissModal} from 'app/actions/navigation';
import {isLandscape} from 'app/selectors/device';
import {getUniqueUserIds} from 'app/utils/reaction';
@@ -38,7 +37,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
getMissingProfilesByIds,
- dismissModal,
}, dispatch),
};
}
diff --git a/app/screens/reaction_list/reaction_list.js b/app/screens/reaction_list/reaction_list.js
index 0be0a36d0..726b86e34 100644
--- a/app/screens/reaction_list/reaction_list.js
+++ b/app/screens/reaction_list/reaction_list.js
@@ -18,6 +18,7 @@ import {
getUniqueUserIds,
sortReactions,
} from 'app/utils/reaction';
+import {dismissModal} from 'app/actions/navigation';
import ReactionHeader from './reaction_header';
import ReactionRow from './reaction_row';
@@ -28,7 +29,6 @@ export default class ReactionList extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
getMissingProfilesByIds: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
}).isRequired,
reactions: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
@@ -108,7 +108,7 @@ export default class ReactionList extends PureComponent {
}
close = () => {
- this.props.actions.dismissModal();
+ dismissModal();
};
getMissingProfiles = () => {
diff --git a/app/screens/reaction_list/reaction_list.test.js b/app/screens/reaction_list/reaction_list.test.js
index 12626a479..d9c297302 100644
--- a/app/screens/reaction_list/reaction_list.test.js
+++ b/app/screens/reaction_list/reaction_list.test.js
@@ -15,7 +15,6 @@ describe('ReactionList', () => {
const baseProps = {
actions: {
getMissingProfilesByIds: jest.fn(),
- dismissModal: jest.fn(),
},
allUserIds: ['user_id_1', 'user_id_2'],
reactions: {'user_id_1-smile': {emoji_name: 'smile', user_id: 'user_id_1'}, 'user_id_2-+1': {emoji_name: '+1', user_id: 'user_id_2'}},
diff --git a/app/screens/reaction_list/reaction_row/index.js b/app/screens/reaction_list/reaction_row/index.js
index 9da44288b..e07be3c2c 100644
--- a/app/screens/reaction_list/reaction_row/index.js
+++ b/app/screens/reaction_list/reaction_row/index.js
@@ -1,10 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
-import {goToScreen} from 'app/actions/navigation';
import {isLandscape} from 'app/selectors/device';
import ReactionRow from './reaction_row';
@@ -14,12 +12,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- goToScreen,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(ReactionRow);
+export default connect(mapStateToProps)(ReactionRow);
diff --git a/app/screens/reaction_list/reaction_row/reaction_row.js b/app/screens/reaction_list/reaction_row/reaction_row.js
index 812f2be03..458fa418c 100644
--- a/app/screens/reaction_list/reaction_row/reaction_row.js
+++ b/app/screens/reaction_list/reaction_row/reaction_row.js
@@ -13,17 +13,15 @@ import {
import {displayUsername} from 'mattermost-redux/utils/user_utils';
import ProfilePicture from 'app/components/profile_picture';
+import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
-import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
+import {goToScreen} from 'app/actions/navigation';
import Emoji from 'app/components/emoji';
export default class ReactionRow extends React.PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- goToScreen: PropTypes.func.isRequired,
- }).isRequired,
emojiName: PropTypes.string.isRequired,
teammateNameDisplay: PropTypes.string.isRequired,
theme: PropTypes.object.isRequired,
@@ -40,7 +38,7 @@ export default class ReactionRow extends React.PureComponent {
};
goToUserProfile = () => {
- const {actions, user} = this.props;
+ const {user} = this.props;
const {formatMessage} = this.context.intl;
const screen = 'UserProfile';
const title = formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
@@ -48,7 +46,7 @@ export default class ReactionRow extends React.PureComponent {
userId: user.id,
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
};
render() {
diff --git a/app/screens/reaction_list/reaction_row/reaction_row.test.js b/app/screens/reaction_list/reaction_row/reaction_row.test.js
index 9f65bf3bb..78d9d19f0 100644
--- a/app/screens/reaction_list/reaction_row/reaction_row.test.js
+++ b/app/screens/reaction_list/reaction_row/reaction_row.test.js
@@ -9,9 +9,6 @@ import ReactionRow from './reaction_row';
describe('ReactionRow', () => {
const baseProps = {
- actions: {
- goToScreen: jest.fn(),
- },
emojiName: 'smile',
teammateNameDisplay: 'username',
theme: Preferences.THEMES.default,
diff --git a/app/screens/recent_mentions/index.js b/app/screens/recent_mentions/index.js
index 7ee56b2ef..83198213c 100644
--- a/app/screens/recent_mentions/index.js
+++ b/app/screens/recent_mentions/index.js
@@ -9,12 +9,6 @@ import {clearSearch, getRecentMentions} from 'mattermost-redux/actions/search';
import {RequestStatus} from 'mattermost-redux/constants';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {
- dismissModal,
- goToScreen,
- showSearchModal,
- showModalOverCurrentContext,
-} from 'app/actions/navigation';
import {loadChannelsByTeamName, loadThreadIfNecessary} from 'app/actions/views/channel';
import {makePreparePostIdsForSearchPosts} from 'app/selectors/post_list';
@@ -46,10 +40,6 @@ function mapDispatchToProps(dispatch) {
getRecentMentions,
selectFocusedPostId,
selectPost,
- showSearchModal,
- dismissModal,
- goToScreen,
- showModalOverCurrentContext,
}, dispatch),
};
}
diff --git a/app/screens/recent_mentions/recent_mentions.js b/app/screens/recent_mentions/recent_mentions.js
index 226d95c64..b146861d2 100644
--- a/app/screens/recent_mentions/recent_mentions.js
+++ b/app/screens/recent_mentions/recent_mentions.js
@@ -24,6 +24,12 @@ import mattermostManaged from 'app/mattermost_managed';
import SearchResultPost from 'app/screens/search/search_result_post';
import ChannelDisplayName from 'app/screens/search/channel_display_name';
import {changeOpacity} from 'app/utils/theme';
+import {
+ goToScreen,
+ showModalOverCurrentContext,
+ showSearchModal,
+ dismissModal,
+} from 'app/actions/navigation';
export default class RecentMentions extends PureComponent {
static propTypes = {
@@ -34,9 +40,6 @@ export default class RecentMentions extends PureComponent {
getRecentMentions: PropTypes.func.isRequired,
selectFocusedPostId: PropTypes.func.isRequired,
selectPost: PropTypes.func.isRequired,
- showSearchModal: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- showModalOverCurrentContext: PropTypes.func.isRequired,
}).isRequired,
didFail: PropTypes.bool,
isLoading: PropTypes.bool,
@@ -77,7 +80,7 @@ export default class RecentMentions extends PureComponent {
Keyboard.dismiss();
actions.loadThreadIfNecessary(rootId);
actions.selectPost(rootId);
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
};
handleClosePermalink = () => {
@@ -92,18 +95,15 @@ export default class RecentMentions extends PureComponent {
};
handleHashtagPress = async (hashtag) => {
- const {actions} = this.props;
-
- await actions.dismissModal();
-
- actions.showSearchModal('#' + hashtag);
+ dismissModal();
+ showSearchModal('#' + hashtag);
};
keyExtractor = (item) => item;
navigationButtonPressed({buttonId}) {
if (buttonId === 'close-settings') {
- this.props.actions.dismissModal();
+ dismissModal();
}
}
@@ -184,7 +184,7 @@ export default class RecentMentions extends PureComponent {
};
this.showingPermalink = true;
- actions.showModalOverCurrentContext(screen, passProps, options);
+ showModalOverCurrentContext(screen, passProps, options);
}
};
diff --git a/app/screens/search/index.js b/app/screens/search/index.js
index 7f6918eae..b727f547b 100644
--- a/app/screens/search/index.js
+++ b/app/screens/search/index.js
@@ -15,7 +15,6 @@ import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers';
import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils';
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
-import {dismissModal, goToScreen, showModalOverCurrentContext} from 'app/actions/navigation';
import {loadChannelsByTeamName, loadThreadIfNecessary} from 'app/actions/views/channel';
import {handleSearchDraftChanged} from 'app/actions/views/search';
import {isLandscape} from 'app/selectors/device';
@@ -85,9 +84,6 @@ function mapDispatchToProps(dispatch) {
searchPostsWithParams,
getMorePostsForSearch,
selectPost,
- dismissModal,
- goToScreen,
- showModalOverCurrentContext,
}, dispatch),
};
}
diff --git a/app/screens/search/search.js b/app/screens/search/search.js
index e9b0983c7..91b566181 100644
--- a/app/screens/search/search.js
+++ b/app/screens/search/search.js
@@ -28,15 +28,16 @@ import PostSeparator from 'app/components/post_separator';
import SafeAreaView from 'app/components/safe_area_view';
import SearchBar from 'app/components/search_bar';
import StatusBar from 'app/components/status_bar';
+import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import {DeviceTypes, ListTypes} from 'app/constants';
import mattermostManaged from 'app/mattermost_managed';
import {preventDoubleTap} from 'app/utils/tap';
+import {goToScreen, showModalOverCurrentContext, dismissModal} from 'app/actions/navigation';
import {
changeOpacity,
makeStyleSheetFromTheme,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
-import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import ChannelDisplayName from './channel_display_name';
import Modifier, {MODIFIER_LABEL_HEIGHT} from './modifier';
@@ -61,9 +62,6 @@ export default class Search extends PureComponent {
getMorePostsForSearch: PropTypes.func.isRequired,
selectFocusedPostId: PropTypes.func.isRequired,
selectPost: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
- showModalOverCurrentContext: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string.isRequired,
currentTeamId: PropTypes.string.isRequired,
@@ -154,7 +152,7 @@ export default class Search extends PureComponent {
if (this.state.preview) {
this.refs.preview.handleClose();
} else {
- this.props.actions.dismissModal();
+ dismissModal();
}
}
}
@@ -185,7 +183,7 @@ export default class Search extends PureComponent {
cancelSearch = preventDoubleTap(() => {
this.handleTextChanged('', true);
- this.props.actions.dismissModal();
+ dismissModal();
});
goToThread = (post) => {
@@ -204,12 +202,12 @@ export default class Search extends PureComponent {
rootId,
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
};
handleHashtagPress = (hashtag) => {
if (this.showingPermalink) {
- this.props.actions.dismissModal();
+ dismissModal();
this.handleClosePermalink();
}
@@ -464,7 +462,7 @@ export default class Search extends PureComponent {
};
this.showingPermalink = true;
- actions.showModalOverCurrentContext(screen, passProps, options);
+ showModalOverCurrentContext(screen, passProps, options);
}
};
diff --git a/app/screens/select_server/index.js b/app/screens/select_server/index.js
index 8bc27e0cf..1f9195c4f 100644
--- a/app/screens/select_server/index.js
+++ b/app/screens/select_server/index.js
@@ -8,7 +8,6 @@ import {getPing, resetPing, setServerVersion} from 'mattermost-redux/actions/gen
import {login} from 'mattermost-redux/actions/users';
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
-import {resetToChannel, goToScreen} from 'app/actions/navigation';
import {setLastUpgradeCheck} from 'app/actions/views/client_upgrade';
import {handleSuccessfulLogin, scheduleExpiredNotification} from 'app/actions/views/login';
import {loadConfigAndLicense} from 'app/actions/views/root';
@@ -45,8 +44,6 @@ function mapDispatchToProps(dispatch) {
resetPing,
setLastUpgradeCheck,
setServerVersion,
- resetToChannel,
- goToScreen,
}, dispatch),
};
}
diff --git a/app/screens/select_server/select_server.js b/app/screens/select_server/select_server.js
index 528b8b0e6..dd63b80cf 100644
--- a/app/screens/select_server/select_server.js
+++ b/app/screens/select_server/select_server.js
@@ -21,7 +21,6 @@ import {
} from 'react-native';
import Button from 'react-native-button';
import RNFetchBlob from 'rn-fetch-blob';
-import SafeAreaView from 'app/components/safe_area_view';
import merge from 'deepmerge';
@@ -29,6 +28,7 @@ import {Client4} from 'mattermost-redux/client';
import ErrorText from 'app/components/error_text';
import FormattedText from 'app/components/formatted_text';
+import SafeAreaView from 'app/components/safe_area_view';
import fetchConfig from 'app/init/fetch';
import mattermostBucket from 'app/mattermost_bucket';
import {GlobalStyles} from 'app/styles';
@@ -38,6 +38,7 @@ import {preventDoubleTap} from 'app/utils/tap';
import tracker from 'app/utils/time_tracker';
import {t} from 'app/utils/i18n';
import {changeOpacity} from 'app/utils/theme';
+import {resetToChannel, goToScreen} from 'app/actions/navigation';
import telemetry from 'app/telemetry';
@@ -53,7 +54,6 @@ export default class SelectServer extends PureComponent {
loadConfigAndLicense: PropTypes.func.isRequired,
login: PropTypes.func.isRequired,
resetPing: PropTypes.func.isRequired,
- resetToChannel: PropTypes.func.isRequired,
setLastUpgradeCheck: PropTypes.func.isRequired,
setServerVersion: PropTypes.func.isRequired,
}).isRequired,
@@ -159,7 +159,6 @@ export default class SelectServer extends PureComponent {
};
goToNextScreen = (screen, title, passProps = {}, navOptions = {}) => {
- const {actions} = this.props;
const defaultOptions = {
popGesture: !LocalConfig.AutoSelectServerUrl,
topBar: {
@@ -169,7 +168,7 @@ export default class SelectServer extends PureComponent {
};
const options = merge(defaultOptions, navOptions);
- actions.goToScreen(screen, title, passProps, options);
+ goToScreen(screen, title, passProps, options);
};
handleAndroidKeyboard = () => {
@@ -287,7 +286,7 @@ export default class SelectServer extends PureComponent {
await this.props.actions.handleSuccessfulLogin();
this.scheduleSessionExpiredNotification();
- this.props.actions.resetToChannel();
+ resetToChannel();
};
pingServer = (url, retryWithHttp = true) => {
diff --git a/app/screens/select_team/index.js b/app/screens/select_team/index.js
index beb5b8c9f..944e483c7 100644
--- a/app/screens/select_team/index.js
+++ b/app/screens/select_team/index.js
@@ -9,7 +9,6 @@ import {logout} from 'mattermost-redux/actions/users';
import {getJoinableTeams} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
-import {resetToChannel, dismissModal} from 'app/actions/navigation';
import {handleTeamChange} from 'app/actions/views/select_team';
import {isLandscape} from 'app/selectors/device';
import {isGuest} from 'app/utils/users';
@@ -34,8 +33,6 @@ function mapDispatchToProps(dispatch) {
handleTeamChange,
joinTeam,
logout,
- resetToChannel,
- dismissModal,
}, dispatch),
};
}
diff --git a/app/screens/select_team/select_team.js b/app/screens/select_team/select_team.js
index b4b1be8e9..7d2c29703 100644
--- a/app/screens/select_team/select_team.js
+++ b/app/screens/select_team/select_team.js
@@ -18,12 +18,13 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter';
import FormattedText from 'app/components/formatted_text';
import Loading from 'app/components/loading';
import StatusBar from 'app/components/status_bar';
-import {preventDoubleTap} from 'app/utils/tap';
-import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
-import {t} from 'app/utils/i18n';
import CustomList from 'app/components/custom_list';
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import TeamIcon from 'app/components/team_icon';
+import {resetToChannel, dismissModal} from 'app/actions/navigation';
+import {preventDoubleTap} from 'app/utils/tap';
+import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
+import {t} from 'app/utils/i18n';
const TEAMS_PER_PAGE = 50;
@@ -34,8 +35,6 @@ export default class SelectTeam extends PureComponent {
handleTeamChange: PropTypes.func.isRequired,
joinTeam: PropTypes.func.isRequired,
logout: PropTypes.func.isRequired,
- resetToChannel: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string.isRequired,
currentUrl: PropTypes.string.isRequired,
@@ -116,7 +115,7 @@ export default class SelectTeam extends PureComponent {
};
close = () => {
- this.props.actions.dismissModal();
+ dismissModal();
};
goToChannelView = () => {
@@ -124,7 +123,7 @@ export default class SelectTeam extends PureComponent {
disableTermsModal: true,
};
- this.props.actions.resetToChannel(passProps);
+ resetToChannel(passProps);
};
onSelectTeam = async (team) => {
diff --git a/app/screens/select_team/select_team.test.js b/app/screens/select_team/select_team.test.js
index 550093919..587bed05a 100644
--- a/app/screens/select_team/select_team.test.js
+++ b/app/screens/select_team/select_team.test.js
@@ -29,8 +29,6 @@ describe('SelectTeam', () => {
handleTeamChange: jest.fn(),
joinTeam: jest.fn(),
logout: jest.fn(),
- resetToChannel: jest.fn(),
- dismissModal: jest.fn(),
};
const baseProps = {
diff --git a/app/screens/selector_screen/index.js b/app/screens/selector_screen/index.js
index 1bd00b4ac..e5f69d1fd 100644
--- a/app/screens/selector_screen/index.js
+++ b/app/screens/selector_screen/index.js
@@ -9,8 +9,6 @@ import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getProfiles, searchProfiles} from 'mattermost-redux/actions/users';
import {getChannels, searchChannels} from 'mattermost-redux/actions/channels';
-import {popTopScreen} from 'app/actions/navigation';
-
import SelectorScreen from './selector_screen';
function mapStateToProps(state) {
@@ -34,7 +32,6 @@ function mapDispatchToProps(dispatch) {
getChannels,
searchProfiles,
searchChannels,
- popTopScreen,
}, dispatch),
};
}
diff --git a/app/screens/selector_screen/selector_screen.js b/app/screens/selector_screen/selector_screen.js
index c89483faf..fc7be8b56 100644
--- a/app/screens/selector_screen/selector_screen.js
+++ b/app/screens/selector_screen/selector_screen.js
@@ -31,6 +31,7 @@ import {
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
+import {popTopScreen} from 'app/actions/navigation';
export default class SelectorScreen extends PureComponent {
static propTypes = {
@@ -39,7 +40,6 @@ export default class SelectorScreen extends PureComponent {
getChannels: PropTypes.func.isRequired,
searchProfiles: PropTypes.func.isRequired,
searchChannels: PropTypes.func.isRequired,
- popTopScreen: PropTypes.func.isRequired,
}),
componentId: PropTypes.string,
currentTeamId: PropTypes.string.isRequired,
@@ -98,7 +98,7 @@ export default class SelectorScreen extends PureComponent {
};
close = () => {
- this.props.actions.popTopScreen();
+ popTopScreen();
};
handleSelectItem = (id, item) => {
diff --git a/app/screens/selector_screen/selector_screen.test.js b/app/screens/selector_screen/selector_screen.test.js
index eb6d311cb..b017809f9 100644
--- a/app/screens/selector_screen/selector_screen.test.js
+++ b/app/screens/selector_screen/selector_screen.test.js
@@ -67,7 +67,6 @@ describe('SelectorScreen', () => {
getChannels,
searchProfiles,
searchChannels,
- popTopScreen: jest.fn(),
};
const baseProps = {
diff --git a/app/screens/settings/advanced_settings/advanced_settings.js b/app/screens/settings/advanced_settings/advanced_settings.js
index 06d881331..9faf22d5a 100644
--- a/app/screens/settings/advanced_settings/advanced_settings.js
+++ b/app/screens/settings/advanced_settings/advanced_settings.js
@@ -22,13 +22,13 @@ import {t} from 'app/utils/i18n';
import {deleteFileCache, getFileCacheSize} from 'app/utils/file';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+import {dismissAllModals} from 'app/actions/navigation';
import Config from 'assets/config';
class AdvancedSettings extends Component {
static propTypes = {
actions: PropTypes.shape({
- dismissAllModals: PropTypes.func.isRequired,
purgeOfflineStore: PropTypes.func.isRequired,
}).isRequired,
intl: intlShape.isRequired,
@@ -81,7 +81,7 @@ class AdvancedSettings extends Component {
actions.purgeOfflineStore();
if (Platform.OS === 'android') {
- actions.dismissAllModals();
+ dismissAllModals();
}
});
diff --git a/app/screens/settings/advanced_settings/index.js b/app/screens/settings/advanced_settings/index.js
index 1109a77a1..0655ea903 100644
--- a/app/screens/settings/advanced_settings/index.js
+++ b/app/screens/settings/advanced_settings/index.js
@@ -4,7 +4,6 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
-import {dismissAllModals} from 'app/actions/navigation';
import {purgeOfflineStore} from 'app/actions/views/root';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {isLandscape} from 'app/selectors/device';
@@ -20,7 +19,6 @@ function mapStateToProps(state) {
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
- dismissAllModals,
purgeOfflineStore,
}, dispatch),
};
diff --git a/app/screens/settings/display_settings/display_settings.js b/app/screens/settings/display_settings/display_settings.js
index dbf9420d1..2734a5971 100644
--- a/app/screens/settings/display_settings/display_settings.js
+++ b/app/screens/settings/display_settings/display_settings.js
@@ -15,12 +15,10 @@ import ClockDisplay from 'app/screens/settings/clock_display';
import SettingsItem from 'app/screens/settings/settings_item';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+import {goToScreen} from 'app/actions/navigation';
export default class DisplaySettings extends PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- goToScreen: PropTypes.func.isRequired,
- }).isRequired,
componentId: PropTypes.string,
theme: PropTypes.object.isRequired,
enableTheme: PropTypes.bool.isRequired,
@@ -41,13 +39,12 @@ export default class DisplaySettings extends PureComponent {
};
goToClockDisplaySettings = preventDoubleTap(() => {
- const {actions} = this.props;
const {intl} = this.context;
if (Platform.OS === 'ios') {
const screen = 'ClockDisplaySettings';
const title = intl.formatMessage({id: 'user.settings.display.clockDisplay', defaultMessage: 'Clock Display'});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
return;
}
@@ -55,30 +52,28 @@ export default class DisplaySettings extends PureComponent {
});
goToSidebarSettings = preventDoubleTap(() => {
- const {actions, theme} = this.props;
+ const {theme} = this.props;
const {intl} = this.context;
const screen = 'SidebarSettings';
const title = intl.formatMessage({id: 'mobile.display_settings.sidebar', defaultMessage: 'Sidebar'});
- actions.goToScreen(screen, title, {theme});
+ goToScreen(screen, title, {theme});
});
goToTimezoneSettings = preventDoubleTap(() => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'TimezoneSettings';
const title = intl.formatMessage({id: 'mobile.advanced_settings.timezone', defaultMessage: 'Timezone'});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
});
goToThemeSettings = preventDoubleTap(() => {
- const {actions} = this.props;
const {intl} = this.context;
const screen = 'ThemeSettings';
const title = intl.formatMessage({id: 'mobile.display_settings.theme', defaultMessage: 'Theme'});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
});
render() {
diff --git a/app/screens/settings/display_settings/display_settings.test.js b/app/screens/settings/display_settings/display_settings.test.js
index eb28389f2..a76dfbb59 100644
--- a/app/screens/settings/display_settings/display_settings.test.js
+++ b/app/screens/settings/display_settings/display_settings.test.js
@@ -15,9 +15,6 @@ jest.mock('react-intl');
describe('DisplaySettings', () => {
const baseProps = {
- actions: {
- goToScreen: jest.fn(),
- },
theme: Preferences.THEMES.default,
enableTheme: false,
enableTimezone: false,
diff --git a/app/screens/settings/display_settings/index.js b/app/screens/settings/display_settings/index.js
index 038533b84..4db4e9d1a 100644
--- a/app/screens/settings/display_settings/index.js
+++ b/app/screens/settings/display_settings/index.js
@@ -1,13 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
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 {getAllowedThemes} from 'app/selectors/theme';
import {isThemeSwitchingEnabled} from 'app/utils/theme';
import {isLandscape} from 'app/selectors/device';
@@ -25,12 +23,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- goToScreen,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(DisplaySettings);
+export default connect(mapStateToProps)(DisplaySettings);
diff --git a/app/screens/settings/general/index.js b/app/screens/settings/general/index.js
index 01e2aff21..38efbe620 100644
--- a/app/screens/settings/general/index.js
+++ b/app/screens/settings/general/index.js
@@ -9,7 +9,6 @@ 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 {purgeOfflineStore} from 'app/actions/views/root';
import {isLandscape} from 'app/selectors/device';
import {removeProtocol} from 'app/utils/url';
@@ -36,8 +35,6 @@ function mapDispatchToProps(dispatch) {
actions: bindActionCreators({
clearErrors,
purgeOfflineStore,
- goToScreen,
- dismissModal,
}, dispatch),
};
}
diff --git a/app/screens/settings/general/settings.js b/app/screens/settings/general/settings.js
index e0e522d18..d3d9c4336 100644
--- a/app/screens/settings/general/settings.js
+++ b/app/screens/settings/general/settings.js
@@ -19,6 +19,7 @@ import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {isValidUrl} from 'app/utils/url';
import {t} from 'app/utils/i18n';
+import {goToScreen, dismissModal} from 'app/actions/navigation';
import LocalConfig from 'assets/config';
@@ -27,8 +28,6 @@ class Settings extends PureComponent {
actions: PropTypes.shape({
clearErrors: PropTypes.func.isRequired,
purgeOfflineStore: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
config: PropTypes.object.isRequired,
@@ -53,7 +52,7 @@ class Settings extends PureComponent {
navigationButtonPressed({buttonId}) {
if (buttonId === 'close-settings') {
- this.props.actions.dismissModal();
+ dismissModal();
}
}
@@ -84,39 +83,39 @@ class Settings extends PureComponent {
};
goToAbout = preventDoubleTap(() => {
- const {actions, intl, config} = this.props;
+ const {intl, config} = this.props;
const screen = 'About';
const title = intl.formatMessage({id: 'about.title', defaultMessage: 'About {appTitle}'}, {appTitle: config.SiteName || 'Mattermost'});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
});
goToNotifications = preventDoubleTap(() => {
- const {actions, intl} = this.props;
+ const {intl} = this.props;
const screen = 'NotificationSettings';
const title = intl.formatMessage({id: 'user.settings.modal.notifications', defaultMessage: 'Notifications'});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
});
goToDisplaySettings = preventDoubleTap(() => {
- const {actions, intl} = this.props;
+ const {intl} = this.props;
const screen = 'DisplaySettings';
const title = intl.formatMessage({id: 'user.settings.modal.display', defaultMessage: 'Display'});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
});
goToAdvancedSettings = preventDoubleTap(() => {
- const {actions, intl} = this.props;
+ const {intl} = this.props;
const screen = 'AdvancedSettings';
const title = intl.formatMessage({id: 'mobile.advanced_settings.title', defaultMessage: 'Advanced Settings'});
- actions.goToScreen(screen, title);
+ goToScreen(screen, title);
});
goToSelectTeam = preventDoubleTap(() => {
- const {actions, currentUrl, intl, theme} = this.props;
+ const {currentUrl, intl, theme} = this.props;
const screen = 'SelectTeam';
const title = intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'});
const passProps = {
@@ -124,18 +123,18 @@ class Settings extends PureComponent {
theme,
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
});
goToClientUpgrade = preventDoubleTap(() => {
- const {actions, intl} = this.props;
+ const {intl} = this.props;
const screen = 'ClientUpgrade';
const title = intl.formatMessage({id: 'mobile.client_upgrade', defaultMessage: 'Upgrade App'});
const passProps = {
userCheckedForUpgrade: true,
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
});
openErrorEmail = preventDoubleTap(() => {
diff --git a/app/screens/settings/notification_settings/index.js b/app/screens/settings/notification_settings/index.js
index 4c98954eb..7104f03b2 100644
--- a/app/screens/settings/notification_settings/index.js
+++ b/app/screens/settings/notification_settings/index.js
@@ -11,8 +11,6 @@ import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers';
import {isLandscape} from 'app/selectors/device';
import {updateMe} from 'mattermost-redux/actions/users';
-import {goToScreen} from 'app/actions/navigation';
-
import NotificationSettings from './notification_settings';
function mapStateToProps(state) {
@@ -38,7 +36,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
updateMe,
- goToScreen,
}, dispatch),
};
}
diff --git a/app/screens/settings/notification_settings/notification_settings.js b/app/screens/settings/notification_settings/notification_settings.js
index c15a1bef7..a8679a8f5 100644
--- a/app/screens/settings/notification_settings/notification_settings.js
+++ b/app/screens/settings/notification_settings/notification_settings.js
@@ -21,12 +21,12 @@ import {getNotificationProps} from 'app/utils/notify_props';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
+import {goToScreen} from 'app/actions/navigation';
class NotificationSettings extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
updateMe: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
}),
componentId: PropTypes.string,
currentUser: PropTypes.object.isRequired,
@@ -63,7 +63,7 @@ class NotificationSettings extends PureComponent {
});
goToNotificationSettingsAutoResponder = () => {
- const {actions, currentUser, intl} = this.props;
+ const {currentUser, intl} = this.props;
const screen = 'NotificationSettingsAutoResponder';
const title = intl.formatMessage({
id: 'mobile.notification_settings.auto_responder_short',
@@ -74,11 +74,11 @@ class NotificationSettings extends PureComponent {
onBack: this.saveAutoResponder,
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
};
goToNotificationSettingsEmail = () => {
- const {actions, currentUser, intl} = this.props;
+ const {currentUser, intl} = this.props;
const screen = 'NotificationSettingsEmail';
const title = intl.formatMessage({
id: 'mobile.notification_settings.email_title',
@@ -88,11 +88,11 @@ class NotificationSettings extends PureComponent {
currentUser,
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
};
goToNotificationSettingsMentions = () => {
- const {actions, currentUser, intl} = this.props;
+ const {currentUser, intl} = this.props;
const screen = 'NotificationSettingsMentions';
const title = intl.formatMessage({
id: 'mobile.notification_settings.mentions_replies',
@@ -103,11 +103,11 @@ class NotificationSettings extends PureComponent {
onBack: this.saveNotificationProps,
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
};
goToNotificationSettingsMobile = () => {
- const {actions, currentUser, intl} = this.props;
+ const {currentUser, intl} = this.props;
const screen = 'NotificationSettingsMobile';
const title = intl.formatMessage({
id: 'mobile.notification_settings.mobile_title',
@@ -121,7 +121,7 @@ class NotificationSettings extends PureComponent {
notificationPreferences,
};
requestAnimationFrame(() => {
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
});
}).catch((e) => {
Alert.alert('There was a problem getting the device preferences', e.message);
diff --git a/app/screens/settings/notification_settings_mentions/index.js b/app/screens/settings/notification_settings_mentions/index.js
index c0fbfb402..ca04f8c97 100644
--- a/app/screens/settings/notification_settings_mentions/index.js
+++ b/app/screens/settings/notification_settings_mentions/index.js
@@ -1,12 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {isLandscape} from 'app/selectors/device';
-import {goToScreen} from 'app/actions/navigation';
import NotificationSettingsMentions from './notification_settings_mentions';
@@ -17,12 +15,4 @@ function mapStateToProps(state) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- goToScreen,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(NotificationSettingsMentions);
+export default connect(mapStateToProps)(NotificationSettingsMentions);
diff --git a/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js b/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js
index d2fced3eb..d10a0fe1e 100644
--- a/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js
+++ b/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js
@@ -11,9 +11,6 @@ import {setNavigatorStyles} from 'app/utils/theme';
export default class NotificationSettingsMentionsBase extends PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- goToScreen: PropTypes.func.isRequired,
- }).isRequired,
componentId: PropTypes.string,
currentUser: PropTypes.object.isRequired,
intl: intlShape.isRequired,
diff --git a/app/screens/settings/notification_settings_mentions/notification_settings_mentions.ios.js b/app/screens/settings/notification_settings_mentions/notification_settings_mentions.ios.js
index 39a8cd97a..776cc89ca 100644
--- a/app/screens/settings/notification_settings_mentions/notification_settings_mentions.ios.js
+++ b/app/screens/settings/notification_settings_mentions/notification_settings_mentions.ios.js
@@ -8,18 +8,20 @@ import {
View,
} from 'react-native';
import {injectIntl} from 'react-intl';
+
import FormattedText from 'app/components/formatted_text';
import StatusBar from 'app/components/status_bar';
import Section from 'app/screens/settings/section';
import SectionItem from 'app/screens/settings/section_item';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
+import {goToScreen} from 'app/actions/navigation';
import NotificationSettingsMentionsBase from './notification_settings_mention_base';
class NotificationSettingsMentionsIos extends NotificationSettingsMentionsBase {
goToNotificationSettingsMentionKeywords = () => {
- const {actions, intl} = this.props;
+ const {intl} = this.props;
this.goingBack = false;
const screen = 'NotificationSettingsMentionsKeywords';
@@ -29,7 +31,7 @@ class NotificationSettingsMentionsIos extends NotificationSettingsMentionsBase {
onBack: this.updateMentionKeys,
};
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
};
renderMentionSection(style) {
diff --git a/app/screens/settings/notification_settings_mentions_keywords/__snapshots__/notification_settings_mentions_keywords.test.js.snap b/app/screens/settings/notification_settings_mentions_keywords/__snapshots__/notification_settings_mentions_keywords.test.js.snap
index 3c43dd499..defd9275c 100644
--- a/app/screens/settings/notification_settings_mentions_keywords/__snapshots__/notification_settings_mentions_keywords.test.js.snap
+++ b/app/screens/settings/notification_settings_mentions_keywords/__snapshots__/notification_settings_mentions_keywords.test.js.snap
@@ -5,14 +5,9 @@ NotificationSettingsMentionsKeywords {
"context": Object {},
"handleSubmit": [Function],
"keywordsRef": [Function],
- "navigationEventListener": Object {
- "remove": [Function],
- },
+ "navigationEventListener": undefined,
"onKeywordsChangeText": [Function],
"props": Object {
- "actions": Object {
- "popTopScreen": [MockFunction],
- },
"componentId": "component-id",
"intl": Object {
"defaultFormats": Object {},
@@ -93,11 +88,6 @@ NotificationSettingsMentionsKeywords {
"useState": [Function],
},
"_element": {
- this.props.actions.popTopScreen();
+ popTopScreen();
};
keywordsRef = (ref) => {
diff --git a/app/screens/settings/notification_settings_mentions_keywords/notification_settings_mentions_keywords.test.js b/app/screens/settings/notification_settings_mentions_keywords/notification_settings_mentions_keywords.test.js
index 3f1c11e41..e90a16f11 100644
--- a/app/screens/settings/notification_settings_mentions_keywords/notification_settings_mentions_keywords.test.js
+++ b/app/screens/settings/notification_settings_mentions_keywords/notification_settings_mentions_keywords.test.js
@@ -11,9 +11,6 @@ import NotificationSettingsMentionsKeywords from './notification_settings_mentio
describe('NotificationSettingsMentionsKeywords', () => {
const baseProps = {
- actions: {
- popTopScreen: jest.fn(),
- },
componentId: 'component-id',
keywords: '',
isLandscape: false,
diff --git a/app/screens/settings/sidebar/sidebar.test.js b/app/screens/settings/sidebar/sidebar.test.js
index 2acf53742..78d9c1452 100644
--- a/app/screens/settings/sidebar/sidebar.test.js
+++ b/app/screens/settings/sidebar/sidebar.test.js
@@ -13,6 +13,7 @@ import SidebarSettings from './index';
jest.mock('react-intl');
jest.mock('app/mattermost_managed', () => ({
isRunningInSplitView: jest.fn().mockResolvedValue(false),
+ addEventListener: jest.fn(),
}));
describe('SidebarSettings', () => {
diff --git a/app/screens/settings/theme/theme.test.js b/app/screens/settings/theme/theme.test.js
index 53318bc99..cbc658296 100644
--- a/app/screens/settings/theme/theme.test.js
+++ b/app/screens/settings/theme/theme.test.js
@@ -3,23 +3,17 @@
import React from 'react';
import {shallow} from 'enzyme';
-import {Navigation} from 'react-native-navigation';
import Preferences from 'mattermost-redux/constants/preferences';
import EphemeralStore from 'app/store/ephemeral_store';
+import * as NavigationActions from 'app/actions/navigation';
import Theme from './theme';
import ThemeTile from './theme_tile';
jest.mock('react-intl');
-jest.mock('react-native-navigation', () => ({
- Navigation: {
- mergeOptions: jest.fn(),
- },
-}));
-
const allowedThemes = [
{
type: 'Mattermost',
@@ -153,7 +147,9 @@ describe('Theme', () => {
expect(wrapper.find(ThemeTile)).toHaveLength(4);
});
- test('should call Navigation.mergeOptions on all navigation components when theme changes', () => {
+ test('should call mergeNavigationOptions on all navigation components when theme changes', () => {
+ const mergeNavigationOptions = jest.spyOn(NavigationActions, 'mergeNavigationOptions');
+
const componentIds = ['component-1', 'component-2', 'component-3'];
componentIds.forEach((componentId) => {
EphemeralStore.addNavigationComponentId(componentId);
@@ -185,7 +181,7 @@ describe('Theme', () => {
},
};
- expect(Navigation.mergeOptions.mock.calls).toEqual([
+ expect(mergeNavigationOptions.mock.calls).toEqual([
[componentIds[2], options],
[componentIds[1], options],
[componentIds[0], options],
diff --git a/app/screens/settings/timezone/index.js b/app/screens/settings/timezone/index.js
index a27919eda..fa3799dd7 100644
--- a/app/screens/settings/timezone/index.js
+++ b/app/screens/settings/timezone/index.js
@@ -10,7 +10,6 @@ import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getUserTimezone} from 'mattermost-redux/selectors/entities/timezone';
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
import {isLandscape} from 'app/selectors/device';
-import {goToScreen} from 'app/actions/navigation';
import {updateUser} from 'app/actions/views/edit_profile';
import Timezone from './timezone';
@@ -34,7 +33,6 @@ function mapDispatchToProps(dispatch) {
actions: bindActionCreators({
getSupportedTimezones,
updateUser,
- goToScreen,
}, dispatch),
};
}
diff --git a/app/screens/settings/timezone/select_timezone/index.js b/app/screens/settings/timezone/select_timezone/index.js
index 681f264c4..632b30363 100644
--- a/app/screens/settings/timezone/select_timezone/index.js
+++ b/app/screens/settings/timezone/select_timezone/index.js
@@ -1,13 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getSupportedTimezones} from 'mattermost-redux/selectors/entities/general';
-import {popTopScreen} from 'app/actions/navigation';
import {isLandscape} from 'app/selectors/device';
import SelectTimezone from './select_timezone';
@@ -30,12 +28,4 @@ function mapStateToProps(state, props) {
};
}
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- popTopScreen,
- }, dispatch),
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(SelectTimezone);
+export default connect(mapStateToProps)(SelectTimezone);
diff --git a/app/screens/settings/timezone/select_timezone/select_timezone.js b/app/screens/settings/timezone/select_timezone/select_timezone.js
index 8928125fd..a16f810e1 100644
--- a/app/screens/settings/timezone/select_timezone/select_timezone.js
+++ b/app/screens/settings/timezone/select_timezone/select_timezone.js
@@ -13,7 +13,7 @@ import {intlShape} from 'react-intl';
import SearchBar from 'app/components/search_bar';
import StatusBar from 'app/components/status_bar';
-import SelectTimezoneRow from './select_timezone_row';
+import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import {ListTypes} from 'app/constants';
import {
@@ -21,16 +21,15 @@ import {
makeStyleSheetFromTheme,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
-import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
+import {popTopScreen} from 'app/actions/navigation';
+
+import SelectTimezoneRow from './select_timezone_row';
const ITEM_HEIGHT = 45;
const VIEWABILITY_CONFIG = ListTypes.VISIBILITY_CONFIG_DEFAULTS;
export default class Timezone extends PureComponent {
static propTypes = {
- actions: PropTypes.shape({
- popTopScreen: PropTypes.func.isRequired,
- }).isRequired,
selectedTimezone: PropTypes.string.isRequired,
initialScrollIndex: PropTypes.number.isRequired,
timezones: PropTypes.array.isRequired,
@@ -67,7 +66,7 @@ export default class Timezone extends PureComponent {
timezoneSelected = (timezone) => {
this.props.onBack(timezone);
- this.props.actions.popTopScreen();
+ popTopScreen();
};
handleTextChanged = (value) => {
diff --git a/app/screens/settings/timezone/timezone.js b/app/screens/settings/timezone/timezone.js
index e04d35bbb..ffebb05d5 100644
--- a/app/screens/settings/timezone/timezone.js
+++ b/app/screens/settings/timezone/timezone.js
@@ -18,6 +18,7 @@ import Section from 'app/screens/settings/section';
import SectionItem from 'app/screens/settings/section_item';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {getDeviceTimezone} from 'app/utils/timezone';
+import {goToScreen} from 'app/actions/navigation';
export default class Timezone extends PureComponent {
static propTypes = {
@@ -116,7 +117,6 @@ export default class Timezone extends PureComponent {
goToSelectTimezone = () => {
const {
- actions,
userTimezone: {manualTimezone},
} = this.props;
const {intl} = this.context;
@@ -129,7 +129,7 @@ export default class Timezone extends PureComponent {
this.goingBack = false;
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
};
render() {
diff --git a/app/screens/sso/index.js b/app/screens/sso/index.js
index 64553978a..f17246b5b 100644
--- a/app/screens/sso/index.js
+++ b/app/screens/sso/index.js
@@ -4,7 +4,6 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
-import {resetToChannel} from 'app/actions/navigation';
import {handleSuccessfulLogin, scheduleExpiredNotification} from 'app/actions/views/login';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
@@ -25,7 +24,6 @@ function mapDispatchToProps(dispatch) {
scheduleExpiredNotification,
handleSuccessfulLogin,
setStoreFromLocalData,
- resetToChannel,
}, dispatch),
};
}
diff --git a/app/screens/sso/sso.js b/app/screens/sso/sso.js
index 20ed882b8..788a37196 100644
--- a/app/screens/sso/sso.js
+++ b/app/screens/sso/sso.js
@@ -19,6 +19,7 @@ import {Client4} from 'mattermost-redux/client';
import {ViewTypes} from 'app/constants';
import Loading from 'app/components/loading';
import StatusBar from 'app/components/status_bar';
+import {resetToChannel} from 'app/actions/navigation';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import tracker from 'app/utils/time_tracker';
@@ -68,7 +69,6 @@ class SSO extends PureComponent {
scheduleExpiredNotification: PropTypes.func.isRequired,
handleSuccessfulLogin: PropTypes.func.isRequired,
setStoreFromLocalData: PropTypes.func.isRequired,
- resetToChannel: PropTypes.func.isRequired,
}).isRequired,
};
@@ -119,7 +119,7 @@ class SSO extends PureComponent {
this.scheduleSessionExpiredNotification();
- this.props.actions.resetToChannel();
+ resetToChannel();
};
onMessage = (event) => {
diff --git a/app/screens/terms_of_service/index.js b/app/screens/terms_of_service/index.js
index 51519d733..a9c843bac 100644
--- a/app/screens/terms_of_service/index.js
+++ b/app/screens/terms_of_service/index.js
@@ -8,12 +8,6 @@ import {getTermsOfService, logout, updateMyTermsOfServiceStatus} from 'mattermos
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {
- setButtons,
- dismissModal,
- dismissAllModals,
-} from 'app/actions/navigation';
-
import TermsOfService from './terms_of_service.js';
function mapStateToProps(state) {
@@ -31,9 +25,6 @@ function mapDispatchToProps(dispatch) {
getTermsOfService,
logout,
updateMyTermsOfServiceStatus,
- setButtons,
- dismissModal,
- dismissAllModals,
}, dispatch),
};
}
diff --git a/app/screens/terms_of_service/terms_of_service.js b/app/screens/terms_of_service/terms_of_service.js
index ceebcb7b7..db72b2192 100644
--- a/app/screens/terms_of_service/terms_of_service.js
+++ b/app/screens/terms_of_service/terms_of_service.js
@@ -19,15 +19,14 @@ import StatusBar from 'app/components/status_bar';
import {getMarkdownTextStyles, getMarkdownBlockStyles} from 'app/utils/markdown';
import {makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
+import {dismissModal, dismissAllModals, setButtons} from 'app/actions/navigation';
+
export default class TermsOfService extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
logout: PropTypes.func.isRequired,
getTermsOfService: PropTypes.func.isRequired,
updateMyTermsOfServiceStatus: PropTypes.func.isRequired,
- setButtons: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- dismissAllModals: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
closeButton: PropTypes.object,
@@ -98,13 +97,13 @@ export default class TermsOfService extends PureComponent {
}
setNavigatorButtons = (enabled = true) => {
- const {actions, componentId} = this.props;
+ const {componentId} = this.props;
const buttons = {
leftButtons: [{...this.leftButton, enabled}],
rightButtons: [{...this.rightButton, enabled}],
};
- actions.setButtons(componentId, buttons);
+ setButtons(componentId, buttons);
};
enableNavigatorLogout = () => {
@@ -113,13 +112,13 @@ export default class TermsOfService extends PureComponent {
rightButtons: [{...this.rightButton, enabled: false}],
};
- this.props.actions.setButtons(buttons);
+ setButtons(buttons);
};
- closeTermsAndLogout = () => {
+ closeTermsAndLogout = async () => {
const {actions} = this.props;
- actions.dismissAllModals();
+ await dismissAllModals();
actions.logout();
};
@@ -156,7 +155,7 @@ export default class TermsOfService extends PureComponent {
this.registerUserAction(
true,
() => {
- this.props.actions.dismissModal();
+ dismissModal();
},
this.handleAcceptTerms
);
diff --git a/app/screens/terms_of_service/terms_of_service.test.js b/app/screens/terms_of_service/terms_of_service.test.js
index 5bfa16711..85a21ae01 100644
--- a/app/screens/terms_of_service/terms_of_service.test.js
+++ b/app/screens/terms_of_service/terms_of_service.test.js
@@ -6,6 +6,8 @@ import {shallow} from 'enzyme';
import Preferences from 'mattermost-redux/constants/preferences';
+import * as NavigationActions from 'app/actions/navigation';
+
import TermsOfService from './terms_of_service.js';
jest.mock('react-intl');
@@ -23,9 +25,6 @@ describe('TermsOfService', () => {
getTermsOfService: jest.fn(),
updateMyTermsOfServiceStatus: jest.fn(),
logout: jest.fn(),
- setButtons: jest.fn(),
- dismissModal: jest.fn(),
- dismissAllModals: jest.fn(),
};
const baseProps = {
@@ -81,18 +80,20 @@ describe('TermsOfService', () => {
expect(wrapper.getElement()).toMatchSnapshot();
});
- test('should call props.actions.setButtons on setNavigatorButtons', async () => {
+ test('should call setButtons on setNavigatorButtons', async () => {
+ const setButtons = jest.spyOn(NavigationActions, 'setButtons');
+
const wrapper = shallow(
,
{context: {intl: {formatMessage: jest.fn()}}},
);
wrapper.setState({loading: false, termsId: 1, termsText: 'Terms Text'});
- expect(baseProps.actions.setButtons).toHaveBeenCalledTimes(2);
+ expect(setButtons).toHaveBeenCalledTimes(2);
wrapper.instance().setNavigatorButtons(true);
- expect(baseProps.actions.setButtons).toHaveBeenCalledTimes(3);
+ expect(setButtons).toHaveBeenCalledTimes(3);
wrapper.instance().setNavigatorButtons(false);
- expect(baseProps.actions.setButtons).toHaveBeenCalledTimes(4);
+ expect(setButtons).toHaveBeenCalledTimes(4);
});
test('should enable/disable navigator buttons on setNavigatorButtons true/false', () => {
@@ -120,6 +121,8 @@ describe('TermsOfService', () => {
});
test('should call dismissAllModals on closeTermsAndLogout', () => {
+ const dismissAllModals = jest.spyOn(NavigationActions, 'dismissAllModals');
+
const wrapper = shallow(
,
{context: {intl: {formatMessage: jest.fn()}}},
@@ -127,6 +130,6 @@ describe('TermsOfService', () => {
wrapper.setState({loading: false, termsId: 1, termsText: 'Terms Text'});
wrapper.instance().closeTermsAndLogout();
- expect(baseProps.actions.dismissAllModals).toHaveBeenCalledTimes(1);
+ expect(dismissAllModals).toHaveBeenCalledTimes(1);
});
});
diff --git a/app/screens/thread/index.js b/app/screens/thread/index.js
index a089d7755..7f1573ab0 100644
--- a/app/screens/thread/index.js
+++ b/app/screens/thread/index.js
@@ -10,8 +10,6 @@ import {selectPost} from 'mattermost-redux/actions/posts';
import {makeGetChannel, getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
import {makeGetPostIdsForThread} from 'mattermost-redux/selectors/entities/posts';
-import {popTopScreen, resetToChannel} from 'app/actions/navigation';
-
import Thread from './thread';
function makeMapStateToProps() {
@@ -39,8 +37,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
selectPost,
- popTopScreen,
- resetToChannel,
}, dispatch),
};
}
diff --git a/app/screens/thread/thread.ios.test.js b/app/screens/thread/thread.ios.test.js
index 0478ed22d..bfb819f43 100644
--- a/app/screens/thread/thread.ios.test.js
+++ b/app/screens/thread/thread.ios.test.js
@@ -6,23 +6,18 @@ import {shallow} from 'enzyme';
import Preferences from 'mattermost-redux/constants/preferences';
import {General, RequestStatus} from 'mattermost-redux/constants';
+
import PostList from 'app/components/post_list';
+import * as NavigationActions from 'app/actions/navigation';
import ThreadIOS from './thread.ios';
jest.mock('react-intl');
-jest.mock('react-native-navigation', () => ({
- Navigation: {
- mergeOptions: jest.fn(),
- },
-}));
describe('thread', () => {
const baseProps = {
actions: {
selectPost: jest.fn(),
- popTopScreen: jest.fn(),
- resetToChannel: jest.fn(),
},
channelId: 'channel_id',
channelType: General.OPEN_CHANNEL,
@@ -55,7 +50,9 @@ describe('thread', () => {
expect(wrapper.getElement()).toMatchSnapshot();
});
- test('should call props.actions.resetToChannel on onCloseChannel', () => {
+ test('should call resetToChannel on onCloseChannel', () => {
+ const resetToChannel = jest.spyOn(NavigationActions, 'resetToChannel');
+
const passProps = {
disableTermsModal: true,
};
@@ -66,8 +63,8 @@ describe('thread', () => {
{context: {intl: {formatMessage: jest.fn()}}},
);
wrapper.instance().onCloseChannel();
- expect(baseProps.actions.resetToChannel).toHaveBeenCalledTimes(1);
- expect(baseProps.actions.resetToChannel).toBeCalledWith(passProps);
+ expect(resetToChannel).toHaveBeenCalledTimes(1);
+ expect(resetToChannel).toBeCalledWith(passProps);
});
test('should match snapshot, render footer', () => {
diff --git a/app/screens/thread/thread_base.js b/app/screens/thread/thread_base.js
index 50c733642..0aa3c357d 100644
--- a/app/screens/thread/thread_base.js
+++ b/app/screens/thread/thread_base.js
@@ -5,20 +5,18 @@ import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {Keyboard} from 'react-native';
import {intlShape} from 'react-intl';
-import {Navigation} from 'react-native-navigation';
import {General, RequestStatus} from 'mattermost-redux/constants';
import Loading from 'app/components/loading';
-import {setNavigatorStyles} from 'app/utils/theme';
import DeletedPost from 'app/components/deleted_post';
+import {resetToChannel, popTopScreen, mergeNavigationOptions} from 'app/actions/navigation';
+import {setNavigatorStyles} from 'app/utils/theme';
export default class ThreadBase extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
selectPost: PropTypes.func.isRequired,
- popTopScreen: PropTypes.func.isRequired,
- resetToChannel: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
channelId: PropTypes.string.isRequired,
@@ -55,13 +53,14 @@ export default class ThreadBase extends PureComponent {
this.postTextbox = React.createRef();
- Navigation.mergeOptions(props.componentId, {
+ const options = {
topBar: {
title: {
text: title,
},
},
- });
+ };
+ mergeNavigationOptions(props.componentId, options);
this.state = {
lastViewedAt: props.myMember && props.myMember.last_viewed_at,
@@ -88,8 +87,8 @@ export default class ThreadBase extends PureComponent {
}
close = () => {
- const {actions, componentId} = this.props;
- actions.popTopScreen(componentId);
+ const {componentId} = this.props;
+ popTopScreen(componentId);
};
handleAutoComplete = (value) => {
@@ -124,6 +123,6 @@ export default class ThreadBase extends PureComponent {
const passProps = {
disableTermsModal: true,
};
- this.props.actions.resetToChannel(passProps);
+ resetToChannel(passProps);
};
}
diff --git a/app/screens/user_profile/index.js b/app/screens/user_profile/index.js
index 4010b31fb..e6383f723 100644
--- a/app/screens/user_profile/index.js
+++ b/app/screens/user_profile/index.js
@@ -15,14 +15,6 @@ import {loadBot} from 'mattermost-redux/actions/bots';
import {getBotAccounts} from 'mattermost-redux/selectors/entities/bots';
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
-import {
- setButtons,
- dismissModal,
- goToScreen,
- dismissAllModals,
- popToRoot,
-} from 'app/actions/navigation';
-
import UserProfile from './user_profile';
function mapStateToProps(state, ownProps) {
@@ -52,11 +44,6 @@ function mapDispatchToProps(dispatch) {
makeDirectChannel,
setChannelDisplayName,
loadBot,
- setButtons,
- dismissModal,
- goToScreen,
- dismissAllModals,
- popToRoot,
}, dispatch),
};
}
diff --git a/app/screens/user_profile/user_profile.js b/app/screens/user_profile/user_profile.js
index 1e1f7d1fb..e7675eaf0 100644
--- a/app/screens/user_profile/user_profile.js
+++ b/app/screens/user_profile/user_profile.js
@@ -11,8 +11,10 @@ import {
} from 'react-native';
import {intlShape} from 'react-intl';
import {Navigation} from 'react-native-navigation';
+
import {displayUsername} from 'mattermost-redux/utils/user_utils';
import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils';
+
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import ProfilePicture from 'app/components/profile_picture';
import FormattedText from 'app/components/formatted_text';
@@ -20,11 +22,20 @@ import FormattedTime from 'app/components/formatted_time';
import StatusBar from 'app/components/status_bar';
import BotTag from 'app/components/bot_tag';
import GuestTag from 'app/components/guest_tag';
+
import {alertErrorWithFallback} from 'app/utils/general';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
import {isGuest} from 'app/utils/users';
+import {
+ goToScreen,
+ popToRoot,
+ dismissModal,
+ dismissAllModals,
+ setButtons,
+} from 'app/actions/navigation';
+
import UserProfileRow from './user_profile_row';
import Config from 'assets/config';
@@ -34,11 +45,6 @@ export default class UserProfile extends PureComponent {
makeDirectChannel: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired,
loadBot: PropTypes.func.isRequired,
- setButtons: PropTypes.func.isRequired,
- dismissModal: PropTypes.func.isRequired,
- goToScreen: PropTypes.func.isRequired,
- dismissAllModals: PropTypes.func.isRequired,
- popToRoot: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
config: PropTypes.object.isRequired,
@@ -74,7 +80,7 @@ export default class UserProfile extends PureComponent {
rightButtons: [this.rightButton],
};
- props.actions.setButtons(props.componentId, buttons);
+ setButtons(props.componentId, buttons);
}
}
@@ -103,16 +109,16 @@ export default class UserProfile extends PureComponent {
}
}
- close = () => {
- const {actions, fromSettings, componentId} = this.props;
+ close = async () => {
+ const {fromSettings} = this.props;
if (fromSettings) {
- actions.dismissModal();
+ dismissModal();
return;
}
- actions.dismissAllModals();
- actions.popToRoot(componentId);
+ await dismissAllModals();
+ await popToRoot();
};
getDisplayName = () => {
@@ -227,7 +233,7 @@ export default class UserProfile extends PureComponent {
};
goToEditProfile = () => {
- const {actions, user: currentUser} = this.props;
+ const {user: currentUser} = this.props;
const {formatMessage} = this.context.intl;
const commandType = 'Push';
const screen = 'EditProfile';
@@ -235,7 +241,7 @@ export default class UserProfile extends PureComponent {
const passProps = {currentUser, commandType};
requestAnimationFrame(() => {
- actions.goToScreen(screen, title, passProps);
+ goToScreen(screen, title, passProps);
});
};
diff --git a/app/screens/user_profile/user_profile.test.js b/app/screens/user_profile/user_profile.test.js
index a3524e65a..79d36b16c 100644
--- a/app/screens/user_profile/user_profile.test.js
+++ b/app/screens/user_profile/user_profile.test.js
@@ -5,6 +5,8 @@ import {shallow} from 'enzyme';
import Preferences from 'mattermost-redux/constants/preferences';
+import * as NavigationActions from 'app/actions/navigation';
+
import UserProfile from './user_profile.js';
import BotTag from 'app/components/bot_tag';
import GuestTag from 'app/components/guest_tag';
@@ -23,11 +25,6 @@ describe('user_profile', () => {
setChannelDisplayName: jest.fn(),
makeDirectChannel: jest.fn(),
loadBot: jest.fn(),
- setButtons: jest.fn(),
- dismissModal: jest.fn(),
- goToScreen: jest.fn(),
- dismissAllModals: jest.fn(),
- popToRoot: jest.fn(),
};
const baseProps = {
actions,
@@ -54,7 +51,7 @@ describe('user_profile', () => {
is_bot: false,
};
- test('should match snapshot', async () => {
+ test('should match snapshot', () => {
const wrapper = shallow(
{
expect(wrapper.getElement()).toMatchSnapshot();
});
- test('should contain bot tag', async () => {
+ test('should contain bot tag', () => {
const botUser = {
email: 'test@test.com',
first_name: 'test',
@@ -91,7 +88,7 @@ describe('user_profile', () => {
)).toEqual(true);
});
- test('should contain guest tag', async () => {
+ test('should contain guest tag', () => {
const guestUser = {
email: 'test@test.com',
first_name: 'test',
@@ -117,7 +114,10 @@ describe('user_profile', () => {
)).toEqual(true);
});
- test('should push EditProfile', async () => {
+ test('should push EditProfile', () => {
+ jest.spyOn(global, 'requestAnimationFrame').mockImplementation((cb) => cb());
+ const goToScreen = jest.spyOn(NavigationActions, 'goToScreen');
+
const wrapper = shallow(
{
);
wrapper.instance().goToEditProfile();
- setTimeout(() => {
- expect(baseProps.actions.goToScreen).toHaveBeenCalledTimes(1);
- }, 16);
+ expect(goToScreen).toHaveBeenCalledTimes(1);
});
test('should call goToEditProfile', () => {
- const props = {
- ...baseProps,
- actions: {
- ...baseProps.actions,
- goToScreen: jest.fn(),
- },
- };
+ const goToScreen = jest.spyOn(NavigationActions, 'goToScreen');
+
const wrapper = shallow(
,
{context: {intl: {formatMessage: jest.fn()}}},
@@ -150,12 +143,14 @@ describe('user_profile', () => {
const event = {buttonId: wrapper.instance().rightButton.id};
wrapper.instance().navigationButtonPressed(event);
- setTimeout(() => {
- expect(baseProps.actions.goToScreen).toHaveBeenCalledTimes(1);
- }, 0);
+ expect(goToScreen).toHaveBeenCalledTimes(1);
});
- test('should close', async () => {
+ test('close should dismiss modal when fromSettings is true', async () => {
+ const dismissModal = jest.spyOn(NavigationActions, 'dismissModal');
+ const dismissAllModals = jest.spyOn(NavigationActions, 'dismissAllModals');
+ const popToRoot = jest.spyOn(NavigationActions, 'popToRoot');
+
const props = {...baseProps, fromSettings: true};
const wrapper = shallow(
@@ -166,15 +161,45 @@ describe('user_profile', () => {
{context: {intl: {formatMessage: jest.fn()}}},
);
+ await wrapper.instance().close();
+ expect(dismissModal).toHaveBeenCalledTimes(1);
+ expect(dismissAllModals).toHaveBeenCalledTimes(0);
+ expect(popToRoot).toHaveBeenCalledTimes(0);
+ });
+
+ test('close should dismiss all modals and pop to root when fromSettings is false', async () => {
+ const dismissModal = jest.spyOn(NavigationActions, 'dismissModal');
+ const dismissAllModals = jest.spyOn(NavigationActions, 'dismissAllModals');
+ const popToRoot = jest.spyOn(NavigationActions, 'popToRoot');
+
+ const props = {...baseProps, fromSettings: false};
+
+ const wrapper = shallow(
+ ,
+ {context: {intl: {formatMessage: jest.fn()}}},
+ );
+
+ await wrapper.instance().close();
+ expect(dismissModal).toHaveBeenCalledTimes(0);
+ expect(dismissAllModals).toHaveBeenCalledTimes(1);
+ expect(popToRoot).toHaveBeenCalledTimes(1);
+ });
+
+ test('should call close', () => {
+ const wrapper = shallow(
+ ,
+ {context: {intl: {formatMessage: jest.fn()}}},
+ );
+
+ const close = jest.spyOn(wrapper.instance(), 'close');
const event = {buttonId: 'close-settings'};
wrapper.instance().navigationButtonPressed(event);
- expect(props.actions.dismissModal).toHaveBeenCalledTimes(1);
-
- props.fromSettings = false;
- wrapper.setProps({...props});
- wrapper.instance().navigationButtonPressed(event);
- expect(props.actions.dismissAllModals).toHaveBeenCalledTimes(1);
- expect(props.actions.popToRoot).toHaveBeenCalledTimes(1);
- expect(props.actions.popToRoot).toHaveBeenCalledWith(props.componentId);
+ expect(close).toHaveBeenCalledTimes(1);
});
});
diff --git a/app/store/index.js b/app/store/index.js
index be1155865..ec5b0c483 100644
--- a/app/store/index.js
+++ b/app/store/index.js
@@ -1,301 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {batchActions} from 'redux-batched-actions';
-import {Platform} from 'react-native';
-import AsyncStorage from '@react-native-community/async-storage';
-import {createBlacklistFilter} from 'redux-persist-transform-filter';
-import {createTransform, persistStore} from 'redux-persist';
+import initialState from 'app/initial_state';
+import configureAppStore from './store';
-import {ErrorTypes, GeneralTypes} from 'mattermost-redux/action_types';
-import {General, RequestStatus} from 'mattermost-redux/constants';
-import {getConfig} from 'mattermost-redux/selectors/entities/general';
-import configureStore from 'mattermost-redux/store';
-import EventEmitter from 'mattermost-redux/utils/event_emitter';
+const store = configureAppStore(initialState);
-import {NavigationTypes, ViewTypes} from 'app/constants';
-import appReducer from 'app/reducers';
-import {throttle} from 'app/utils/general';
-import {getSiteUrl, setSiteUrl} from 'app/utils/image_cache_manager';
-import {createSentryMiddleware} from 'app/utils/sentry/middleware';
-
-import mattermostBucket from 'app/mattermost_bucket';
-
-import {messageRetention} from './middleware';
-import {createThunkMiddleware} from './thunk';
-import {transformSet} from './utils';
-
-function getAppReducer() {
- return require('../../app/reducers'); // eslint-disable-line global-require
-}
-
-const usersSetTransform = [
- 'profilesInChannel',
- 'profilesNotInChannel',
- 'profilesInTeam',
- 'profilesNotInTeam',
-];
-
-const channelSetTransform = [
- 'channelsInTeam',
-];
-
-const rolesSetTransform = [
- 'pending',
-];
-
-const setTransforms = [
- ...usersSetTransform,
- ...channelSetTransform,
- ...rolesSetTransform,
-];
-
-export default function configureAppStore(initialState) {
- const viewsBlackListFilter = createBlacklistFilter(
- 'views',
- ['extension', 'login', 'root']
- );
-
- const typingBlackListFilter = createBlacklistFilter(
- 'entities',
- ['typing']
- );
-
- const channelViewBlackList = {loading: true, refreshing: true, loadingPosts: true, postVisibility: true, retryFailed: true, loadMorePostsVisible: true};
- const channelViewBlackListFilter = createTransform(
- (inboundState) => {
- const channel = {};
-
- for (const channelKey of Object.keys(inboundState.channel)) {
- if (!channelViewBlackList[channelKey]) {
- channel[channelKey] = inboundState.channel[channelKey];
- }
- }
-
- return {
- ...inboundState,
- channel,
- };
- },
- null,
- {whitelist: ['views']} // Only run this filter on the views state (or any other entry that ends up being named views)
- );
-
- const emojiBlackList = {nonExistentEmoji: true};
- const emojiBlackListFilter = createTransform(
- (inboundState) => {
- const emojis = {};
-
- for (const emojiKey of Object.keys(inboundState.emojis)) {
- if (!emojiBlackList[emojiKey]) {
- emojis[emojiKey] = inboundState.emojis[emojiKey];
- }
- }
-
- return {
- ...inboundState,
- emojis,
- };
- },
- null,
- {whitelist: ['entities']} // Only run this filter on the entities state (or any other entry that ends up being named entities)
- );
-
- const setTransformer = createTransform(
- (inboundState, key) => {
- if (key === 'entities') {
- const state = {...inboundState};
- for (const prop in state) {
- if (state.hasOwnProperty(prop)) {
- state[prop] = transformSet(state[prop], setTransforms);
- }
- }
-
- return state;
- }
-
- return inboundState;
- },
- (outboundState, key) => {
- if (key === 'entities') {
- const state = {...outboundState};
- for (const prop in state) {
- if (state.hasOwnProperty(prop)) {
- state[prop] = transformSet(state[prop], setTransforms, false);
- }
- }
-
- return state;
- }
-
- return outboundState;
- }
- );
-
- const offlineOptions = {
- effect: (effect, action) => {
- if (typeof effect !== 'function') {
- throw new Error('Offline Action: effect must be a function.');
- } else if (!action.meta.offline.commit) {
- throw new Error('Offline Action: commit action must be present.');
- }
-
- return effect();
- },
- persist: (store, options) => {
- const persistor = persistStore(store, {storage: AsyncStorage, ...options}, () => {
- store.dispatch({
- type: General.STORE_REHYDRATION_COMPLETE,
- });
- });
-
- let purging = false;
-
- // for iOS write the entities to a shared file
- if (Platform.OS === 'ios') {
- store.subscribe(throttle(() => {
- const state = store.getState();
- if (state.entities) {
- const channelsInTeam = {...state.entities.channels.channelsInTeam};
- Object.keys(channelsInTeam).forEach((teamId) => {
- channelsInTeam[teamId] = Array.from(channelsInTeam[teamId]);
- });
-
- const profilesInChannel = {...state.entities.users.profilesInChannel};
- Object.keys(profilesInChannel).forEach((channelId) => {
- profilesInChannel[channelId] = Array.from(profilesInChannel[channelId]);
- });
-
- const entities = {
- ...state.entities,
- channels: {
- ...state.entities.channels,
- channelsInTeam,
- },
- users: {
- ...state.entities.users,
- profilesInChannel,
- profilesNotInTeam: [],
- profilesWithoutTeam: [],
- profilesNotInChannel: [],
- },
- };
- mattermostBucket.writeToFile('entities', JSON.stringify(entities));
- }
- }, 1000));
- }
-
- // check to see if the logout request was successful
- store.subscribe(async () => {
- const state = store.getState();
- const config = getConfig(state);
-
- if (getSiteUrl() !== config?.SiteURL) {
- setSiteUrl(config.SiteURL);
- }
-
- if ((state.requests.users.logout.status === RequestStatus.SUCCESS || state.requests.users.logout.status === RequestStatus.FAILURE) && !purging) {
- purging = true;
-
- await persistor.purge();
-
- store.dispatch(batchActions([
- {
- type: General.OFFLINE_STORE_RESET,
- data: initialState,
- },
- {
- type: General.STORE_REHYDRATION_COMPLETE,
- },
- {
- type: ViewTypes.SERVER_URL_CHANGED,
- serverUrl: state.entities.general.credentials.url || state.views.selectServer.serverUrl,
- },
- {
- type: GeneralTypes.RECEIVED_APP_DEVICE_TOKEN,
- data: state.entities.general.deviceToken,
- },
- ]));
-
- // When logging out remove the data stored in the bucket
- mattermostBucket.removePreference('cert');
- mattermostBucket.removePreference('emm');
- mattermostBucket.removeFile('entities');
- setSiteUrl(null);
-
- setTimeout(() => {
- purging = false;
- }, 500);
- } else if (state.views.root.purge && !purging) {
- purging = true;
-
- await persistor.purge();
-
- store.dispatch(batchActions([
- {
- type: General.OFFLINE_STORE_RESET,
- data: initialState,
- },
- {
- type: ErrorTypes.RESTORE_ERRORS,
- data: [...state.errors],
- },
- {
- type: GeneralTypes.RECEIVED_APP_DEVICE_TOKEN,
- data: state.entities.general.deviceToken,
- },
- {
- type: GeneralTypes.RECEIVED_APP_CREDENTIALS,
- data: {
- url: state.entities.general.credentials.url,
- },
- },
- {
- type: ViewTypes.SERVER_URL_CHANGED,
- serverUrl: state.entities.general.credentials.url || state.views.selectServer.serverUrl,
- },
- {
- type: GeneralTypes.RECEIVED_SERVER_VERSION,
- data: state.entities.general.serverVersion,
- },
- {
- type: General.STORE_REHYDRATION_COMPLETE,
- },
- ], 'BATCH_FOR_RESTART'));
-
- setTimeout(() => {
- purging = false;
- EventEmitter.emit(NavigationTypes.RESTART_APP);
- }, 500);
- }
- });
-
- return persistor;
- },
- persistOptions: {
- autoRehydrate: {
- log: false,
- },
- blacklist: ['device', 'navigation', 'offline', 'requests'],
- debounce: 500,
- transforms: [
- setTransformer,
- viewsBlackListFilter,
- typingBlackListFilter,
- channelViewBlackListFilter,
- emojiBlackListFilter,
- ],
- },
- };
-
- const clientOptions = {
- additionalMiddleware: [
- createThunkMiddleware(),
- createSentryMiddleware(),
- messageRetention,
- ],
- enableThunk: false, // We override the default thunk middleware
- };
-
- return configureStore(initialState, appReducer, offlineOptions, getAppReducer, clientOptions);
-}
+export default store;
diff --git a/app/store/store.js b/app/store/store.js
new file mode 100644
index 000000000..be1155865
--- /dev/null
+++ b/app/store/store.js
@@ -0,0 +1,301 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {batchActions} from 'redux-batched-actions';
+import {Platform} from 'react-native';
+import AsyncStorage from '@react-native-community/async-storage';
+import {createBlacklistFilter} from 'redux-persist-transform-filter';
+import {createTransform, persistStore} from 'redux-persist';
+
+import {ErrorTypes, GeneralTypes} from 'mattermost-redux/action_types';
+import {General, RequestStatus} from 'mattermost-redux/constants';
+import {getConfig} from 'mattermost-redux/selectors/entities/general';
+import configureStore from 'mattermost-redux/store';
+import EventEmitter from 'mattermost-redux/utils/event_emitter';
+
+import {NavigationTypes, ViewTypes} from 'app/constants';
+import appReducer from 'app/reducers';
+import {throttle} from 'app/utils/general';
+import {getSiteUrl, setSiteUrl} from 'app/utils/image_cache_manager';
+import {createSentryMiddleware} from 'app/utils/sentry/middleware';
+
+import mattermostBucket from 'app/mattermost_bucket';
+
+import {messageRetention} from './middleware';
+import {createThunkMiddleware} from './thunk';
+import {transformSet} from './utils';
+
+function getAppReducer() {
+ return require('../../app/reducers'); // eslint-disable-line global-require
+}
+
+const usersSetTransform = [
+ 'profilesInChannel',
+ 'profilesNotInChannel',
+ 'profilesInTeam',
+ 'profilesNotInTeam',
+];
+
+const channelSetTransform = [
+ 'channelsInTeam',
+];
+
+const rolesSetTransform = [
+ 'pending',
+];
+
+const setTransforms = [
+ ...usersSetTransform,
+ ...channelSetTransform,
+ ...rolesSetTransform,
+];
+
+export default function configureAppStore(initialState) {
+ const viewsBlackListFilter = createBlacklistFilter(
+ 'views',
+ ['extension', 'login', 'root']
+ );
+
+ const typingBlackListFilter = createBlacklistFilter(
+ 'entities',
+ ['typing']
+ );
+
+ const channelViewBlackList = {loading: true, refreshing: true, loadingPosts: true, postVisibility: true, retryFailed: true, loadMorePostsVisible: true};
+ const channelViewBlackListFilter = createTransform(
+ (inboundState) => {
+ const channel = {};
+
+ for (const channelKey of Object.keys(inboundState.channel)) {
+ if (!channelViewBlackList[channelKey]) {
+ channel[channelKey] = inboundState.channel[channelKey];
+ }
+ }
+
+ return {
+ ...inboundState,
+ channel,
+ };
+ },
+ null,
+ {whitelist: ['views']} // Only run this filter on the views state (or any other entry that ends up being named views)
+ );
+
+ const emojiBlackList = {nonExistentEmoji: true};
+ const emojiBlackListFilter = createTransform(
+ (inboundState) => {
+ const emojis = {};
+
+ for (const emojiKey of Object.keys(inboundState.emojis)) {
+ if (!emojiBlackList[emojiKey]) {
+ emojis[emojiKey] = inboundState.emojis[emojiKey];
+ }
+ }
+
+ return {
+ ...inboundState,
+ emojis,
+ };
+ },
+ null,
+ {whitelist: ['entities']} // Only run this filter on the entities state (or any other entry that ends up being named entities)
+ );
+
+ const setTransformer = createTransform(
+ (inboundState, key) => {
+ if (key === 'entities') {
+ const state = {...inboundState};
+ for (const prop in state) {
+ if (state.hasOwnProperty(prop)) {
+ state[prop] = transformSet(state[prop], setTransforms);
+ }
+ }
+
+ return state;
+ }
+
+ return inboundState;
+ },
+ (outboundState, key) => {
+ if (key === 'entities') {
+ const state = {...outboundState};
+ for (const prop in state) {
+ if (state.hasOwnProperty(prop)) {
+ state[prop] = transformSet(state[prop], setTransforms, false);
+ }
+ }
+
+ return state;
+ }
+
+ return outboundState;
+ }
+ );
+
+ const offlineOptions = {
+ effect: (effect, action) => {
+ if (typeof effect !== 'function') {
+ throw new Error('Offline Action: effect must be a function.');
+ } else if (!action.meta.offline.commit) {
+ throw new Error('Offline Action: commit action must be present.');
+ }
+
+ return effect();
+ },
+ persist: (store, options) => {
+ const persistor = persistStore(store, {storage: AsyncStorage, ...options}, () => {
+ store.dispatch({
+ type: General.STORE_REHYDRATION_COMPLETE,
+ });
+ });
+
+ let purging = false;
+
+ // for iOS write the entities to a shared file
+ if (Platform.OS === 'ios') {
+ store.subscribe(throttle(() => {
+ const state = store.getState();
+ if (state.entities) {
+ const channelsInTeam = {...state.entities.channels.channelsInTeam};
+ Object.keys(channelsInTeam).forEach((teamId) => {
+ channelsInTeam[teamId] = Array.from(channelsInTeam[teamId]);
+ });
+
+ const profilesInChannel = {...state.entities.users.profilesInChannel};
+ Object.keys(profilesInChannel).forEach((channelId) => {
+ profilesInChannel[channelId] = Array.from(profilesInChannel[channelId]);
+ });
+
+ const entities = {
+ ...state.entities,
+ channels: {
+ ...state.entities.channels,
+ channelsInTeam,
+ },
+ users: {
+ ...state.entities.users,
+ profilesInChannel,
+ profilesNotInTeam: [],
+ profilesWithoutTeam: [],
+ profilesNotInChannel: [],
+ },
+ };
+ mattermostBucket.writeToFile('entities', JSON.stringify(entities));
+ }
+ }, 1000));
+ }
+
+ // check to see if the logout request was successful
+ store.subscribe(async () => {
+ const state = store.getState();
+ const config = getConfig(state);
+
+ if (getSiteUrl() !== config?.SiteURL) {
+ setSiteUrl(config.SiteURL);
+ }
+
+ if ((state.requests.users.logout.status === RequestStatus.SUCCESS || state.requests.users.logout.status === RequestStatus.FAILURE) && !purging) {
+ purging = true;
+
+ await persistor.purge();
+
+ store.dispatch(batchActions([
+ {
+ type: General.OFFLINE_STORE_RESET,
+ data: initialState,
+ },
+ {
+ type: General.STORE_REHYDRATION_COMPLETE,
+ },
+ {
+ type: ViewTypes.SERVER_URL_CHANGED,
+ serverUrl: state.entities.general.credentials.url || state.views.selectServer.serverUrl,
+ },
+ {
+ type: GeneralTypes.RECEIVED_APP_DEVICE_TOKEN,
+ data: state.entities.general.deviceToken,
+ },
+ ]));
+
+ // When logging out remove the data stored in the bucket
+ mattermostBucket.removePreference('cert');
+ mattermostBucket.removePreference('emm');
+ mattermostBucket.removeFile('entities');
+ setSiteUrl(null);
+
+ setTimeout(() => {
+ purging = false;
+ }, 500);
+ } else if (state.views.root.purge && !purging) {
+ purging = true;
+
+ await persistor.purge();
+
+ store.dispatch(batchActions([
+ {
+ type: General.OFFLINE_STORE_RESET,
+ data: initialState,
+ },
+ {
+ type: ErrorTypes.RESTORE_ERRORS,
+ data: [...state.errors],
+ },
+ {
+ type: GeneralTypes.RECEIVED_APP_DEVICE_TOKEN,
+ data: state.entities.general.deviceToken,
+ },
+ {
+ type: GeneralTypes.RECEIVED_APP_CREDENTIALS,
+ data: {
+ url: state.entities.general.credentials.url,
+ },
+ },
+ {
+ type: ViewTypes.SERVER_URL_CHANGED,
+ serverUrl: state.entities.general.credentials.url || state.views.selectServer.serverUrl,
+ },
+ {
+ type: GeneralTypes.RECEIVED_SERVER_VERSION,
+ data: state.entities.general.serverVersion,
+ },
+ {
+ type: General.STORE_REHYDRATION_COMPLETE,
+ },
+ ], 'BATCH_FOR_RESTART'));
+
+ setTimeout(() => {
+ purging = false;
+ EventEmitter.emit(NavigationTypes.RESTART_APP);
+ }, 500);
+ }
+ });
+
+ return persistor;
+ },
+ persistOptions: {
+ autoRehydrate: {
+ log: false,
+ },
+ blacklist: ['device', 'navigation', 'offline', 'requests'],
+ debounce: 500,
+ transforms: [
+ setTransformer,
+ viewsBlackListFilter,
+ typingBlackListFilter,
+ channelViewBlackListFilter,
+ emojiBlackListFilter,
+ ],
+ },
+ };
+
+ const clientOptions = {
+ additionalMiddleware: [
+ createThunkMiddleware(),
+ createSentryMiddleware(),
+ messageRetention,
+ ],
+ enableThunk: false, // We override the default thunk middleware
+ };
+
+ return configureStore(initialState, appReducer, offlineOptions, getAppReducer, clientOptions);
+}
diff --git a/app/telemetry/telemetry.android.js b/app/telemetry/telemetry.android.js
index cd6dd4fc4..903d6543b 100644
--- a/app/telemetry/telemetry.android.js
+++ b/app/telemetry/telemetry.android.js
@@ -3,7 +3,7 @@
import LocalConfig from 'assets/config'; // eslint-disable-line
-import {store} from 'app/mattermost';
+import store from 'app/store';
import {
saveToTelemetryServer,
diff --git a/app/utils/bottom_sheet/bottom_sheet.android.js b/app/utils/bottom_sheet/bottom_sheet.android.js
index e45100b26..2a1548527 100644
--- a/app/utils/bottom_sheet/bottom_sheet.android.js
+++ b/app/utils/bottom_sheet/bottom_sheet.android.js
@@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {store} from 'app/mattermost';
import {showModalOverCurrentContext} from 'app/actions/navigation';
export default {
@@ -15,6 +14,6 @@ export default {
text: o,
}));
- store.dispatch(showModalOverCurrentContext('OptionsModal', {title: '', items}));
+ showModalOverCurrentContext('OptionsModal', {title: '', items});
},
};
diff --git a/app/utils/images.js b/app/utils/images.js
index c231e2ca2..104779914 100644
--- a/app/utils/images.js
+++ b/app/utils/images.js
@@ -7,6 +7,7 @@ import {
IMAGE_MAX_HEIGHT,
IMAGE_MIN_DIMENSION,
} from 'app/constants/image';
+import {showModalOverCurrentContext} from 'app/actions/navigation';
let previewComponents;
@@ -57,7 +58,7 @@ export const calculateDimensions = (height, width, viewPortWidth = 0, viewPortHe
};
};
-export function previewImageAtIndex(components, index, files, showModalOverCurrentContext) {
+export function previewImageAtIndex(components, index, files) {
previewComponents = components;
const component = components[index];
if (component) {
diff --git a/app/utils/push_notifications.js b/app/utils/push_notifications.js
index 3a7529959..84e726294 100644
--- a/app/utils/push_notifications.js
+++ b/app/utils/push_notifications.js
@@ -16,6 +16,7 @@ import {
loadFromPushNotification,
} from 'app/actions/views/root';
import {dismissAllModals, popToRoot} from 'app/actions/navigation';
+
import {ViewTypes} from 'app/constants';
import {getLocalizedMessage} from 'app/i18n';
import {getCurrentServerUrl, getAppCredentials} from 'app/init/credentials';
@@ -54,9 +55,8 @@ class PushNotificationUtils {
EventEmitter.emit('close_channel_drawer');
EventEmitter.emit('close_settings_sidebar');
- const {dispatch} = this.store;
- dispatch(dismissAllModals());
- dispatch(popToRoot());
+ await dismissAllModals();
+ await popToRoot();
PushNotifications.resetNotification();
}
diff --git a/app/utils/theme.js b/app/utils/theme.js
index 8d21d9aef..ee4ea9cba 100644
--- a/app/utils/theme.js
+++ b/app/utils/theme.js
@@ -2,12 +2,12 @@
// See LICENSE.txt for license information.
import {StyleSheet} from 'react-native';
-import {Navigation} from 'react-native-navigation';
-
import tinyColor from 'tinycolor2';
import * as ThemeUtils from 'mattermost-redux/utils/theme_utils';
+import {mergeNavigationOptions} from 'app/actions/navigation';
+
export function makeStyleSheetFromTheme(getStyleFromTheme) {
return ThemeUtils.makeStyleFromTheme((theme) => {
return StyleSheet.create(getStyleFromTheme(theme));
@@ -23,7 +23,7 @@ export function concatStyles(...styles) {
}
export function setNavigatorStyles(componentId, theme) {
- Navigation.mergeOptions(componentId, {
+ const options = {
topBar: {
title: {
color: theme.sidebarHeaderTextColor,
@@ -40,7 +40,9 @@ export function setNavigatorStyles(componentId, theme) {
layout: {
backgroundColor: theme.centerChannelBg,
},
- });
+ };
+
+ mergeNavigationOptions(componentId, options);
}
export function isThemeSwitchingEnabled(state) {
diff --git a/package-lock.json b/package-lock.json
index 7ff0e53b7..7f058c820 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5612,7 +5612,6 @@
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
- "optional": true,
"requires": {
"is-extendable": "^0.1.0"
}
@@ -5657,8 +5656,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
- "dev": true,
- "optional": true
+ "dev": true
},
"is-glob": {
"version": "4.0.1",
@@ -8446,8 +8444,7 @@
"ansi-regex": {
"version": "2.1.1",
"resolved": false,
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "optional": true
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"aproba": {
"version": "1.2.0",
@@ -8468,14 +8465,12 @@
"balanced-match": {
"version": "1.0.0",
"resolved": false,
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
- "optional": true
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -8490,20 +8485,17 @@
"code-point-at": {
"version": "1.1.0",
"resolved": false,
- "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
- "optional": true
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"concat-map": {
"version": "0.0.1",
"resolved": false,
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
- "optional": true
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"console-control-strings": {
"version": "1.1.0",
"resolved": false,
- "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
- "optional": true
+ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"core-util-is": {
"version": "1.0.2",
@@ -8620,8 +8612,7 @@
"inherits": {
"version": "2.0.3",
"resolved": false,
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
- "optional": true
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ini": {
"version": "1.3.5",
@@ -8633,7 +8624,6 @@
"version": "1.0.0",
"resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -8648,7 +8638,6 @@
"version": "3.0.4",
"resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
- "optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -8656,14 +8645,12 @@
"minimist": {
"version": "0.0.8",
"resolved": false,
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "optional": true
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"minipass": {
"version": "2.3.5",
"resolved": false,
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
- "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -8682,7 +8669,6 @@
"version": "0.5.1",
"resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -8763,8 +8749,7 @@
"number-is-nan": {
"version": "1.0.1",
"resolved": false,
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
- "optional": true
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"object-assign": {
"version": "4.1.1",
@@ -8776,7 +8761,6 @@
"version": "1.4.0",
"resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "optional": true,
"requires": {
"wrappy": "1"
}
@@ -8862,8 +8846,7 @@
"safe-buffer": {
"version": "5.1.2",
"resolved": false,
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "optional": true
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
@@ -8899,7 +8882,6 @@
"version": "1.0.2",
"resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -8919,7 +8901,6 @@
"version": "3.0.1",
"resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -8963,14 +8944,12 @@
"wrappy": {
"version": "1.0.2",
"resolved": false,
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
- "optional": true
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"yallist": {
"version": "3.0.3",
"resolved": false,
- "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
- "optional": true
+ "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
}
}
},
@@ -17099,8 +17078,7 @@
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
- "dev": true,
- "optional": true
+ "dev": true
},
"braces": {
"version": "2.3.2",
@@ -17363,8 +17341,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
- "dev": true,
- "optional": true
+ "dev": true
},
"micromatch": {
"version": "3.1.10",
diff --git a/native_modules/react-native-haptic-feedback+1.8.2.patch b/patches/react-native-haptic-feedback+1.8.2.patch
similarity index 100%
rename from native_modules/react-native-haptic-feedback+1.8.2.patch
rename to patches/react-native-haptic-feedback+1.8.2.patch
diff --git a/patches/react-native-navigation+2.21.1.patch b/patches/react-native-navigation+2.21.1.patch
new file mode 100644
index 000000000..842f41e72
--- /dev/null
+++ b/patches/react-native-navigation+2.21.1.patch
@@ -0,0 +1,102 @@
+diff --git a/node_modules/react-native-navigation/lib/ios/RNNCommandsHandler.m b/node_modules/react-native-navigation/lib/ios/RNNCommandsHandler.m
+index 91b878d..9cd61d5 100644
+--- a/node_modules/react-native-navigation/lib/ios/RNNCommandsHandler.m
++++ b/node_modules/react-native-navigation/lib/ios/RNNCommandsHandler.m
+@@ -72,7 +72,7 @@ - (void)setRoot:(NSDictionary*)layout commandId:(NSString*)commandId completion:
+ }
+ }
+
+- [_modalManager dismissAllModalsAnimated:NO];
++ [_modalManager dismissAllModalsAnimated:NO completion:nil];
+
+ UIViewController *vc = [_controllerFactory createLayout:layout[@"root"]];
+
+@@ -286,10 +286,9 @@ - (void)dismissAllModals:(NSDictionary *)mergeOptions commandId:(NSString*)comma
+ [CATransaction begin];
+ [CATransaction setCompletionBlock:^{
+ [_eventEmitter sendOnNavigationCommandCompletion:dismissAllModals commandId:commandId params:@{}];
+- completion();
+ }];
+ RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:mergeOptions];
+- [_modalManager dismissAllModalsAnimated:[options.animations.dismissModal.enable getWithDefaultValue:YES]];
++ [_modalManager dismissAllModalsAnimated:[options.animations.dismissModal.enable getWithDefaultValue:YES] completion:completion];
+
+ [CATransaction commit];
+ }
+diff --git a/node_modules/react-native-navigation/lib/ios/RNNModalManager.h b/node_modules/react-native-navigation/lib/ios/RNNModalManager.h
+index 9809db0..a602c92 100644
+--- a/node_modules/react-native-navigation/lib/ios/RNNModalManager.h
++++ b/node_modules/react-native-navigation/lib/ios/RNNModalManager.h
+@@ -19,6 +19,6 @@ typedef void (^RNNTransitionRejectionBlock)(NSString *code, NSString *message, N
+ - (void)showModal:(UIViewController *)viewController animated:(BOOL)animated completion:(RNNTransitionWithComponentIdCompletionBlock)completion;
+ - (void)showModal:(UIViewController *)viewController animated:(BOOL)animated hasCustomAnimation:(BOOL)hasCustomAnimation completion:(RNNTransitionWithComponentIdCompletionBlock)completion;
+ - (void)dismissModal:(UIViewController *)viewController completion:(RNNTransitionCompletionBlock)completion;
+-- (void)dismissAllModalsAnimated:(BOOL)animated;
++- (void)dismissAllModalsAnimated:(BOOL)animated completion:(RNNTransitionCompletionBlock)completion;
+
+ @end
+diff --git a/node_modules/react-native-navigation/lib/ios/RNNModalManager.m b/node_modules/react-native-navigation/lib/ios/RNNModalManager.m
+index 18c0eb8..a404da7 100644
+--- a/node_modules/react-native-navigation/lib/ios/RNNModalManager.m
++++ b/node_modules/react-native-navigation/lib/ios/RNNModalManager.m
+@@ -50,9 +50,16 @@ - (void)dismissModal:(UIViewController *)viewController completion:(RNNTransitio
+ }
+ }
+
+--(void)dismissAllModalsAnimated:(BOOL)animated {
++-(void)dismissAllModalsAnimated:(BOOL)animated completion:(RNNTransitionCompletionBlock)completion {
++ if (!_presentedModals || !_presentedModals.count) {
++ if (completion) {
++ completion();
++ }
++ return;
++ }
++
+ UIViewController *root = UIApplication.sharedApplication.delegate.window.rootViewController;
+- [root dismissViewControllerAnimated:animated completion:nil];
++ [root dismissViewControllerAnimated:animated completion:completion];
+ [_delegate dismissedMultipleModals:_presentedModals];
+ [_pendingModalIdsToDismiss removeAllObjects];
+ [_presentedModals removeAllObjects];
+diff --git a/node_modules/react-native-navigation/lib/ios/ReactNativeNavigation.xcodeproj/xcuserdata/miguel.xcuserdatad/xcschemes/xcschememanagement.plist b/node_modules/react-native-navigation/lib/ios/ReactNativeNavigation.xcodeproj/xcuserdata/miguel.xcuserdatad/xcschemes/xcschememanagement.plist
+new file mode 100644
+index 0000000..a2f2607
+--- /dev/null
++++ b/node_modules/react-native-navigation/lib/ios/ReactNativeNavigation.xcodeproj/xcuserdata/miguel.xcuserdatad/xcschemes/xcschememanagement.plist
+@@ -0,0 +1,14 @@
++
++
++
++
++ SchemeUserState
++
++ ReactNativeNavigation.xcscheme_^#shared#^_
++
++ orderHint
++ 6
++
++
++
++
+diff --git a/node_modules/react-native-navigation/lib/ios/ReactNativeNavigationTests/RNNModalManagerTest.m b/node_modules/react-native-navigation/lib/ios/ReactNativeNavigationTests/RNNModalManagerTest.m
+index 407051a..8f02aae 100644
+--- a/node_modules/react-native-navigation/lib/ios/ReactNativeNavigationTests/RNNModalManagerTest.m
++++ b/node_modules/react-native-navigation/lib/ios/ReactNativeNavigationTests/RNNModalManagerTest.m
+@@ -51,7 +51,7 @@ - (void)testDismissMultipleModalsInvokeDelegateWithCorrectParameters {
+ [_modalManager showModal:_vc3 animated:NO completion:nil];
+
+ _modalManager.delegate = self;
+- [_modalManager dismissAllModalsAnimated:NO];
++ [_modalManager dismissAllModalsAnimated:NO completion:nil];
+
+ XCTAssertTrue(_modalDismissedCount == 3);
+ }
+@@ -87,7 +87,7 @@ - (void)testDismissAllModals_AfterDismissingPreviousModal_InvokeDelegateWithCorr
+ [_modalManager dismissModal:_vc2 completion:nil];
+
+ XCTAssertTrue(_modalDismissedCount == 1);
+- [_modalManager dismissAllModalsAnimated:NO];
++ [_modalManager dismissAllModalsAnimated:NO completion:nil];
+ XCTAssertTrue(_modalDismissedCount == 2);
+ }
+
diff --git a/share_extension/android/index.js b/share_extension/android/index.js
index 305a28627..f865125ce 100644
--- a/share_extension/android/index.js
+++ b/share_extension/android/index.js
@@ -7,7 +7,7 @@ import {IntlProvider} from 'react-intl';
import {getTranslations} from 'app/i18n';
import {getCurrentLocale} from 'app/selectors/i18n';
-import {store} from 'app/mattermost';
+import store from 'app/store';
import {extensionSelectTeamId} from './actions';
import Extension from './extension';
diff --git a/test/setup.js b/test/setup.js
index 48a6ea4e4..8dfb3aa00 100644
--- a/test/setup.js
+++ b/test/setup.js
@@ -9,6 +9,7 @@ configure({adapter: new Adapter()});
const mockImpl = new MockAsyncStorage();
jest.mock('@react-native-community/async-storage', () => mockImpl);
global.window = {};
+global.fetch = jest.fn(() => Promise.resolve());
/* eslint-disable no-console */
@@ -72,6 +73,49 @@ jest.mock('react-native-cookies', () => ({
getInitialURL: jest.fn(),
}));
+jest.mock('react-native-navigation', () => {
+ const RNN = require.requireActual('react-native-navigation');
+ return {
+ ...RNN,
+ Navigation: {
+ ...RNN.Navigation,
+ events: () => ({
+ registerAppLaunchedListener: jest.fn(),
+ bindComponent: jest.fn(),
+ }),
+ setRoot: jest.fn(),
+ pop: jest.fn(),
+ push: jest.fn(),
+ showModal: jest.fn(),
+ dismissModal: jest.fn(),
+ dismissAllModals: jest.fn(),
+ popToRoot: jest.fn(),
+ mergeOptions: jest.fn(),
+ showOverlay: jest.fn(),
+ dismissOverlay: jest.fn(),
+ },
+ };
+});
+
+jest.mock('app/actions/navigation', () => ({
+ resetToChannel: jest.fn(),
+ resetToSelectServer: jest.fn(),
+ resetToTeams: jest.fn(),
+ goToScreen: jest.fn(),
+ popTopScreen: jest.fn(),
+ showModal: jest.fn(),
+ showModalOverCurrentContext: jest.fn(),
+ showSearchModal: jest.fn(),
+ peek: jest.fn(),
+ setButtons: jest.fn(),
+ showOverlay: jest.fn(),
+ mergeNavigationOptions: jest.fn(),
+ popToRoot: jest.fn(() => Promise.resolve()),
+ dismissModal: jest.fn(() => Promise.resolve()),
+ dismissAllModals: jest.fn(() => Promise.resolve()),
+ dismissOverlay: jest.fn(() => Promise.resolve()),
+}));
+
let logs;
let warns;
let errors;