MM-23848 consolidate store, upgrade mmkv and fix logout (#4145)
* MM-23848 consolidate store, upgrade mmkv and fix logout * Feedback review * Add store.ts to modulesPath
This commit is contained in:
parent
3a887b446a
commit
0e2bc65d70
61 changed files with 1053 additions and 1677 deletions
|
|
@ -7,13 +7,13 @@ import merge from 'deepmerge';
|
|||
|
||||
import {getTheme} from '@mm-redux/selectors/entities/preferences';
|
||||
|
||||
import store from 'app/store';
|
||||
import EphemeralStore from 'app/store/ephemeral_store';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import Store from '@store/store';
|
||||
|
||||
const CHANNEL_SCREEN = 'Channel';
|
||||
|
||||
function getThemeFromState() {
|
||||
const state = store.getState();
|
||||
const state = Store.redux?.getState() || {};
|
||||
|
||||
return getTheme(state);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,21 +3,27 @@
|
|||
|
||||
import {Platform} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import merge from 'deepmerge';
|
||||
|
||||
import * as NavigationActions from '@actions/navigation';
|
||||
import Preferences from '@mm-redux/constants/preferences';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import intitialState from '@store/initial_state';
|
||||
import Store from '@store/store';
|
||||
|
||||
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', () => ({
|
||||
jest.unmock('@actions/navigation');
|
||||
jest.mock('@store/ephemeral_store', () => ({
|
||||
getNavigationTopComponentId: jest.fn(),
|
||||
clearNavigationComponents: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('app/actions/navigation', () => {
|
||||
const mockStore = configureMockStore([thunk]);
|
||||
const store = mockStore(intitialState);
|
||||
Store.redux = store;
|
||||
|
||||
describe('@actions/navigation', () => {
|
||||
const topComponentId = 'top-component-id';
|
||||
const name = 'name';
|
||||
const title = 'title';
|
||||
|
|
|
|||
|
|
@ -4,20 +4,20 @@
|
|||
import configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
import initialState from 'app/initial_state';
|
||||
import {ChannelTypes} from '@mm-redux/action_types';
|
||||
import testHelper from 'test/test_helper';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import * as ChannelActions from 'app/actions/views/channel';
|
||||
import * as ChannelActions from '@actions/views/channel';
|
||||
import {ViewTypes} from '@constants';
|
||||
import {ChannelTypes} from '@mm-redux/action_types';
|
||||
import postReducer from '@mm-redux/reducers/entities/posts';
|
||||
import initialState from '@store/initial_state';
|
||||
|
||||
const {
|
||||
handleSelectChannel,
|
||||
handleSelectChannelByName,
|
||||
loadPostsIfNecessaryWithRetry,
|
||||
} = ChannelActions;
|
||||
|
||||
import postReducer from '@mm-redux/reducers/entities/posts';
|
||||
|
||||
const MOCK_CHANNEL_MARK_AS_READ = 'MOCK_CHANNEL_MARK_AS_READ';
|
||||
const MOCK_CHANNEL_MARK_AS_VIEWED = 'MOCK_CHANNEL_MARK_AS_VIEWED';
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import {PostTypes, UserTypes} from '@mm-redux/action_types';
|
|||
import * as PostSelectors from '@mm-redux/selectors/entities/posts';
|
||||
import * as ChannelUtils from '@mm-redux/utils/channel_utils';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import initialState from 'app/initial_state';
|
||||
import {ViewTypes} from '@constants';
|
||||
import initialState from '@store/initial_state';
|
||||
|
||||
import {loadUnreadChannelPosts} from '@actions/views/post';
|
||||
|
||||
|
|
|
|||
|
|
@ -13,11 +13,10 @@ import {General} from '@mm-redux/constants';
|
|||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
|
||||
import {NavigationTypes, ViewTypes} from '@constants';
|
||||
import initialState from 'app/initial_state';
|
||||
import {persistor} from 'app/store';
|
||||
import EphemeralStore from 'app/store/ephemeral_store';
|
||||
import {getStateForReset} from 'app/store/utils';
|
||||
import {recordTime} from 'app/utils/segment';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import initialState from '@store/initial_state';
|
||||
import {getStateForReset} from '@store/utils';
|
||||
import {recordTime} from '@utils/segment';
|
||||
|
||||
import {markChannelViewedAndRead} from './channel';
|
||||
|
||||
|
|
@ -144,13 +143,11 @@ export function handleSelectTeamAndChannel(teamId, channelId) {
|
|||
export function purgeOfflineStore() {
|
||||
return (dispatch, getState) => {
|
||||
const currentState = getState();
|
||||
persistor.purge();
|
||||
|
||||
dispatch({
|
||||
type: General.OFFLINE_STORE_PURGE,
|
||||
state: getStateForReset(initialState, currentState),
|
||||
});
|
||||
persistor.flush();
|
||||
persistor.persist();
|
||||
|
||||
EventEmitter.emit(NavigationTypes.RESTART_APP);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@ import EventEmitter from '@mm-redux/utils/event_emitter';
|
|||
|
||||
import * as Actions from '@actions/websocket';
|
||||
import {WebsocketEvents} from '@constants';
|
||||
import initial_state from '@store/initial_state';
|
||||
|
||||
import TestHelper from 'test/test_helper';
|
||||
import configureStore from 'test/test_store';
|
||||
import initial_state from 'app/initial_state';
|
||||
|
||||
global.WebSocket = MockWebSocket;
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ export const removeAppCredentials = async () => {
|
|||
KeyChain.resetInternetCredentials(url);
|
||||
}
|
||||
|
||||
KeyChain.resetGenericPassword();
|
||||
EphemeralStore.currentServerUrl = null;
|
||||
AsyncStorage.removeItem(CURRENT_SERVER);
|
||||
};
|
||||
|
|
@ -90,9 +89,7 @@ async function getCredentialsFromGenericKeyChain() {
|
|||
Client4.setToken(token);
|
||||
await setCSRFFromCookie(url);
|
||||
|
||||
// Migration: remove the generic credentials and add a server specific one
|
||||
setAppCredentials(deviceToken, currentUserId, token, url);
|
||||
KeyChain.resetGenericPassword();
|
||||
|
||||
return {
|
||||
username: usernameParsed,
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@
|
|||
|
||||
import {Alert, Platform} from 'react-native';
|
||||
|
||||
import {setServerUrl} from 'app/actions/views/select_server';
|
||||
import {getTranslations} from 'app/i18n';
|
||||
import {setServerUrl} from '@actions/views/select_server';
|
||||
import {getTranslations} from '@i18n';
|
||||
import {getCurrentLocale} from '@selectors/i18n';
|
||||
import Store from '@store/store';
|
||||
import {t} from '@utils/i18n';
|
||||
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
import mattermostManaged from 'app/mattermost_managed';
|
||||
import {getCurrentLocale} from 'app/selectors/i18n';
|
||||
import {t} from 'app/utils/i18n';
|
||||
|
||||
import {getAppCredentials} from './credentials';
|
||||
|
||||
|
|
@ -29,11 +31,11 @@ class EMMProvider {
|
|||
this.emmServerUrl = null;
|
||||
}
|
||||
|
||||
checkIfDeviceIsTrusted = (store) => {
|
||||
checkIfDeviceIsTrusted = () => {
|
||||
const isTrusted = mattermostManaged.isTrustedDevice();
|
||||
|
||||
if (!isTrusted) {
|
||||
const state = store.getState();
|
||||
const state = Store.redux.getState();
|
||||
const locale = getCurrentLocale(state);
|
||||
const translations = getTranslations(locale);
|
||||
Alert.alert(
|
||||
|
|
@ -51,10 +53,10 @@ class EMMProvider {
|
|||
}
|
||||
};
|
||||
|
||||
handleAuthentication = async (store, prompt = true) => {
|
||||
handleAuthentication = async (prompt = true) => {
|
||||
this.performingAuthentication = true;
|
||||
const isSecured = await mattermostManaged.isDeviceSecure();
|
||||
const state = store.getState();
|
||||
const state = Store.redux.getState();
|
||||
const locale = getCurrentLocale(state);
|
||||
const translations = getTranslations(locale);
|
||||
|
||||
|
|
@ -83,12 +85,12 @@ class EMMProvider {
|
|||
return true;
|
||||
};
|
||||
|
||||
handleManagedConfig = async (store) => {
|
||||
handleManagedConfig = async () => {
|
||||
if (this.performingAuthentication) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const {dispatch} = store;
|
||||
const {dispatch} = Store.redux;
|
||||
|
||||
if (LocalConfig.AutoSelectServerUrl) {
|
||||
dispatch(setServerUrl(LocalConfig.DefaultServerUrl));
|
||||
|
|
|
|||
|
|
@ -29,11 +29,12 @@ import {NavigationTypes, ViewTypes} from '@constants';
|
|||
import {getTranslations, resetMomentLocale} from '@i18n';
|
||||
import PushNotifications from 'app/push_notifications';
|
||||
import {getCurrentLocale} from '@selectors/i18n';
|
||||
import initialState from '@store/initial_state';
|
||||
import Store from '@store/store';
|
||||
import {t} from '@utils/i18n';
|
||||
import {deleteFileCache} from '@utils/file';
|
||||
import {getDeviceTimezoneAsync} from '@utils/timezone';
|
||||
|
||||
import initialState from 'app/initial_state';
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
import mattermostManaged from 'app/mattermost_managed';
|
||||
import LocalConfig from 'assets/config';
|
||||
|
|
@ -66,7 +67,7 @@ class GlobalEventHandler {
|
|||
|
||||
// Once the app becomes active we check if the device needs to have a passcode set
|
||||
const prompt = emmProvider.inBackgroundSince && authExpired; // if more than 5 minutes have passed prompt for passcode
|
||||
await emmProvider.handleAuthentication(this.store, prompt);
|
||||
await emmProvider.handleAuthentication(prompt);
|
||||
}
|
||||
|
||||
emmProvider.inBackgroundSince = null; /* eslint-disable-line require-atomic-updates */
|
||||
|
|
@ -75,7 +76,7 @@ class GlobalEventHandler {
|
|||
appInactive = () => {
|
||||
this.turnOffInAppNotificationHandling();
|
||||
|
||||
const {dispatch} = this.store;
|
||||
const {dispatch} = Store.redux;
|
||||
|
||||
// When the app is sent to the background we set the time when that happens
|
||||
// and perform a data clean up to improve on performance
|
||||
|
|
@ -85,7 +86,6 @@ class GlobalEventHandler {
|
|||
};
|
||||
|
||||
configure = (opts) => {
|
||||
this.store = opts.store;
|
||||
this.launchApp = opts.launchApp;
|
||||
|
||||
// onAppStateChange may be called by the AppState listener before we
|
||||
|
|
@ -107,7 +107,7 @@ class GlobalEventHandler {
|
|||
}
|
||||
|
||||
this.JavascriptAndNativeErrorHandler = require('app/utils/error_handling').default;
|
||||
this.JavascriptAndNativeErrorHandler.initializeErrorHandling(this.store);
|
||||
this.JavascriptAndNativeErrorHandler.initializeErrorHandling(Store.redux);
|
||||
|
||||
mattermostManaged.addEventListener('managedConfigDidChange', this.onManagedConfigurationChange);
|
||||
};
|
||||
|
|
@ -126,8 +126,8 @@ class GlobalEventHandler {
|
|||
const isActive = appState === 'active';
|
||||
const isBackground = appState === 'background';
|
||||
|
||||
if (this.store) {
|
||||
this.store.dispatch(setAppState(isActive));
|
||||
if (Store.redux) {
|
||||
Store.redux.dispatch(setAppState(isActive));
|
||||
|
||||
if (isActive && (!emmProvider.enabled || emmProvider.previousAppState === 'background')) {
|
||||
this.appActive();
|
||||
|
|
@ -142,12 +142,12 @@ class GlobalEventHandler {
|
|||
onDeepLink = (event) => {
|
||||
const {url} = event;
|
||||
if (url) {
|
||||
this.store.dispatch(setDeepLinkURL(url));
|
||||
Store.redux.dispatch(setDeepLinkURL(url));
|
||||
}
|
||||
};
|
||||
|
||||
onManagedConfigurationChange = () => {
|
||||
emmProvider.handleManagedConfig(this.store, true);
|
||||
emmProvider.handleManagedConfig(true);
|
||||
};
|
||||
|
||||
onServerConfigChanged = (config) => {
|
||||
|
|
@ -155,9 +155,9 @@ class GlobalEventHandler {
|
|||
};
|
||||
|
||||
onLogout = async () => {
|
||||
this.store.dispatch(closeWebSocket(false));
|
||||
this.store.dispatch(setServerVersion(''));
|
||||
this.resetState();
|
||||
Store.redux.dispatch(closeWebSocket(false));
|
||||
Store.redux.dispatch(setServerVersion(''));
|
||||
await this.resetState();
|
||||
removeAppCredentials();
|
||||
deleteFileCache();
|
||||
resetMomentLocale();
|
||||
|
|
@ -190,8 +190,8 @@ class GlobalEventHandler {
|
|||
};
|
||||
|
||||
onOrientationChange = (dimensions) => {
|
||||
if (this.store) {
|
||||
const {dispatch, getState} = this.store;
|
||||
if (Store.redux) {
|
||||
const {dispatch, getState} = Store.redux;
|
||||
const deviceState = getState().device;
|
||||
|
||||
if (DeviceInfo.isTablet()) {
|
||||
|
|
@ -215,7 +215,7 @@ class GlobalEventHandler {
|
|||
};
|
||||
|
||||
onRestartApp = async () => {
|
||||
const {dispatch, getState} = this.store;
|
||||
const {dispatch, getState} = Store.redux;
|
||||
const state = getState();
|
||||
const {currentUserId} = state.entities.users;
|
||||
const user = getUser(state, currentUserId);
|
||||
|
|
@ -236,7 +236,7 @@ class GlobalEventHandler {
|
|||
};
|
||||
|
||||
onServerVersionChanged = async (serverVersion) => {
|
||||
const {dispatch, getState} = this.store;
|
||||
const {dispatch, getState} = Store.redux;
|
||||
const state = getState();
|
||||
const match = serverVersion && serverVersion.match(/^[0-9]*.[0-9]*.[0-9]*(-[a-zA-Z0-9.-]*)?/g);
|
||||
const version = match && match[0];
|
||||
|
|
@ -263,17 +263,17 @@ class GlobalEventHandler {
|
|||
};
|
||||
|
||||
onStatusBarHeightChange = (nextStatusBarHeight) => {
|
||||
this.store.dispatch(setStatusBarHeight(nextStatusBarHeight));
|
||||
Store.redux.dispatch(setStatusBarHeight(nextStatusBarHeight));
|
||||
};
|
||||
|
||||
onSwitchToDefaultChannel = (teamId) => {
|
||||
this.store.dispatch(selectDefaultChannel(teamId));
|
||||
Store.redux.dispatch(selectDefaultChannel(teamId));
|
||||
};
|
||||
|
||||
resetState = async () => {
|
||||
try {
|
||||
await AsyncStorage.clear();
|
||||
const state = this.store.getState();
|
||||
const state = Store.redux.getState();
|
||||
const newState = {
|
||||
...initialState,
|
||||
app: {
|
||||
|
|
@ -298,19 +298,23 @@ class GlobalEventHandler {
|
|||
serverUrl: state.views.selectServer.serverUrl,
|
||||
},
|
||||
},
|
||||
_persist: {
|
||||
rehydrated: true,
|
||||
},
|
||||
};
|
||||
|
||||
this.store.dispatch({
|
||||
return Store.redux.dispatch({
|
||||
type: General.OFFLINE_STORE_PURGE,
|
||||
state: newState,
|
||||
});
|
||||
} catch (e) {
|
||||
// clear error
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
serverUpgradeNeeded = async () => {
|
||||
const {dispatch} = this.store;
|
||||
const {dispatch} = Store.redux;
|
||||
|
||||
dispatch(setServerVersion(''));
|
||||
Client4.serverVersion = '';
|
||||
|
|
@ -332,7 +336,7 @@ class GlobalEventHandler {
|
|||
|
||||
handleInAppNotification = (notification) => {
|
||||
const {data} = notification;
|
||||
const {getState} = this.store;
|
||||
const {getState} = Store.redux;
|
||||
const state = getState();
|
||||
const currentChannelId = getCurrentChannelId(state);
|
||||
|
||||
|
|
@ -348,7 +352,7 @@ class GlobalEventHandler {
|
|||
};
|
||||
|
||||
setUserTimezone = async () => {
|
||||
const {dispatch, getState} = this.store;
|
||||
const {dispatch, getState} = Store.redux;
|
||||
const state = getState();
|
||||
const currentUserId = getCurrentUserId(state);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ import configureMockStore from 'redux-mock-store';
|
|||
import thunk from 'redux-thunk';
|
||||
import semver from 'semver/preload';
|
||||
|
||||
import intitialState from 'app/initial_state';
|
||||
import PushNotification from 'app/push_notifications';
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
import * as I18n from 'app/i18n';
|
||||
import * as I18n from '@i18n';
|
||||
import Store from '@store/store';
|
||||
import intitialState from '@store/initial_state';
|
||||
|
||||
import {MinServerVersion} from 'assets/config';
|
||||
|
||||
|
|
@ -51,7 +52,7 @@ jest.mock('app/actions/views/root', () => ({
|
|||
|
||||
const mockStore = configureMockStore([thunk]);
|
||||
const store = mockStore(intitialState);
|
||||
GlobalEventHandler.store = store;
|
||||
Store.redux = store;
|
||||
|
||||
// TODO: Add Android test as part of https://mattermost.atlassian.net/browse/MM-17110
|
||||
describe('GlobalEventHandler', () => {
|
||||
|
|
@ -71,15 +72,16 @@ describe('GlobalEventHandler', () => {
|
|||
it('should call onAppStateChange after configuration', () => {
|
||||
const onAppStateChange = jest.spyOn(GlobalEventHandler, 'onAppStateChange');
|
||||
|
||||
GlobalEventHandler.configure({store});
|
||||
expect(GlobalEventHandler.store).not.toBeNull();
|
||||
Store.redux = store;
|
||||
GlobalEventHandler.configure({launchApp: jest.fn()});
|
||||
expect(Store.redux).not.toBeNull();
|
||||
expect(onAppStateChange).toHaveBeenCalledWith('active');
|
||||
});
|
||||
|
||||
it('should handle onAppStateChange to active if the store set', () => {
|
||||
const appActive = jest.spyOn(GlobalEventHandler, 'appActive');
|
||||
const appInactive = jest.spyOn(GlobalEventHandler, 'appInactive');
|
||||
expect(GlobalEventHandler.store).not.toBeNull();
|
||||
expect(Store.redux).not.toBeNull();
|
||||
|
||||
GlobalEventHandler.onAppStateChange('active');
|
||||
expect(appActive).toHaveBeenCalled();
|
||||
|
|
@ -89,7 +91,7 @@ describe('GlobalEventHandler', () => {
|
|||
it('should handle onAppStateChange to background if the store set', () => {
|
||||
const appActive = jest.spyOn(GlobalEventHandler, 'appActive');
|
||||
const appInactive = jest.spyOn(GlobalEventHandler, 'appInactive');
|
||||
expect(GlobalEventHandler.store).not.toBeNull();
|
||||
expect(Store.redux).not.toBeNull();
|
||||
|
||||
GlobalEventHandler.onAppStateChange('background');
|
||||
expect(appActive).not.toHaveBeenCalled();
|
||||
|
|
@ -99,7 +101,7 @@ describe('GlobalEventHandler', () => {
|
|||
it('should not handle onAppStateChange if the store is not set', () => {
|
||||
const appActive = jest.spyOn(GlobalEventHandler, 'appActive');
|
||||
const appInactive = jest.spyOn(GlobalEventHandler, 'appInactive');
|
||||
GlobalEventHandler.store = null;
|
||||
Store.redux = null;
|
||||
|
||||
GlobalEventHandler.onAppStateChange('active');
|
||||
expect(appActive).not.toHaveBeenCalled();
|
||||
|
|
@ -114,8 +116,9 @@ describe('GlobalEventHandler', () => {
|
|||
const onAppStateChange = jest.spyOn(GlobalEventHandler, 'onAppStateChange');
|
||||
const setUserTimezone = jest.spyOn(GlobalEventHandler, 'setUserTimezone');
|
||||
|
||||
GlobalEventHandler.configure({store});
|
||||
expect(GlobalEventHandler.store).not.toBeNull();
|
||||
Store.redux = store;
|
||||
GlobalEventHandler.configure({launchApp: jest.fn()});
|
||||
expect(Store.redux).not.toBeNull();
|
||||
expect(onAppStateChange).toHaveBeenCalledWith('active');
|
||||
expect(setUserTimezone).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
|
@ -127,7 +130,7 @@ describe('GlobalEventHandler', () => {
|
|||
|
||||
const minVersion = semver.parse(MinServerVersion);
|
||||
const currentUserId = 'current-user-id';
|
||||
GlobalEventHandler.store.getState = jest.fn().mockReturnValue({
|
||||
Store.redux.getState = jest.fn().mockReturnValue({
|
||||
entities: {
|
||||
users: {
|
||||
currentUserId,
|
||||
|
|
@ -137,9 +140,9 @@ describe('GlobalEventHandler', () => {
|
|||
},
|
||||
},
|
||||
});
|
||||
GlobalEventHandler.store.dispatch = jest.fn().mockReturnValue({});
|
||||
Store.redux.dispatch = jest.fn().mockReturnValue({});
|
||||
|
||||
const dispatch = jest.spyOn(GlobalEventHandler.store, 'dispatch');
|
||||
const dispatch = jest.spyOn(Store.redux, 'dispatch');
|
||||
const configureAnalytics = jest.spyOn(GlobalEventHandler, 'configureAnalytics');
|
||||
const alert = jest.spyOn(Alert, 'alert');
|
||||
|
||||
|
|
|
|||
|
|
@ -7,34 +7,38 @@ import {Provider} from 'react-redux';
|
|||
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
|
||||
import {loadMe} from 'app/actions/views/user';
|
||||
|
||||
import {resetToChannel, resetToSelectServer} from 'app/actions/navigation';
|
||||
import {setDeepLinkURL} from 'app/actions/views/root';
|
||||
import {NavigationTypes} from 'app/constants';
|
||||
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 store, {persistor} from 'app/store';
|
||||
import {waitForHydration} from 'app/store/utils';
|
||||
import EphemeralStore from 'app/store/ephemeral_store';
|
||||
import {resetToChannel, resetToSelectServer} from '@actions/navigation';
|
||||
import {setDeepLinkURL} from '@actions/views/root';
|
||||
import {loadMe, logout} from '@actions/views/user';
|
||||
import telemetry from 'app/telemetry';
|
||||
import {validatePreviousVersion} from 'app/utils/general';
|
||||
import pushNotificationsUtils from 'app/utils/push_notifications';
|
||||
import {NavigationTypes} from '@constants';
|
||||
import {getAppCredentials} from '@init/credentials';
|
||||
import emmProvider from '@init/emm_provider';
|
||||
import '@init/device';
|
||||
import '@init/fetch';
|
||||
import globalEventHandler from '@init/global_event_handler';
|
||||
import {registerScreens} from '@screens';
|
||||
import configureStore from '@store';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import getStorage from '@store/mmkv_adapter';
|
||||
import Store from '@store/store';
|
||||
import {waitForHydration} from '@store/utils';
|
||||
import {validatePreviousVersion} from '@utils/general';
|
||||
import pushNotificationsUtils from '@utils/push_notifications';
|
||||
|
||||
const init = async () => {
|
||||
const credentials = await getAppCredentials();
|
||||
const dt = Date.now();
|
||||
const MMKVStorage = await getStorage();
|
||||
|
||||
const {store} = configureStore(MMKVStorage);
|
||||
if (EphemeralStore.appStarted) {
|
||||
launchApp(credentials);
|
||||
return;
|
||||
}
|
||||
|
||||
pushNotificationsUtils.configure(store);
|
||||
pushNotificationsUtils.configure();
|
||||
globalEventHandler.configure({
|
||||
store,
|
||||
launchApp,
|
||||
});
|
||||
|
||||
|
|
@ -52,10 +56,9 @@ const launchApp = (credentials) => {
|
|||
]);
|
||||
|
||||
if (credentials) {
|
||||
waitForHydration(store, () => {
|
||||
const valid = validatePreviousVersion(store);
|
||||
if (valid) {
|
||||
store.dispatch(loadMe());
|
||||
waitForHydration(Store.redux, async () => {
|
||||
if (validatePreviousVersion(Store.redux, EphemeralStore.prevAppVersion)) {
|
||||
Store.redux.dispatch(loadMe());
|
||||
resetToChannel({skipMetrics: true});
|
||||
}
|
||||
});
|
||||
|
|
@ -68,13 +71,13 @@ const launchApp = (credentials) => {
|
|||
|
||||
Linking.getInitialURL().then((url) => {
|
||||
if (url) {
|
||||
store.dispatch(setDeepLinkURL(url));
|
||||
Store.redux.dispatch(setDeepLinkURL(url));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const launchAppAndAuthenticateIfNeeded = async (credentials) => {
|
||||
await emmProvider.handleManagedConfig(store);
|
||||
await emmProvider.handleManagedConfig();
|
||||
await launchApp(credentials);
|
||||
|
||||
if (emmProvider.enabled) {
|
||||
|
|
@ -83,7 +86,7 @@ const launchAppAndAuthenticateIfNeeded = async (credentials) => {
|
|||
}
|
||||
|
||||
if (emmProvider.inAppPinCode) {
|
||||
await emmProvider.handleAuthentication(store);
|
||||
await emmProvider.handleAuthentication();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import configureStore from 'test/test_store';
|
|||
import deepFreeze from '@mm-redux/utils/deep_freeze';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
|
||||
import initialState from '@mm-redux/store/initial_state';
|
||||
import initialState from '@store/initial_state';
|
||||
|
||||
const OK_RESPONSE = {status: 'OK'};
|
||||
const UNAUTHORIZED = {status_code: 401};
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ import * as client from './client';
|
|||
import * as constants from './constants';
|
||||
import * as reducers from './reducers';
|
||||
import * as selectors from './selectors';
|
||||
import store from './store';
|
||||
import * as types from './types';
|
||||
import * as utils from './utils';
|
||||
|
||||
export {action_types, actions, client, constants, reducers, selectors, store, types, utils};
|
||||
export {action_types, actions, client, constants, reducers, selectors, types, utils};
|
||||
|
|
|
|||
|
|
@ -1,108 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import * as redux from 'redux';
|
||||
import {persistReducer, persistStore, Persistor, createPersistoid} from 'redux-persist';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
|
||||
import {General} from '@mm-redux/constants';
|
||||
import serviceReducer from '@mm-redux/reducers';
|
||||
|
||||
import {createReducer, getStoredState} from './helpers';
|
||||
import initialState from './initial_state';
|
||||
import {createMiddleware} from './middleware';
|
||||
import reducerRegistry from './reducer_registry';
|
||||
|
||||
/**
|
||||
* Configures and constructs the redux store. Accepts the following parameters:
|
||||
* preloadedState - Any preloaded state to be applied to the store after it is initially configured.
|
||||
* appReducer - An object containing any app-specific reducer functions that the client needs.
|
||||
* persistConfig - Configuration for redux-persist.
|
||||
* getAppReducer - A function that returns the appReducer as defined above. Only used in development to enable hot reloading.
|
||||
* clientOptions - An object containing additional options used when configuring the redux store. The following options are available:
|
||||
* additionalMiddleware - func | array - Allows for single or multiple additional middleware functions to be passed in from the client side.
|
||||
* enableBuffer - bool - default = true - If true, the store will buffer all actions until offline state rehydration occurs.
|
||||
* enableThunk - bool - default = true - If true, include the thunk middleware automatically. If false, thunk must be provided as part of additionalMiddleware.
|
||||
*/
|
||||
|
||||
type ReduxStore = {
|
||||
store: redux.Store;
|
||||
persistor: Persistor;
|
||||
}
|
||||
|
||||
type ClientOptions = {
|
||||
additionalMiddleware: [];
|
||||
enableBuffer: boolean;
|
||||
enableThunk: boolean;
|
||||
enhancers: [];
|
||||
}
|
||||
|
||||
type V4Store = {
|
||||
storeKeys: Array<string>;
|
||||
restoredState: any;
|
||||
}
|
||||
|
||||
const defaultClientOptions: ClientOptions = {
|
||||
additionalMiddleware: [],
|
||||
enableBuffer: true,
|
||||
enableThunk: true,
|
||||
enhancers: [],
|
||||
};
|
||||
|
||||
export default function configureStore(preloadedState: any, appReducer: any, persistConfig: any, clientOptions: ClientOptions): ReduxStore {
|
||||
const baseState = Object.assign({}, initialState, preloadedState);
|
||||
const rootReducer = createReducer(serviceReducer as any, appReducer);
|
||||
const persistedReducer = persistReducer({...persistConfig}, rootReducer);
|
||||
const options = Object.assign({}, defaultClientOptions, clientOptions);
|
||||
|
||||
const store = redux.createStore(
|
||||
persistedReducer,
|
||||
baseState,
|
||||
redux.compose(
|
||||
redux.applyMiddleware(
|
||||
...createMiddleware(options),
|
||||
),
|
||||
...options.enhancers,
|
||||
),
|
||||
);
|
||||
|
||||
reducerRegistry.setChangeListener((reducers: any) => {
|
||||
const reducer = persistReducer(persistConfig, createReducer(baseState, reducers));
|
||||
|
||||
store.replaceReducer(reducer);
|
||||
});
|
||||
|
||||
const persistor = persistStore(store, null);
|
||||
|
||||
getStoredState().then(({storeKeys, restoredState}: V4Store) => {
|
||||
if (Object.keys(restoredState).length) {
|
||||
const state = {
|
||||
...restoredState,
|
||||
views: {
|
||||
...restoredState.views,
|
||||
root: {
|
||||
hydrationComplete: true,
|
||||
},
|
||||
},
|
||||
_persist: persistor.getState(),
|
||||
};
|
||||
|
||||
store.dispatch({
|
||||
type: General.OFFLINE_STORE_PURGE,
|
||||
state,
|
||||
});
|
||||
|
||||
console.log('HYDRATED FROM v4', storeKeys); // eslint-disable-line no-console
|
||||
const persistoid = createPersistoid(persistConfig);
|
||||
store.subscribe(() => {
|
||||
persistoid.update(store.getState());
|
||||
});
|
||||
store.dispatch({type: General.REHYDRATED});
|
||||
AsyncStorage.multiRemove(storeKeys);
|
||||
} else {
|
||||
store.dispatch({type: General.REHYDRATED});
|
||||
}
|
||||
});
|
||||
|
||||
return {store, persistor};
|
||||
}
|
||||
|
|
@ -1,266 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {GlobalState} from '@mm-redux/types/store';
|
||||
|
||||
const state: GlobalState = {
|
||||
entities: {
|
||||
general: {
|
||||
appState: false,
|
||||
credentials: {},
|
||||
config: {},
|
||||
dataRetentionPolicy: {},
|
||||
deviceToken: '',
|
||||
license: {},
|
||||
serverVersion: '',
|
||||
timezones: [],
|
||||
},
|
||||
users: {
|
||||
currentUserId: '',
|
||||
isManualStatus: {},
|
||||
mySessions: [],
|
||||
myAudits: [],
|
||||
profiles: {},
|
||||
profilesInTeam: {},
|
||||
profilesNotInTeam: {},
|
||||
profilesWithoutTeam: new Set(),
|
||||
profilesInChannel: {},
|
||||
profilesNotInChannel: {},
|
||||
statuses: {},
|
||||
stats: {},
|
||||
},
|
||||
teams: {
|
||||
currentTeamId: '',
|
||||
teams: {},
|
||||
myMembers: {},
|
||||
membersInTeam: {},
|
||||
stats: {},
|
||||
groupsAssociatedToTeam: {},
|
||||
totalCount: 0,
|
||||
},
|
||||
channels: {
|
||||
currentChannelId: '',
|
||||
channels: {},
|
||||
channelsInTeam: {},
|
||||
myMembers: {},
|
||||
membersInChannel: {},
|
||||
stats: {},
|
||||
groupsAssociatedToChannel: {},
|
||||
totalCount: 0,
|
||||
manuallyUnread: {},
|
||||
channelModerations: {},
|
||||
},
|
||||
posts: {
|
||||
expandedURLs: {},
|
||||
posts: {},
|
||||
postsInChannel: {},
|
||||
postsInThread: {},
|
||||
pendingPostIds: [],
|
||||
reactions: {},
|
||||
openGraph: {},
|
||||
selectedPostId: '',
|
||||
currentFocusedPostId: '',
|
||||
messagesHistory: {
|
||||
messages: [],
|
||||
index: {
|
||||
post: -1,
|
||||
comment: -1,
|
||||
},
|
||||
},
|
||||
},
|
||||
preferences: {
|
||||
myPreferences: {},
|
||||
},
|
||||
bots: {
|
||||
accounts: {},
|
||||
},
|
||||
jobs: {
|
||||
jobs: {},
|
||||
jobsByTypeList: {},
|
||||
},
|
||||
integrations: {
|
||||
incomingHooks: {},
|
||||
outgoingHooks: {},
|
||||
oauthApps: {},
|
||||
systemCommands: {},
|
||||
commands: {},
|
||||
},
|
||||
files: {
|
||||
files: {},
|
||||
fileIdsByPostId: {},
|
||||
},
|
||||
emojis: {
|
||||
customEmoji: {},
|
||||
nonExistentEmoji: new Set(),
|
||||
},
|
||||
search: {
|
||||
results: [],
|
||||
current: {},
|
||||
recent: {},
|
||||
matches: {},
|
||||
flagged: [],
|
||||
pinned: {},
|
||||
isSearchingTerm: false,
|
||||
isSearchGettingMore: false,
|
||||
},
|
||||
typing: {},
|
||||
roles: {
|
||||
roles: {},
|
||||
pending: new Set(),
|
||||
},
|
||||
gifs: {
|
||||
categories: {
|
||||
tagsList: [],
|
||||
tagsDict: {},
|
||||
},
|
||||
cache: {
|
||||
gifs: {},
|
||||
updating: false,
|
||||
},
|
||||
search: {
|
||||
searchText: '',
|
||||
searchBarText: '',
|
||||
resultsByTerm: {},
|
||||
scrollPosition: 0,
|
||||
priorLocation: null,
|
||||
},
|
||||
},
|
||||
schemes: {
|
||||
schemes: {},
|
||||
},
|
||||
groups: {
|
||||
groups: {},
|
||||
syncables: {},
|
||||
members: {},
|
||||
},
|
||||
channelCategories: {
|
||||
byId: {},
|
||||
orderByTeam: {},
|
||||
},
|
||||
},
|
||||
errors: [],
|
||||
requests: {
|
||||
channels: {
|
||||
getAllChannels: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
getChannels: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
myChannels: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
createChannel: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
updateChannel: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
general: {
|
||||
websocket: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
posts: {
|
||||
createPost: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
editPost: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
getPostThread: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
teams: {
|
||||
getMyTeams: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
getTeams: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
joinTeam: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
users: {
|
||||
checkMfa: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
login: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
autocompleteUsers: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
updateMe: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
files: {
|
||||
uploadFiles: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
roles: {
|
||||
getRolesByNames: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
getRoleByName: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
getRole: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
editRole: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
jobs: {
|
||||
createJob: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
getJob: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
getJobs: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
cancelJob: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
websocket: {
|
||||
connected: false,
|
||||
lastConnectAt: 0,
|
||||
lastDisconnectAt: 0,
|
||||
},
|
||||
};
|
||||
export default state;
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import thunk, {ThunkMiddleware} from 'redux-thunk';
|
||||
|
||||
import createActionBuffer from 'redux-action-buffer';
|
||||
import {PERSIST} from 'redux-persist';
|
||||
import {General} from '@mm-redux/constants';
|
||||
|
||||
export function createMiddleware(clientOptions: any): ThunkMiddleware[] {
|
||||
const {
|
||||
additionalMiddleware,
|
||||
enableBuffer,
|
||||
enableThunk,
|
||||
} = clientOptions;
|
||||
const middleware: ThunkMiddleware[] = [];
|
||||
|
||||
if (enableThunk) {
|
||||
middleware.push(thunk);
|
||||
}
|
||||
|
||||
if (additionalMiddleware) {
|
||||
if (typeof additionalMiddleware === 'function') {
|
||||
middleware.push(additionalMiddleware);
|
||||
} else {
|
||||
middleware.push(...additionalMiddleware);
|
||||
}
|
||||
}
|
||||
|
||||
if (enableBuffer) {
|
||||
middleware.push(createActionBuffer({breaker: General.REHYDRATED, passthrough: [PERSIST]}));
|
||||
}
|
||||
|
||||
return middleware;
|
||||
}
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import MMKV from 'react-native-mmkv-storage';
|
||||
|
||||
type ReadOnlyArrayString = ReadonlyArray<string>;
|
||||
|
||||
type MultiGetCallbackFunction = (
|
||||
errors: ReadonlyArray<Error> | null | undefined,
|
||||
result: ReadonlyArray<ReadOnlyArrayString> | null | undefined,
|
||||
) => void;
|
||||
|
||||
type MultiRequest = {
|
||||
keys: ReadonlyArray<string>;
|
||||
callback: MultiGetCallbackFunction | null | undefined;
|
||||
keyIndex: number;
|
||||
resolve: (
|
||||
result?: Promise<ReadonlyArray<ReadOnlyArrayString> | null | undefined>,
|
||||
) => void | null | undefined;
|
||||
reject: (error?: any) => void | null | undefined;
|
||||
};
|
||||
|
||||
function checkValidInput(usedKey: string, value?: any) {
|
||||
const isValuePassed = arguments.length > 1;
|
||||
|
||||
if (typeof usedKey !== 'string') {
|
||||
console.warn(
|
||||
`[MMKVStorageAdapter] Using ${typeof usedKey} type is not suppported. This can lead to unexpected behavior/errors. Use string instead.\nKey passed: ${usedKey}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
if (isValuePassed && typeof value !== 'string') {
|
||||
if (value == null) {
|
||||
throw new Error(
|
||||
`[MMKVStorageAdapter] Passing null/undefined as value is not supported. If you want to remove value, Use .remove method instead.\nPassed value: ${value}\nPassed key: ${usedKey}\n`,
|
||||
);
|
||||
} else {
|
||||
console.warn(
|
||||
`[MMKVStorageAdapter] The value for key "${usedKey}" is not a string. This can lead to unexpected behavior/errors. Consider stringifying it.\nPassed value: ${value}\nPassed key: ${usedKey}\n`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MMKVStorageAdapter = {
|
||||
_getRequests: [] as Array<MultiRequest>,
|
||||
_getKeys: [] as Array<string>,
|
||||
_immediate: null as number | null | undefined,
|
||||
|
||||
getItem: (
|
||||
key: string,
|
||||
callback?: (
|
||||
error: Error | null | undefined,
|
||||
result: string | null,
|
||||
) => void | null | undefined,
|
||||
): Promise<string | null> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
checkValidInput(key);
|
||||
MMKV.getStringAsync(key).then((result: string) => {
|
||||
if (callback) {
|
||||
callback(null, result);
|
||||
}
|
||||
resolve(result);
|
||||
}).catch((error) => {
|
||||
if (callback) {
|
||||
callback(null, error);
|
||||
}
|
||||
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
setItem: (
|
||||
key: string,
|
||||
value: string,
|
||||
callback?: (
|
||||
error: Error | null | undefined
|
||||
) => void | null | undefined,
|
||||
): Promise<null> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
checkValidInput(key, value);
|
||||
MMKV.setStringAsync(key, value).then(() => {
|
||||
if (callback) {
|
||||
callback(null);
|
||||
}
|
||||
resolve(null);
|
||||
}).catch((error) => {
|
||||
if (callback) {
|
||||
callback(error);
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
removeItem: (
|
||||
key: string,
|
||||
callback?: (
|
||||
error: Error | null | undefined
|
||||
) => void | null | undefined,
|
||||
): Promise<null> => {
|
||||
checkValidInput(key);
|
||||
if (callback) {
|
||||
callback(null);
|
||||
}
|
||||
return MMKV.removeItem(key);
|
||||
},
|
||||
|
||||
clear: (callback?: (error: Error | null | undefined) => void | null | undefined): Promise<null> => {
|
||||
if (callback) {
|
||||
callback(null);
|
||||
}
|
||||
|
||||
return MMKV.clearStore();
|
||||
},
|
||||
|
||||
getAllKeys: (
|
||||
callback?: (
|
||||
error: Error | null | undefined,
|
||||
keys: ReadOnlyArrayString | null | undefined
|
||||
) => void,
|
||||
): Promise<ReadOnlyArrayString> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
MMKV.getKeysAsync().then((keys: Array<string>) => {
|
||||
if (callback) {
|
||||
callback(null, keys);
|
||||
}
|
||||
resolve(keys);
|
||||
}).catch((error) => {
|
||||
if (callback) {
|
||||
callback(error, null);
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default MMKVStorageAdapter;
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import assert from 'assert';
|
||||
import reducerRegistry from '@mm-redux/store/reducer_registry';
|
||||
import configureStore from 'test/test_store';
|
||||
|
||||
describe('ReducerRegistry', () => {
|
||||
let store;
|
||||
|
||||
function testReducer() {
|
||||
return 'teststate';
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
store = await configureStore();
|
||||
});
|
||||
|
||||
it('register reducer', () => {
|
||||
reducerRegistry.register('testReducer', testReducer);
|
||||
assert.equal(store.getState().testReducer, 'teststate');
|
||||
});
|
||||
|
||||
it('get reducers', () => {
|
||||
reducerRegistry.register('testReducer', testReducer);
|
||||
const reducers = reducerRegistry.getReducers();
|
||||
assert.ok(reducers.testReducer);
|
||||
assert.ok(reducers.entities);
|
||||
assert.ok(reducers.requests);
|
||||
assert.ok(reducers.errors);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Reducer} from '@mm-redux/types/actions';
|
||||
import {Dictionary} from '@mm-redux/types/utilities';
|
||||
|
||||
// Based on http://nicolasgallagher.com/redux-modules-and-code-splitting/
|
||||
export class ReducerRegistry {
|
||||
emitChange?: Function;
|
||||
reducers: Dictionary<Reducer> = {};
|
||||
|
||||
setReducers = (reducers: Dictionary<Reducer>) => {
|
||||
this.reducers = reducers;
|
||||
}
|
||||
|
||||
getReducers = () => {
|
||||
return {...this.reducers};
|
||||
}
|
||||
|
||||
register = (name: string, reducer: Reducer) => {
|
||||
this.reducers = {...this.reducers, [name]: reducer};
|
||||
if (this.emitChange) {
|
||||
this.emitChange(this.getReducers());
|
||||
}
|
||||
}
|
||||
|
||||
setChangeListener = (listener: Function) => {
|
||||
this.emitChange = listener;
|
||||
}
|
||||
}
|
||||
|
||||
const reducerRegistry = new ReducerRegistry();
|
||||
export default reducerRegistry;
|
||||
5
app/mm-redux/types/module.d.ts
vendored
5
app/mm-redux/types/module.d.ts
vendored
|
|
@ -2,6 +2,5 @@
|
|||
// See LICENSE.txt for license information.
|
||||
declare module 'gfycat-sdk';
|
||||
declare module 'remote-redux-devtools';
|
||||
declare module 'redux-persist';
|
||||
declare module 'redux-persist/constants';
|
||||
declare module 'redux-action-buffer';
|
||||
declare module 'redux-action-buffer';
|
||||
declare module 'redux-reset';
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
import {AppState, NativeModules} from 'react-native';
|
||||
import {NotificationsAndroid, PendingNotifications} from 'react-native-notifications';
|
||||
|
||||
import ephemeralStore from 'app/store/ephemeral_store';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
|
||||
const {NotificationPreferences} = NativeModules;
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ class PushNotification {
|
|||
if (notification) {
|
||||
const data = notification.getData();
|
||||
if (data) {
|
||||
ephemeralStore.appStartedFromPushNotification = true;
|
||||
EphemeralStore.appStartedFromPushNotification = true;
|
||||
this.handleNotification(data, true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ import NotificationsIOS, {
|
|||
DEVICE_NOTIFICATION_OPENED_EVENT,
|
||||
} from 'react-native-notifications';
|
||||
|
||||
import {getBadgeCount} from 'app/selectors/views';
|
||||
import EphemeralStore from 'app/store/ephemeral_store';
|
||||
import {getCurrentLocale} from 'app/selectors/i18n';
|
||||
import {getLocalizedMessage} from 'app/i18n';
|
||||
import {t} from 'app/utils/i18n';
|
||||
import {getLocalizedMessage} from '@i18n';
|
||||
import {getCurrentLocale} from '@selectors/i18n';
|
||||
import {getBadgeCount} from '@selectors/views';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import Store from '@store/store';
|
||||
import {t} from '@utils/i18n';
|
||||
|
||||
const CATEGORY = 'CAN_REPLY';
|
||||
const REPLY_ACTION = 'REPLY_ACTION';
|
||||
|
|
@ -24,7 +25,6 @@ class PushNotification {
|
|||
this.deviceNotification = null;
|
||||
this.onRegister = null;
|
||||
this.onNotification = null;
|
||||
this.reduxStore = null;
|
||||
|
||||
NotificationsIOS.addEventListener(DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT, this.onRemoteNotificationsRegistered);
|
||||
NotificationsIOS.addEventListener(DEVICE_NOTIFICATION_RECEIVED_FOREGROUND_EVENT, this.onNotificationReceivedForeground);
|
||||
|
|
@ -46,7 +46,6 @@ class PushNotification {
|
|||
};
|
||||
|
||||
configure(options) {
|
||||
this.reduxStore = options.reduxStore;
|
||||
this.onRegister = options.onRegister;
|
||||
this.onNotification = options.onNotification;
|
||||
|
||||
|
|
@ -75,7 +74,7 @@ class PushNotification {
|
|||
}
|
||||
|
||||
createReplyCategory = () => {
|
||||
const {getState} = this.reduxStore;
|
||||
const {getState} = Store.redux;
|
||||
const state = getState();
|
||||
const locale = getCurrentLocale(state);
|
||||
|
||||
|
|
@ -198,8 +197,8 @@ class PushNotification {
|
|||
const ids = [];
|
||||
let badgeCount = notifications.length;
|
||||
|
||||
if (this.reduxStore) {
|
||||
const totalMentions = getBadgeCount(this.reduxStore.getState());
|
||||
if (Store.redux) {
|
||||
const totalMentions = getBadgeCount(Store.redux.getState());
|
||||
if (totalMentions > -1) {
|
||||
badgeCount = totalMentions;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
import NotificationsIOS from 'react-native-notifications';
|
||||
|
||||
import * as ViewSelectors from 'app/selectors/views';
|
||||
import * as ViewSelectors from '@selectors/views';
|
||||
import Store from '@store/store';
|
||||
|
||||
import PushNotification from './push_notifications.ios';
|
||||
|
||||
|
|
@ -95,7 +96,7 @@ describe('PushNotification', () => {
|
|||
});
|
||||
|
||||
it('clearChannelNotifications should set app badge number from to delivered notification count when redux store is not set', () => {
|
||||
PushNotification.reduxStore = null;
|
||||
Store.redux = null;
|
||||
const setApplicationIconBadgeNumber = jest.spyOn(PushNotification, 'setApplicationIconBadgeNumber');
|
||||
const deliveredNotifications = [{identifier: 1}, {identifier: 2}];
|
||||
NotificationsIOS.setDeliveredNotifications(deliveredNotifications);
|
||||
|
|
@ -105,7 +106,7 @@ describe('PushNotification', () => {
|
|||
});
|
||||
|
||||
it('clearChannelNotifications should set app badge number from redux store when set', () => {
|
||||
PushNotification.reduxStore = {
|
||||
Store.redux = {
|
||||
getState: jest.fn(),
|
||||
};
|
||||
const setApplicationIconBadgeNumber = jest.spyOn(PushNotification, 'setApplicationIconBadgeNumber');
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ function locale(state = defaultLocale, action) {
|
|||
switch (action.type) {
|
||||
case UserTypes.RECEIVED_ME: {
|
||||
const data = action.data || action.payload;
|
||||
return data.locale;
|
||||
if (data?.locale) {
|
||||
return data.locale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ function deepLinkURL(state = '', action) {
|
|||
|
||||
function hydrationComplete(state = false, action) {
|
||||
switch (action.type) {
|
||||
case General.REHYDRATED:
|
||||
case General.STORE_REHYDRATION_COMPLETE:
|
||||
return true;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export const getThreadDraft = createSelector(
|
|||
);
|
||||
|
||||
export function getProfileImageUri(state) {
|
||||
return state.views.user.profileImageUri;
|
||||
return state.views?.user?.profileImageUri;
|
||||
}
|
||||
|
||||
export const getBadgeCount = createSelector(
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ import {ViewTypes} from 'app/constants';
|
|||
|
||||
class EphemeralStore {
|
||||
constructor() {
|
||||
this.allNavigationComponentIds = [];
|
||||
this.appStarted = false;
|
||||
this.appStartedFromPushNotification = false;
|
||||
this.deviceToken = null;
|
||||
this.navigationComponentIdStack = [];
|
||||
this.allNavigationComponentIds = [];
|
||||
this.currentServerUrl = null;
|
||||
this.navigationComponentIdStack = [];
|
||||
this.safeAreaInsets = {
|
||||
[ViewTypes.PORTRAIT]: null,
|
||||
[ViewTypes.LANDSCAPE]: null,
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {combineReducers} from 'redux';
|
||||
import reducerRegistry from './reducer_registry';
|
||||
import {enableBatching, Reducer} from '@mm-redux/types/actions';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import {combineReducers} from 'redux';
|
||||
|
||||
import {enableBatching, Reducer} from '@mm-redux/types/actions';
|
||||
|
||||
const KEY_PREFIX = 'reduxPersist:';
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
export function createReducer(...reducers: Reducer[]) {
|
||||
reducerRegistry.setReducers(Object.assign({}, ...reducers));
|
||||
const baseReducer = combineReducers(reducerRegistry.getReducers());
|
||||
const reducerRegistry = Object.assign({}, ...reducers);
|
||||
const baseReducer = combineReducers(reducerRegistry);
|
||||
|
||||
return enableBatching(baseReducer);
|
||||
}
|
||||
|
||||
const KEY_PREFIX = 'reduxPersist:';
|
||||
|
||||
export async function getStoredState() {
|
||||
const restoredState: Record<string, any> = {};
|
||||
let storeKeys: Array<string> = [];
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import initialState from 'app/initial_state';
|
||||
import configureAppStore from './store';
|
||||
|
||||
const {store, persistor} = configureAppStore(initialState);
|
||||
|
||||
export {persistor};
|
||||
export default store;
|
||||
242
app/store/index.ts
Normal file
242
app/store/index.ts
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import * as redux from 'redux';
|
||||
import {createPersistoid, createTransform, persistReducer, persistStore, Persistor, PersistConfig} from 'redux-persist';
|
||||
import {createBlacklistFilter} from 'redux-persist-transform-filter';
|
||||
import reduxReset from 'redux-reset';
|
||||
|
||||
import {General} from '@mm-redux/constants';
|
||||
import serviceReducer from '@mm-redux/reducers';
|
||||
import {GlobalState} from '@mm-redux/types/store';
|
||||
|
||||
import initialState from '@store/initial_state';
|
||||
import appReducer from 'app/reducers';
|
||||
|
||||
import {createReducer, getStoredState} from './helpers';
|
||||
import {createMiddlewares} from './middlewares';
|
||||
import Store from './store';
|
||||
import {transformSet} from './utils';
|
||||
|
||||
/**
|
||||
* Configures and constructs the redux store. Accepts the following parameters:
|
||||
* preloadedState - Any preloaded state to be applied to the store after it is initially configured.
|
||||
* appReducer - An object containing any app-specific reducer functions that the client needs.
|
||||
* persistConfig - Configuration for redux-persist.
|
||||
* getAppReducer - A function that returns the appReducer as defined above. Only used in development to enable hot reloading.
|
||||
* clientOptions - An object containing additional options used when configuring the redux store. The following options are available:
|
||||
* additionalMiddleware - func | array - Allows for single or multiple additional middleware functions to be passed in from the client side.
|
||||
* enableBuffer - bool - default = true - If true, the store will buffer all actions until offline state rehydration occurs.
|
||||
* enableThunk - bool - default = true - If true, include the thunk middleware automatically. If false, thunk must be provided as part of additionalMiddleware.
|
||||
*/
|
||||
|
||||
type ReduxStore = {
|
||||
store: redux.Store;
|
||||
persistor: Persistor;
|
||||
}
|
||||
|
||||
type ClientOptions = {
|
||||
enableBuffer: boolean;
|
||||
enableThunk: boolean;
|
||||
}
|
||||
|
||||
type V4Store = {
|
||||
storeKeys: Array<string>;
|
||||
restoredState: any;
|
||||
}
|
||||
|
||||
const usersSetTransform = [
|
||||
'profilesInChannel',
|
||||
'profilesNotInChannel',
|
||||
'profilesInTeam',
|
||||
'profilesNotInTeam',
|
||||
];
|
||||
|
||||
const channelSetTransform = [
|
||||
'channelsInTeam',
|
||||
];
|
||||
|
||||
const rolesSetTransform = [
|
||||
'pending',
|
||||
];
|
||||
|
||||
const setTransforms = [
|
||||
...usersSetTransform,
|
||||
...channelSetTransform,
|
||||
...rolesSetTransform,
|
||||
];
|
||||
|
||||
const viewsBlackListFilter = createBlacklistFilter(
|
||||
'views',
|
||||
['extension', 'root'],
|
||||
);
|
||||
|
||||
const typingBlackListFilter = createBlacklistFilter(
|
||||
'entities',
|
||||
['typing'],
|
||||
);
|
||||
|
||||
const channelViewBlackList: any = {loading: true, refreshing: true, loadingPosts: true, retryFailed: true, loadMorePostsVisible: true};
|
||||
const channelViewBlackListFilter = createTransform(
|
||||
(inboundState: any) => {
|
||||
const channel: any = {};
|
||||
const keys: Array<string> = inboundState.channel ? Object.keys(inboundState.channel) : [];
|
||||
|
||||
for (const channelKey of keys) {
|
||||
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: any = {nonExistentEmoji: true};
|
||||
const emojiBlackListFilter = createTransform(
|
||||
(inboundState: any) => {
|
||||
const emojis: any = {};
|
||||
const keys: Array<string> = inboundState.emojis ? Object.keys(inboundState.emojis) : [];
|
||||
|
||||
for (const emojiKey of keys) {
|
||||
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: any, key: string) => {
|
||||
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: any, key: string) => {
|
||||
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;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Function to configure the redux store with persistence
|
||||
* @param storage the storage engine to use
|
||||
* @param preloadedState (optional) the initial state to use (applies to tests)
|
||||
* @param optionalConfig (optional) persist configuration (applies to tests)
|
||||
* @param optionalOptions (optional) middleware configuration (applies to tests)
|
||||
*/
|
||||
export default function configureStore(storage: any, preloadedState: any = {}, optionalConfig: {}, optionalOptions = {}): ReduxStore {
|
||||
const defaultOptions: ClientOptions = {
|
||||
enableBuffer: true,
|
||||
enableThunk: true,
|
||||
};
|
||||
|
||||
const defaultConfig: PersistConfig<GlobalState> = {
|
||||
key: 'root',
|
||||
storage,
|
||||
serialize: (state: GlobalState) => ({...state}),
|
||||
deserialize: false,
|
||||
blacklist: ['device', 'navigation', 'requests', '_persist'],
|
||||
transforms: [
|
||||
setTransformer,
|
||||
viewsBlackListFilter,
|
||||
typingBlackListFilter,
|
||||
channelViewBlackListFilter,
|
||||
emojiBlackListFilter,
|
||||
],
|
||||
throttle: 100,
|
||||
};
|
||||
|
||||
const persistConfig: PersistConfig<GlobalState> = Object.assign({}, defaultConfig, optionalConfig);
|
||||
const baseState: any = Object.assign({}, initialState, preloadedState);
|
||||
const rootReducer: any = createReducer(serviceReducer as any, appReducer as any);
|
||||
const persistedReducer = persistReducer({...persistConfig}, rootReducer);
|
||||
const options: ClientOptions = Object.assign({}, defaultOptions, optionalOptions);
|
||||
|
||||
const store = redux.createStore(
|
||||
persistedReducer,
|
||||
baseState,
|
||||
redux.compose(
|
||||
redux.applyMiddleware(
|
||||
...createMiddlewares(options),
|
||||
),
|
||||
reduxReset(General.OFFLINE_STORE_PURGE),
|
||||
),
|
||||
);
|
||||
|
||||
const persistor = persistStore(store, null);
|
||||
|
||||
getStoredState().then(({storeKeys, restoredState}: V4Store) => {
|
||||
if (Object.keys(restoredState).length) {
|
||||
const state = {
|
||||
...restoredState,
|
||||
views: {
|
||||
...restoredState.views,
|
||||
root: {
|
||||
hydrationComplete: true,
|
||||
},
|
||||
},
|
||||
_persist: persistor.getState(),
|
||||
};
|
||||
|
||||
store.dispatch({
|
||||
type: General.OFFLINE_STORE_PURGE,
|
||||
state,
|
||||
});
|
||||
|
||||
console.log('HYDRATED FROM v4', storeKeys); // eslint-disable-line no-console
|
||||
const persistoid = createPersistoid(persistConfig);
|
||||
store.subscribe(() => {
|
||||
persistoid.update(store.getState());
|
||||
});
|
||||
store.dispatch({type: General.REHYDRATED});
|
||||
AsyncStorage.multiRemove(storeKeys);
|
||||
} else {
|
||||
let executed = false;
|
||||
const unsubscribe = store.subscribe(() => {
|
||||
if (store.getState()._persist?.rehydrated && !executed) { // eslint-disable-line no-underscore-dangle
|
||||
unsubscribe();
|
||||
executed = true;
|
||||
store.dispatch({type: General.REHYDRATED});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Store.redux = store;
|
||||
Store.persistor = persistor;
|
||||
|
||||
return {store, persistor};
|
||||
}
|
||||
|
|
@ -1,172 +1,11 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Platform} from 'react-native';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
import {REHYDRATE} from 'redux-persist';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import initialState from 'app/initial_state';
|
||||
import {throttle} from 'app/utils/general';
|
||||
import initialState from '@store/initial_state';
|
||||
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
|
||||
import EphemeralStore from './ephemeral_store';
|
||||
|
||||
import {
|
||||
captureException,
|
||||
LOGGER_JAVASCRIPT_WARNING,
|
||||
} from 'app/utils/sentry';
|
||||
|
||||
const SAVE_STATE_ACTIONS = [
|
||||
'CONNECTION_CHANGED',
|
||||
'DATA_CLEANUP',
|
||||
'LOGIN',
|
||||
'Offline/STATUS_CHANGED',
|
||||
'persist/REHYDRATE',
|
||||
'RECEIVED_APP_STATE',
|
||||
'WEBSOCKET_CLOSED',
|
||||
'WEBSOCKET_SUCCESS',
|
||||
];
|
||||
|
||||
export const middlewares = () => {
|
||||
const middlewareFunctions = [
|
||||
messageRetention,
|
||||
];
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
middlewareFunctions.push(saveShareExtensionState);
|
||||
}
|
||||
|
||||
return middlewareFunctions;
|
||||
};
|
||||
|
||||
// This middleware stores key parts of state entities into a file (in the App Group container) on certain actions.
|
||||
// iOS only. Allows the share extension to work, without having access available to the redux store object.
|
||||
// Remove this middleware if/when state is moved to a persisted solution.
|
||||
function saveShareExtensionState(store) {
|
||||
return (next) => (action) => {
|
||||
if (SAVE_STATE_ACTIONS.includes(action.type)) {
|
||||
throttle(saveStateToFile(store));
|
||||
}
|
||||
return next(action);
|
||||
};
|
||||
}
|
||||
|
||||
async function saveStateToFile(store) {
|
||||
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]);
|
||||
});
|
||||
|
||||
let url;
|
||||
if (state.entities.users.currentUserId) {
|
||||
url = state.entities.general.credentials.url || state.views.selectServer.serverUrl;
|
||||
}
|
||||
|
||||
const entities = {
|
||||
...state.entities,
|
||||
general: {
|
||||
...state.entities.general,
|
||||
credentials: {
|
||||
url,
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
...state.entities.channels,
|
||||
channelsInTeam,
|
||||
},
|
||||
users: {
|
||||
...state.entities.users,
|
||||
profilesInChannel,
|
||||
profilesNotInTeam: [],
|
||||
profilesWithoutTeam: [],
|
||||
profilesNotInChannel: [],
|
||||
},
|
||||
};
|
||||
|
||||
mattermostBucket.writeToFile('entities', JSON.stringify(entities));
|
||||
}
|
||||
}
|
||||
|
||||
function messageRetention(store) {
|
||||
return (next) => (action) => {
|
||||
if (action.type === REHYDRATE) {
|
||||
// On first run payload is not set (when installed)
|
||||
if (!action.payload) {
|
||||
action.payload = {
|
||||
app: {
|
||||
build: DeviceInfo.getBuildNumber(),
|
||||
version: DeviceInfo.getVersion(),
|
||||
},
|
||||
views: {
|
||||
root: {
|
||||
hydrationComplete: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const {app} = action.payload;
|
||||
const {entities, views} = action.payload;
|
||||
|
||||
if (!entities || !views) {
|
||||
const version = DeviceInfo.getVersion();
|
||||
EphemeralStore.prevAppVersion = version;
|
||||
action.payload = {
|
||||
...action.payload,
|
||||
app: {
|
||||
build: DeviceInfo.getBuildNumber(),
|
||||
version,
|
||||
},
|
||||
};
|
||||
return next(action);
|
||||
}
|
||||
|
||||
EphemeralStore.prevAppVersion = app?.version;
|
||||
if (app?.version !== DeviceInfo.getVersion() || app?.build !== DeviceInfo.getBuildNumber()) {
|
||||
// When a new version of the app has been detected
|
||||
return next(resetStateForNewVersion(action));
|
||||
}
|
||||
|
||||
// Keep only the last 60 messages for the last 5 viewed channels in each team
|
||||
// and apply data retention on those posts if applies
|
||||
try {
|
||||
action.payload = cleanUpState(action.payload);
|
||||
} catch (e) {
|
||||
// Sometimes, the payload is incomplete so log the error to Sentry and skip the cleanup
|
||||
console.warn(e); // eslint-disable-line no-console
|
||||
captureException(e, LOGGER_JAVASCRIPT_WARNING, store);
|
||||
}
|
||||
|
||||
return next(action);
|
||||
} else if (action.type === ViewTypes.DATA_CLEANUP) {
|
||||
action.payload = cleanUpState(action.payload, true);
|
||||
return next(action);
|
||||
}
|
||||
|
||||
/* Uncomment the following lines to log the actions being dispatched */
|
||||
// if (action.type === 'BATCHING_REDUCER.BATCH') {
|
||||
// action.payload.forEach((p) => {
|
||||
// console.log('BATCHED ACTIONS', p.type);
|
||||
// });
|
||||
// } else {
|
||||
// console.log('ACTION', action.type);
|
||||
// }
|
||||
|
||||
return next(action);
|
||||
};
|
||||
}
|
||||
|
||||
function resetStateForNewVersion(payload) {
|
||||
export function resetStateForNewVersion(payload) {
|
||||
const lastChannelForTeam = getLastChannelForTeam(payload);
|
||||
|
||||
let general = initialState.entities.general;
|
||||
|
|
@ -258,6 +97,9 @@ function resetStateForNewVersion(payload) {
|
|||
}
|
||||
|
||||
const nextState = {
|
||||
_persist: {
|
||||
rehydrated: true,
|
||||
},
|
||||
app: {
|
||||
build: DeviceInfo.getBuildNumber(),
|
||||
version: DeviceInfo.getVersion(),
|
||||
|
|
@ -295,7 +137,7 @@ function resetStateForNewVersion(payload) {
|
|||
return nextState;
|
||||
}
|
||||
|
||||
function getLastChannelForTeam(payload) {
|
||||
export function getLastChannelForTeam(payload) {
|
||||
if (payload?.views?.team?.lastChannelForTeam) {
|
||||
const lastChannelForTeam = {...payload.views.team.lastChannelForTeam};
|
||||
const convertLastChannelForTeam = Object.values(lastChannelForTeam).some((value) => !Array.isArray(value));
|
||||
|
|
@ -444,9 +286,6 @@ export function cleanUpState(payload, keepCurrent = false) {
|
|||
...resetPayload.views.channel,
|
||||
...payload.views.channel,
|
||||
},
|
||||
root: {
|
||||
hydrationComplete: true,
|
||||
},
|
||||
},
|
||||
websocket: {
|
||||
lastConnectAt: payload.websocket?.lastConnectAt,
|
||||
|
|
@ -536,4 +375,4 @@ function removePendingPost(pendingPostIds, id) {
|
|||
if (pendingIndex !== -1) {
|
||||
pendingPostIds.splice(pendingIndex, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
35
app/store/middlewares/index.ts
Normal file
35
app/store/middlewares/index.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Platform} from 'react-native';
|
||||
import {PERSIST, REHYDRATE} from 'redux-persist';
|
||||
import {ThunkMiddleware} from 'redux-thunk';
|
||||
import createActionBuffer from 'redux-action-buffer';
|
||||
|
||||
import messageRetention from './message_retention';
|
||||
import createSentryMiddleware from './sentry';
|
||||
import thunk from './thunk';
|
||||
|
||||
export function createMiddlewares(clientOptions: any): ThunkMiddleware[] {
|
||||
const {
|
||||
enableBuffer,
|
||||
enableThunk,
|
||||
} = clientOptions;
|
||||
const middleware: ThunkMiddleware[] = [];
|
||||
|
||||
if (enableThunk) {
|
||||
middleware.push(thunk);
|
||||
}
|
||||
|
||||
middleware.push(createSentryMiddleware(), messageRetention);
|
||||
if (Platform.OS === 'ios') {
|
||||
const iosExtension = require('./ios_extension').default;
|
||||
middleware.push(iosExtension);
|
||||
}
|
||||
|
||||
if (enableBuffer) {
|
||||
middleware.push(createActionBuffer({breaker: REHYDRATE, passthrough: [PERSIST]}));
|
||||
}
|
||||
|
||||
return middleware;
|
||||
}
|
||||
72
app/store/middlewares/ios_extension.js
Normal file
72
app/store/middlewares/ios_extension.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
import {throttle} from '@utils/general';
|
||||
|
||||
const SAVE_STATE_ACTIONS = [
|
||||
'CONNECTION_CHANGED',
|
||||
'DATA_CLEANUP',
|
||||
'LOGIN',
|
||||
'Offline/STATUS_CHANGED',
|
||||
'persist/REHYDRATE',
|
||||
'RECEIVED_APP_STATE',
|
||||
'WEBSOCKET_CLOSED',
|
||||
'WEBSOCKET_SUCCESS',
|
||||
];
|
||||
|
||||
// This middleware stores key parts of state entities into a file (in the App Group container) on certain actions.
|
||||
// iOS only. Allows the share extension to work, without having access available to the redux store object.
|
||||
// Remove this middleware if/when state is moved to a persisted solution.
|
||||
export default function saveShareExtensionState(store) {
|
||||
return (next) => (action) => {
|
||||
if (SAVE_STATE_ACTIONS.includes(action.type)) {
|
||||
throttle(saveStateToFile(store));
|
||||
}
|
||||
return next(action);
|
||||
};
|
||||
}
|
||||
|
||||
async function saveStateToFile(store) {
|
||||
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]);
|
||||
});
|
||||
|
||||
let url;
|
||||
if (state.entities.users.currentUserId) {
|
||||
url = state.entities.general.credentials.url || state.views.selectServer.serverUrl;
|
||||
}
|
||||
|
||||
const entities = {
|
||||
...state.entities,
|
||||
general: {
|
||||
...state.entities.general,
|
||||
credentials: {
|
||||
url,
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
...state.entities.channels,
|
||||
channelsInTeam,
|
||||
},
|
||||
users: {
|
||||
...state.entities.users,
|
||||
profilesInChannel,
|
||||
profilesNotInTeam: [],
|
||||
profilesWithoutTeam: [],
|
||||
profilesNotInChannel: [],
|
||||
},
|
||||
};
|
||||
|
||||
mattermostBucket.writeToFile('entities', JSON.stringify(entities));
|
||||
}
|
||||
}
|
||||
84
app/store/middlewares/message_retention.js
Normal file
84
app/store/middlewares/message_retention.js
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
import {REHYDRATE} from 'redux-persist';
|
||||
import semver from 'semver/preload';
|
||||
|
||||
import {ViewTypes} from '@constants';
|
||||
import {General} from '@mm-redux/constants';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import {
|
||||
captureException,
|
||||
LOGGER_JAVASCRIPT_WARNING,
|
||||
} from '@utils/sentry';
|
||||
|
||||
import {cleanUpState, resetStateForNewVersion} from './helpers';
|
||||
|
||||
export default function messageRetention(store) {
|
||||
return (next) => (action) => {
|
||||
if (action.type === REHYDRATE) {
|
||||
if (!action.payload) {
|
||||
// On first run payload is not set (when installed)
|
||||
const version = DeviceInfo.getVersion();
|
||||
const major = semver.major(version);
|
||||
const minor = semver.minor(version);
|
||||
const patch = semver.patch(version);
|
||||
const prevAppVersion = `${major}.${parseInt(minor, 10) - 1}.${patch}`;
|
||||
EphemeralStore.prevAppVersion = prevAppVersion;
|
||||
action.payload = {
|
||||
app: {
|
||||
build: DeviceInfo.getBuildNumber(),
|
||||
version,
|
||||
},
|
||||
_persist: {
|
||||
rehydrated: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const {app} = action.payload;
|
||||
const {entities, views} = action.payload;
|
||||
|
||||
if (!EphemeralStore.prevAppVersion) {
|
||||
EphemeralStore.prevAppVersion = app?.version;
|
||||
}
|
||||
|
||||
if (!entities || !views) {
|
||||
return next(action);
|
||||
}
|
||||
|
||||
// When a new version of the app has been detected
|
||||
if (!app || !app.version || app.version !== DeviceInfo.getVersion() || app.build !== DeviceInfo.getBuildNumber()) {
|
||||
action.payload = resetStateForNewVersion(action.payload);
|
||||
return next(action);
|
||||
}
|
||||
|
||||
// Keep only the last 60 messages for the last 5 viewed channels in each team
|
||||
// and apply data retention on those posts if applies
|
||||
try {
|
||||
action.payload = cleanUpState(action.payload);
|
||||
} catch (e) {
|
||||
// Sometimes, the payload is incomplete so log the error to Sentry and skip the cleanup
|
||||
console.warn(e); // eslint-disable-line no-console
|
||||
captureException(e, LOGGER_JAVASCRIPT_WARNING, store);
|
||||
}
|
||||
|
||||
return next(action);
|
||||
} else if (action.type === ViewTypes.DATA_CLEANUP) {
|
||||
action.payload = cleanUpState(action.payload, true);
|
||||
return next(action);
|
||||
}
|
||||
|
||||
/* Uncomment the following lines to log the actions being dispatched */
|
||||
// if (action.type === 'BATCHING_REDUCER.BATCH') {
|
||||
// action.payload.forEach((p) => {
|
||||
// console.log('BATCHED ACTIONS', p.type);
|
||||
// });
|
||||
// } else {
|
||||
// console.log('ACTION', action.type);
|
||||
// }
|
||||
|
||||
return next(action);
|
||||
};
|
||||
}
|
||||
|
|
@ -5,17 +5,15 @@
|
|||
|
||||
import assert from 'assert';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import {ViewTypes} from '@constants';
|
||||
import {
|
||||
cleanUpPostsInChannel,
|
||||
cleanUpState,
|
||||
getAllFromPostsInChannel,
|
||||
middlewares,
|
||||
} from 'app/store/middleware';
|
||||
} from './helpers';
|
||||
import messageRetention from './message_retention';
|
||||
|
||||
describe('messageRetention', () => {
|
||||
const messageRetention = middlewares()[0];
|
||||
|
||||
describe('should chain the same incoming action type', () => {
|
||||
const actions = [
|
||||
{
|
||||
|
|
@ -6,7 +6,7 @@ import {BATCH} from 'redux-batched-actions';
|
|||
export const BREADCRUMB_REDUX_ACTION = 'redux-action';
|
||||
|
||||
let Sentry;
|
||||
export function createSentryMiddleware() {
|
||||
export default function createSentryMiddleware() {
|
||||
if (!Sentry) {
|
||||
Sentry = require('@sentry/react-native');
|
||||
}
|
||||
39
app/store/middlewares/thunk.js
Normal file
39
app/store/middlewares/thunk.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {
|
||||
captureMessage,
|
||||
cleanUrlForLogging,
|
||||
LOGGER_JAVASCRIPT_WARNING,
|
||||
} from 'app/utils/sentry';
|
||||
|
||||
// Creates middleware that mimics thunk while catching network errors thrown by Client4 that haven't
|
||||
// been otherwise handled.
|
||||
export default (store) => (next) => (action) => {
|
||||
if (typeof action === 'function') {
|
||||
const result = action(store.dispatch, store.getState);
|
||||
|
||||
if (result instanceof Promise) {
|
||||
return result.catch((error) => {
|
||||
if (error.url) {
|
||||
// This is a connection error from app/mm-redux. This should've been handled
|
||||
// within the action itself, so we'll log to Sentry enough to identify where
|
||||
// that handling is missing.
|
||||
captureMessage(
|
||||
`Caught Client4 error "${error.message}" from "${cleanUrlForLogging(error.url)}"`,
|
||||
LOGGER_JAVASCRIPT_WARNING,
|
||||
store,
|
||||
);
|
||||
|
||||
return {error};
|
||||
}
|
||||
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return next(action);
|
||||
};
|
||||
98
app/store/mmkv_adapter.ts
Normal file
98
app/store/mmkv_adapter.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import MMKVStorage from 'react-native-mmkv-storage';
|
||||
|
||||
function checkValidInput(usedKey: string, value?: any) {
|
||||
const isValuePassed = arguments.length > 1;
|
||||
|
||||
if (typeof usedKey !== 'string') {
|
||||
console.warn(
|
||||
`[MMKVStorageAdapter] Using ${typeof usedKey} type is not suppported. This can lead to unexpected behavior/errors. Use string instead.\nKey passed: ${usedKey}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
if (isValuePassed && typeof value !== 'object') {
|
||||
if (value == null) {
|
||||
throw new Error(
|
||||
`[MMKVStorageAdapter] Passing null/undefined as value is not supported. If you want to remove value, Use .remove method instead.\nPassed value: ${value}\nPassed key: ${usedKey}\n`,
|
||||
);
|
||||
} else {
|
||||
console.warn(
|
||||
`[MMKVStorageAdapter] The value for key "${usedKey}" is not a object. This can lead to unexpected behavior/errors. Consider JSON.parse it.\nPassed value: ${value}\nPassed key: ${usedKey}\n`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default async function getStorage(identifier = 'default') {
|
||||
const MMKV = await new MMKVStorage.Loader().
|
||||
withInstanceID(identifier).
|
||||
setProcessingMode(MMKVStorage.MODES.MULTI_PROCESS).
|
||||
withEncryption().
|
||||
initialize();
|
||||
|
||||
return {
|
||||
getItem: (
|
||||
key: string,
|
||||
callback?: (
|
||||
error: Error | null | undefined,
|
||||
result: object | null,
|
||||
) => void | null | undefined,
|
||||
): Promise<object | null> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
checkValidInput(key);
|
||||
MMKV.getMapAsync(key).then((result: object) => {
|
||||
if (callback) {
|
||||
callback(null, result);
|
||||
}
|
||||
resolve(result);
|
||||
}).catch((error: Error) => {
|
||||
if (callback) {
|
||||
callback(null, error);
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
setItem: (
|
||||
key: string,
|
||||
value: object,
|
||||
callback?: (
|
||||
error: Error | null | undefined
|
||||
) => void | null | undefined,
|
||||
): Promise<null> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
checkValidInput(key, value);
|
||||
MMKV.setMapAsync(key, value).then(() => {
|
||||
if (callback) {
|
||||
callback(null);
|
||||
}
|
||||
resolve(null);
|
||||
}).catch((error: Error) => {
|
||||
if (callback) {
|
||||
callback(error);
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
removeItem: (
|
||||
key: string,
|
||||
callback?: (
|
||||
error: Error | null | undefined
|
||||
) => void | null | undefined,
|
||||
): Promise<boolean> => {
|
||||
checkValidInput(key);
|
||||
if (callback) {
|
||||
callback(null);
|
||||
}
|
||||
|
||||
return MMKV.removeItem(key);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {createBlacklistFilter} from 'redux-persist-transform-filter';
|
||||
import {createTransform} from 'redux-persist';
|
||||
import reduxReset from 'redux-reset';
|
||||
|
||||
import {General} from '@mm-redux/constants';
|
||||
import configureStore from '@mm-redux/store';
|
||||
import MMKVStorageAdapter from '@mm-redux/store/mmkv_adapter';
|
||||
|
||||
import appReducer from 'app/reducers';
|
||||
import {createSentryMiddleware} from 'app/utils/sentry/middleware';
|
||||
|
||||
import {middlewares} from './middleware';
|
||||
import {createThunkMiddleware} from './thunk';
|
||||
import {transformSet} from './utils';
|
||||
|
||||
const usersSetTransform = [
|
||||
'profilesInChannel',
|
||||
'profilesNotInChannel',
|
||||
'profilesInTeam',
|
||||
'profilesNotInTeam',
|
||||
];
|
||||
|
||||
const channelSetTransform = [
|
||||
'channelsInTeam',
|
||||
];
|
||||
|
||||
const rolesSetTransform = [
|
||||
'pending',
|
||||
];
|
||||
|
||||
const setTransforms = [
|
||||
...usersSetTransform,
|
||||
...channelSetTransform,
|
||||
...rolesSetTransform,
|
||||
];
|
||||
|
||||
const viewsBlackListFilter = createBlacklistFilter(
|
||||
'views',
|
||||
['extension', 'root'],
|
||||
);
|
||||
|
||||
const typingBlackListFilter = createBlacklistFilter(
|
||||
'entities',
|
||||
['typing'],
|
||||
);
|
||||
|
||||
const channelViewBlackList = {loading: true, refreshing: true, loadingPosts: true, retryFailed: true, loadMorePostsVisible: true};
|
||||
const channelViewBlackListFilter = createTransform(
|
||||
(inboundState) => {
|
||||
const channel = {};
|
||||
const keys = inboundState.channel ? Object.keys(inboundState.channel) : [];
|
||||
|
||||
for (const channelKey of keys) {
|
||||
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 = {};
|
||||
const keys = inboundState.emojis ? Object.keys(inboundState.emojis) : [];
|
||||
|
||||
for (const emojiKey of keys) {
|
||||
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 persistConfig = {
|
||||
key: 'root',
|
||||
storage: MMKVStorageAdapter,
|
||||
blacklist: ['device', 'navigation', 'offline', 'requests'],
|
||||
transforms: [
|
||||
setTransformer,
|
||||
viewsBlackListFilter,
|
||||
typingBlackListFilter,
|
||||
channelViewBlackListFilter,
|
||||
emojiBlackListFilter,
|
||||
],
|
||||
};
|
||||
|
||||
export default function configureAppStore(initialState) {
|
||||
const clientOptions = {
|
||||
additionalMiddleware: [
|
||||
createThunkMiddleware(),
|
||||
createSentryMiddleware(),
|
||||
...middlewares(),
|
||||
],
|
||||
enableThunk: false, // We override the default thunk middleware
|
||||
enhancers: [reduxReset(General.OFFLINE_STORE_PURGE)],
|
||||
};
|
||||
|
||||
return configureStore(initialState, appReducer, persistConfig, clientOptions);
|
||||
}
|
||||
17
app/store/store.ts
Normal file
17
app/store/store.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import * as redux from 'redux';
|
||||
import {Persistor} from 'redux-persist';
|
||||
|
||||
class Store {
|
||||
redux: redux.Store | null;
|
||||
persistor: Persistor | null;
|
||||
|
||||
constructor() {
|
||||
this.redux = null;
|
||||
this.persistor = null;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Store();
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {
|
||||
captureMessage,
|
||||
cleanUrlForLogging,
|
||||
LOGGER_JAVASCRIPT_WARNING,
|
||||
} from 'app/utils/sentry';
|
||||
|
||||
// Creates middleware that mimics thunk while catching network errors thrown by Client4 that haven't
|
||||
// been otherwise handled.
|
||||
export function createThunkMiddleware() {
|
||||
return (store) => (next) => (action) => {
|
||||
if (typeof action === 'function') {
|
||||
const result = action(store.dispatch, store.getState);
|
||||
|
||||
if (result instanceof Promise) {
|
||||
return result.catch((error) => {
|
||||
if (error.url) {
|
||||
// This is a connection error from app/mm-redux. This should've been handled
|
||||
// within the action itself, so we'll log to Sentry enough to identify where
|
||||
// that handling is missing.
|
||||
captureMessage(
|
||||
`Caught Client4 error "${error.message}" from "${cleanUrlForLogging(error.url)}"`,
|
||||
LOGGER_JAVASCRIPT_WARNING,
|
||||
store,
|
||||
);
|
||||
|
||||
return {error};
|
||||
}
|
||||
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return next(action);
|
||||
};
|
||||
}
|
||||
|
|
@ -46,14 +46,21 @@ export function transformSet(incoming, setTransforms, toStorage = true) {
|
|||
|
||||
export function waitForHydration(store, callback) {
|
||||
let executed = false; // this is to prevent a race condition when subcription runs before unsubscribed
|
||||
if (store.getState().views?.root?.hydrationComplete && !executed) {
|
||||
let state = store.getState();
|
||||
let root = state.views?.root;
|
||||
let persist = state._persist; //eslint-disable-line no-underscore-dangle
|
||||
|
||||
if (root?.hydrationComplete && !executed) {
|
||||
if (callback && typeof callback === 'function') {
|
||||
executed = true;
|
||||
callback();
|
||||
}
|
||||
} else {
|
||||
const subscription = () => {
|
||||
if (store.getState().views?.root?.hydrationComplete && !executed) {
|
||||
state = store.getState();
|
||||
root = state.views?.root;
|
||||
persist = state._persist; //eslint-disable-line no-underscore-dangle
|
||||
if (root?.hydrationComplete && !executed) {
|
||||
unsubscribeFromStore();
|
||||
if (callback && typeof callback === 'function') {
|
||||
executed = true;
|
||||
|
|
@ -106,6 +113,9 @@ export function getStateForReset(initialState, currentState) {
|
|||
hydrationComplete: true,
|
||||
},
|
||||
},
|
||||
_persist: {
|
||||
rehydrated: true,
|
||||
},
|
||||
});
|
||||
|
||||
return resetState;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import initialState from 'app/initial_state';
|
||||
import {getStateForReset} from 'app/store/utils';
|
||||
import initialState from '@store/initial_state';
|
||||
import {getStateForReset} from '@store/utils';
|
||||
|
||||
/*
|
||||
const {currentUserId} = currentState.entities.users;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import LocalConfig from 'assets/config'; // eslint-disable-line
|
||||
|
||||
import store from 'app/store';
|
||||
import Store from '@store/store';
|
||||
|
||||
import {
|
||||
saveToTelemetryServer,
|
||||
|
|
@ -32,7 +32,7 @@ class Telemetry {
|
|||
}
|
||||
|
||||
canSendTelemetry() {
|
||||
const {config} = store.getState().entities.general;
|
||||
const {config} = Store.redux.getState().entities.general;
|
||||
return Boolean(!__DEV__ && config.EnableDiagnostics === 'true' && LocalConfig.TelemetryEnabled);
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +144,7 @@ class Telemetry {
|
|||
});
|
||||
});
|
||||
|
||||
const {config} = store.getState().entities.general;
|
||||
const {config} = Store.redux.getState().entities.general;
|
||||
const deviceInfo = getDeviceInfo();
|
||||
deviceInfo.server_version = config.Version;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,14 +17,16 @@ import {
|
|||
loadFromPushNotification,
|
||||
} from '@actions/views/root';
|
||||
|
||||
import {NavigationTypes, ViewTypes} from 'app/constants';
|
||||
import {getLocalizedMessage} from 'app/i18n';
|
||||
import {NavigationTypes, ViewTypes} from '@constants';
|
||||
import {getLocalizedMessage} from '@i18n';
|
||||
import {getCurrentLocale} from '@selectors/i18n';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import Store from '@store/store';
|
||||
import {waitForHydration} from '@store/utils';
|
||||
import {t} from '@utils/i18n';
|
||||
|
||||
import {getCurrentServerUrl, getAppCredentials} from 'app/init/credentials';
|
||||
import PushNotifications from 'app/push_notifications';
|
||||
import {getCurrentLocale} from 'app/selectors/i18n';
|
||||
import EphemeralStore from 'app/store/ephemeral_store';
|
||||
import {waitForHydration} from 'app/store/utils';
|
||||
import {t} from 'app/utils/i18n';
|
||||
|
||||
class PushNotificationUtils {
|
||||
constructor() {
|
||||
|
|
@ -32,11 +34,8 @@ class PushNotificationUtils {
|
|||
this.replyNotificationData = null;
|
||||
}
|
||||
|
||||
configure = (store) => {
|
||||
this.store = store;
|
||||
|
||||
configure = () => {
|
||||
PushNotifications.configure({
|
||||
reduxStore: store,
|
||||
onRegister: this.onRegisterDevice,
|
||||
onNotification: this.onPushNotification,
|
||||
onReply: this.onPushNotificationReply,
|
||||
|
|
@ -48,7 +47,7 @@ class PushNotificationUtils {
|
|||
loadFromNotification = async (notification) => {
|
||||
// Set appStartedFromPushNotification to avoid channel screen to call selectInitialChannel
|
||||
EphemeralStore.setStartFromNotification(true);
|
||||
await this.store.dispatch(loadFromPushNotification(notification));
|
||||
await Store.redux.dispatch(loadFromPushNotification(notification));
|
||||
|
||||
// if we have a componentId means that the app is already initialized
|
||||
const componentId = EphemeralStore.getNavigationTopComponentId();
|
||||
|
|
@ -64,7 +63,7 @@ class PushNotificationUtils {
|
|||
};
|
||||
|
||||
onPushNotification = async (deviceNotification) => {
|
||||
const {dispatch, getState} = this.store;
|
||||
const {dispatch, getState} = Store.redux;
|
||||
const {data, foreground, message, userInteraction} = deviceNotification;
|
||||
const notification = {
|
||||
data,
|
||||
|
|
@ -80,7 +79,7 @@ class PushNotificationUtils {
|
|||
if (foreground) {
|
||||
EventEmitter.emit(ViewTypes.NOTIFICATION_IN_APP, notification);
|
||||
} else if (userInteraction && !notification?.data?.localNotification) {
|
||||
waitForHydration(this.store, () => {
|
||||
waitForHydration(Store.redux, () => {
|
||||
this.loadFromNotification(notification);
|
||||
});
|
||||
}
|
||||
|
|
@ -88,7 +87,7 @@ class PushNotificationUtils {
|
|||
};
|
||||
|
||||
onPushNotificationReply = async (data, text, completion) => {
|
||||
const {dispatch, getState} = this.store;
|
||||
const {dispatch, getState} = Store.redux;
|
||||
const state = getState();
|
||||
const credentials = await getAppCredentials(); // TODO Change to handle multiple servers
|
||||
const url = await getCurrentServerUrl(); // TODO Change to handle multiple servers
|
||||
|
|
@ -149,7 +148,7 @@ class PushNotificationUtils {
|
|||
};
|
||||
|
||||
onRegisterDevice = (data) => {
|
||||
const {dispatch} = this.store;
|
||||
const {dispatch} = Store.redux;
|
||||
|
||||
let prefix;
|
||||
if (Platform.OS === 'ios') {
|
||||
|
|
@ -164,7 +163,7 @@ class PushNotificationUtils {
|
|||
EphemeralStore.deviceToken = `${prefix}:${data.token}`;
|
||||
|
||||
// TODO: Remove when realm is ready
|
||||
waitForHydration(this.store, () => {
|
||||
waitForHydration(Store.redux, () => {
|
||||
this.configured = true;
|
||||
dispatch(setDeviceToken(EphemeralStore.deviceToken));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,11 +16,14 @@ module.exports = {
|
|||
'@actions': './app/actions',
|
||||
'@constants': './app/constants',
|
||||
'@i18n': './app/i18n',
|
||||
'@init': './app/init',
|
||||
'@mm-redux': './app/mm-redux',
|
||||
'@screens': './app/screens',
|
||||
'@selectors': './app/selectors',
|
||||
'@store': './app/store',
|
||||
'@telemetry': './app/telemetry',
|
||||
'@utils': './app/utils',
|
||||
'@websocket': './app/client/websocket',
|
||||
'@mm-redux': './app/mm-redux',
|
||||
},
|
||||
}],
|
||||
],
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
00E356F31AD99517003FC87E /* MattermostTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* MattermostTests.m */; };
|
||||
0111A42B7F264BCF8CBDE3ED /* OpenSans-ExtraBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FBBEC29EE2D3418D9AC33BD5 /* OpenSans-ExtraBoldItalic.ttf */; };
|
||||
0C0D24F53F254F75869E5951 /* OpenSans-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 41F3AFE83AAF4B74878AB78A /* OpenSans-Italic.ttf */; };
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
|
||||
|
|
@ -61,7 +60,6 @@
|
|||
A08D512E7ADC40CCAD055A9E /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BC977883E2624E05975CA65B /* OpenSans-Regular.ttf */; };
|
||||
AA9605CFDA8E4E7CB8A041BF /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C5BD64DE829E455A997DCAD5 /* Roboto-Regular.ttf */; };
|
||||
ABF5F93B1D0A47BAACEAC376 /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34B20A903038487E8D7DEA1E /* Roboto-Light.ttf */; };
|
||||
B9899621C7F80C4B5C51BB8B /* libPods-MattermostTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BB993883BB9CFFBD2BEB9FC /* libPods-MattermostTests.a */; };
|
||||
C99BB3F4E4564F01A531FBEA /* Roboto-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 09FA716C56874BDDA30FE8C4 /* Roboto-Medium.ttf */; };
|
||||
D719A67137964F08BE47A5FC /* OpenSans-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3647DF63D6764CF093375861 /* OpenSans-ExtraBold.ttf */; };
|
||||
DDE492F7425D451884DAA088 /* Roboto-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6EFF13DD24CE4E26953E598A /* Roboto-Black.ttf */; };
|
||||
|
|
@ -72,13 +70,6 @@
|
|||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
|
||||
remoteInfo = Mattermost;
|
||||
};
|
||||
7F240A21220D3A2300637665 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
|
|
@ -149,7 +140,6 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
00E356EE1AD99517003FC87E /* MattermostTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MattermostTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
00E356F21AD99517003FC87E /* MattermostTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MattermostTests.m; sourceTree = "<group>"; };
|
||||
031EF04FB2D14EEFAACBAA1A /* Roboto-Italic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Italic.ttf"; path = "../assets/fonts/Roboto-Italic.ttf"; sourceTree = "<group>"; };
|
||||
|
|
@ -165,7 +155,6 @@
|
|||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Mattermost/main.m; sourceTree = "<group>"; };
|
||||
25BF2BACE89201DE6E585B7E /* Pods-Mattermost.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mattermost.release.xcconfig"; path = "Target Support Files/Pods-Mattermost/Pods-Mattermost.release.xcconfig"; sourceTree = "<group>"; };
|
||||
263D389521BE459684618177 /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = "<group>"; };
|
||||
297AAFCCF0BD99FC109DA2BC /* Pods-MattermostTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MattermostTests.release.xcconfig"; path = "Target Support Files/Pods-MattermostTests/Pods-MattermostTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
2CD0ACABF8EE4E1A94982CC8 /* Zocial.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Zocial.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = "<group>"; };
|
||||
32AC3D4EA79E44738A6E9766 /* OpenSans-BoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "OpenSans-BoldItalic.ttf"; path = "../assets/fonts/OpenSans-BoldItalic.ttf"; sourceTree = "<group>"; };
|
||||
34B20A903038487E8D7DEA1E /* Roboto-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Light.ttf"; path = "../assets/fonts/Roboto-Light.ttf"; sourceTree = "<group>"; };
|
||||
|
|
@ -189,8 +178,6 @@
|
|||
536CC6C123E79287002C478C /* RNNotificationEventHandler+HandleReplyAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "RNNotificationEventHandler+HandleReplyAction.m"; path = "Mattermost/RNNotificationEventHandler+HandleReplyAction.m"; sourceTree = "<group>"; };
|
||||
536CC6C223E79287002C478C /* RNNotificationEventHandler+HandleReplyAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "RNNotificationEventHandler+HandleReplyAction.h"; path = "Mattermost/RNNotificationEventHandler+HandleReplyAction.h"; sourceTree = "<group>"; };
|
||||
563B800AC53A447FA18F47D3 /* FontAwesome5_Solid.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome5_Solid.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf"; sourceTree = "<group>"; };
|
||||
57CB4735B7E57B50D0B50E16 /* Pods-MattermostTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MattermostTests.debug.xcconfig"; path = "Target Support Files/Pods-MattermostTests/Pods-MattermostTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
5BB993883BB9CFFBD2BEB9FC /* libPods-MattermostTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MattermostTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6561AEAC21CC40B8A72ABB93 /* OpenSans-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "OpenSans-Light.ttf"; path = "../assets/fonts/OpenSans-Light.ttf"; sourceTree = "<group>"; };
|
||||
6BAF8296411D4657B5A0E8F8 /* libRNReactNativeDocViewer.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNReactNativeDocViewer.a; sourceTree = "<group>"; };
|
||||
6EFF13DD24CE4E26953E598A /* Roboto-Black.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Black.ttf"; path = "../assets/fonts/Roboto-Black.ttf"; sourceTree = "<group>"; };
|
||||
|
|
@ -272,14 +259,6 @@
|
|||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
00E356EB1AD99517003FC87E /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B9899621C7F80C4B5C51BB8B /* libPods-MattermostTests.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -412,8 +391,6 @@
|
|||
children = (
|
||||
EB4F0DF36537B0B21BE962FB /* Pods-Mattermost.debug.xcconfig */,
|
||||
25BF2BACE89201DE6E585B7E /* Pods-Mattermost.release.xcconfig */,
|
||||
57CB4735B7E57B50D0B50E16 /* Pods-MattermostTests.debug.xcconfig */,
|
||||
297AAFCCF0BD99FC109DA2BC /* Pods-MattermostTests.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -448,7 +425,6 @@
|
|||
7F43D6051F6BF9EB001FC614 /* libPods-Mattermost.a */,
|
||||
81061F4CBB31484A94D5A8EE /* libz.tbd */,
|
||||
8DEEFB3ED6175724A2653247 /* libPods-Mattermost.a */,
|
||||
5BB993883BB9CFFBD2BEB9FC /* libPods-MattermostTests.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -533,7 +509,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07F961A680F5B00A75B9A /* Mattermost.app */,
|
||||
00E356EE1AD99517003FC87E /* MattermostTests.xctest */,
|
||||
7F240A19220D3A2300637665 /* MattermostShare.appex */,
|
||||
7F581D32221ED5C60099E66B /* NotificationService.appex */,
|
||||
);
|
||||
|
|
@ -543,25 +518,6 @@
|
|||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
00E356ED1AD99517003FC87E /* MattermostTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "MattermostTests" */;
|
||||
buildPhases = (
|
||||
C540C9BFE580D99F0700EC9D /* [CP] Check Pods Manifest.lock */,
|
||||
00E356EA1AD99517003FC87E /* Sources */,
|
||||
00E356EB1AD99517003FC87E /* Frameworks */,
|
||||
00E356EC1AD99517003FC87E /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
00E356F51AD99517003FC87E /* PBXTargetDependency */,
|
||||
);
|
||||
name = MattermostTests;
|
||||
productName = MattermostTests;
|
||||
productReference = 00E356EE1AD99517003FC87E /* MattermostTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
13B07F861A680F5B00A75B9A /* Mattermost */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Mattermost" */;
|
||||
|
|
@ -634,10 +590,6 @@
|
|||
LastUpgradeCheck = 820;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
TargetAttributes = {
|
||||
00E356ED1AD99517003FC87E = {
|
||||
CreatedOnToolsVersion = 6.2;
|
||||
TestTargetID = 13B07F861A680F5B00A75B9A;
|
||||
};
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
DevelopmentTeam = UQ8HT4Q2XM;
|
||||
LastSwiftMigration = 1010;
|
||||
|
|
@ -703,7 +655,6 @@
|
|||
projectRoot = "";
|
||||
targets = (
|
||||
13B07F861A680F5B00A75B9A /* Mattermost */,
|
||||
00E356ED1AD99517003FC87E /* MattermostTests */,
|
||||
7F240A18220D3A2300637665 /* MattermostShare */,
|
||||
7F581D31221ED5C60099E66B /* NotificationService */,
|
||||
);
|
||||
|
|
@ -721,13 +672,6 @@
|
|||
/* End PBXReferenceProxy section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
00E356EC1AD99517003FC87E /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -820,58 +764,6 @@
|
|||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
1D22DC6FC0A37F7F657B8274 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${SRCROOT}/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-youtube/assets/YTPlayerView-iframe-player.html",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/YTPlayerView-iframe-player.html",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
AE4769B235D14E6C9C64EA78 /* Upload Debug Symbols to Sentry */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -886,28 +778,6 @@
|
|||
shellPath = /bin/sh;
|
||||
shellScript = "./uploadDebugSymbols.sh\n";
|
||||
};
|
||||
C540C9BFE580D99F0700EC9D /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-MattermostTests-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
ED4C644925C525E30315E09E /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -961,14 +831,6 @@
|
|||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
00E356EA1AD99517003FC87E /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
00E356F31AD99517003FC87E /* MattermostTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
13B07F871A680F5B00A75B9A /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -1015,11 +877,6 @@
|
|||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 13B07F861A680F5B00A75B9A /* Mattermost */;
|
||||
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
|
||||
};
|
||||
7F240A22220D3A2300637665 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 7F240A18220D3A2300637665 /* MattermostShare */;
|
||||
|
|
@ -1059,55 +916,6 @@
|
|||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
00E356F61AD99517003FC87E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 57CB4735B7E57B50D0B50E16 /* Pods-MattermostTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = MattermostTests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mattermost.rnbeta;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Mattermost.app/Mattermost";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
00E356F71AD99517003FC87E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 297AAFCCF0BD99FC109DA2BC /* Pods-MattermostTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = MattermostTests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mattermost.rnbeta;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Mattermost.app/Mattermost";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = EB4F0DF36537B0B21BE962FB /* Pods-Mattermost.debug.xcconfig */;
|
||||
|
|
@ -1428,15 +1236,6 @@
|
|||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "MattermostTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
00E356F61AD99517003FC87E /* Debug */,
|
||||
00E356F71AD99517003FC87E /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Mattermost" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.30.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>282</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
#define TIMEOUT_SECONDS 600
|
||||
#define TEXT_TO_LOOK_FOR @"Welcome to React Native!"
|
||||
|
||||
@interface MattermostTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation MattermostTests
|
||||
|
||||
- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
|
||||
{
|
||||
if (test(view)) {
|
||||
return YES;
|
||||
}
|
||||
for (UIView *subview in [view subviews]) {
|
||||
if ([self findSubviewInView:subview matching:test]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)testRendersWelcomeScreen
|
||||
{
|
||||
UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
|
||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
|
||||
BOOL foundElement = NO;
|
||||
|
||||
__block NSString *redboxError = nil;
|
||||
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
|
||||
if (level >= RCTLogLevelError) {
|
||||
redboxError = message;
|
||||
}
|
||||
});
|
||||
|
||||
while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
|
||||
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||
|
||||
foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
|
||||
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}];
|
||||
}
|
||||
|
||||
RCTSetLogFunction(RCTDefaultLogFunction);
|
||||
|
||||
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
|
||||
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
@ -42,10 +42,5 @@ target 'Mattermost' do
|
|||
pod 'XCDYouTubeKit', '2.8.2'
|
||||
pod 'Swime', '3.0.6'
|
||||
|
||||
target 'MattermostTests' do
|
||||
inherit! :search_paths
|
||||
# Pods for testing
|
||||
end
|
||||
|
||||
use_native_modules!
|
||||
end
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ PODS:
|
|||
- libwebp/mux (1.1.0):
|
||||
- libwebp/demux
|
||||
- libwebp/webp (1.1.0)
|
||||
- MMKV (1.0.24)
|
||||
- MMKV (1.1.0):
|
||||
- MMKVCore (~> 1.1.0)
|
||||
- MMKVCore (1.1.0)
|
||||
- Permission-Camera (2.0.10):
|
||||
- RNPermissions
|
||||
- Permission-PhotoLibrary (2.0.10):
|
||||
|
|
@ -213,8 +215,8 @@ PODS:
|
|||
- React
|
||||
- react-native-image-picker (2.3.1):
|
||||
- React
|
||||
- react-native-mmkv-storage (0.2.2):
|
||||
- MMKV (= 1.0.24)
|
||||
- react-native-mmkv-storage (0.3.1):
|
||||
- MMKV (= 1.1.0)
|
||||
- React
|
||||
- react-native-netinfo (4.4.0):
|
||||
- React
|
||||
|
|
@ -384,6 +386,7 @@ SPEC REPOS:
|
|||
- boost-for-react-native
|
||||
- libwebp
|
||||
- MMKV
|
||||
- MMKVCore
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- Sentry
|
||||
|
|
@ -517,7 +520,8 @@ SPEC CHECKSUMS:
|
|||
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
|
||||
jail-monkey: d7c5048b2336f22ee9c9e0efa145f1f917338ea9
|
||||
libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
|
||||
MMKV: 758b2edee46b08bdd958db4169191afb9a6d4ebd
|
||||
MMKV: 7bb6c30f9ff2ea45bc2398c86e66dd1cb63cfe20
|
||||
MMKVCore: f1e0aad4fc330e7cbfbdff16720017bf0973db5a
|
||||
Permission-Camera: 8f0e5decca5f28f70f28a8dc31f012c9bad40ad8
|
||||
Permission-PhotoLibrary: b209bf23b784c9e1409a57d81c6d11ab1d3079c1
|
||||
RCTRequired: b153add4da6e7dbc44aebf93f3cf4fcae392ddf1
|
||||
|
|
@ -535,7 +539,7 @@ SPEC CHECKSUMS:
|
|||
react-native-document-picker: 0573c02d742d4bef38a5d16b5f039754cfa69888
|
||||
react-native-hw-keyboard-event: b517cefb8d5c659a38049c582de85ff43337dc53
|
||||
react-native-image-picker: 668e72d0277dc8c12ae90e835507c1eddd2e4f85
|
||||
react-native-mmkv-storage: 84162ebe353ecf7476d235c47becade29789ae2c
|
||||
react-native-mmkv-storage: d413e1e00b1f410a744e1d547a912a1f87e67260
|
||||
react-native-netinfo: 892a5130be97ff8bb69c523739c424a2ffc296d1
|
||||
react-native-notifications: d5cb54ef8bf3004dcb56c887650dea08ecbddee7
|
||||
react-native-passcode-status: 88c4f6e074328bc278bd127646b6c694ad5a530a
|
||||
|
|
@ -575,6 +579,6 @@ SPEC CHECKSUMS:
|
|||
Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b
|
||||
YoutubePlayer-in-WKWebView: af2f5929fc78882d94bfdfeea999b661b78d9717
|
||||
|
||||
PODFILE CHECKSUM: 8199a7b8e5d4cc8c741ea6292c067c33ad835da9
|
||||
PODFILE CHECKSUM: 77806e4d5b8185ba5cb04fc8d698bcc9123cb578
|
||||
|
||||
COCOAPODS: 1.7.5
|
||||
|
|
|
|||
19
package-lock.json
generated
19
package-lock.json
generated
|
|
@ -4855,6 +4855,11 @@
|
|||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.24.tgz",
|
||||
"integrity": "sha512-bImQZbBv86zcOWOq6fLg7r4aqMx8fScdmykA7cSh+gH1Yh8AM0Dbw0gHYrsOrza6oBBnkK+/OaR+UAa9UsMrDw=="
|
||||
},
|
||||
"debounce": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz",
|
||||
"integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
|
|
@ -15241,9 +15246,9 @@
|
|||
"integrity": "sha512-36cYGZGCG82pMiVJbQa5WMA93khP4v5JqLutFkMyB/eRpCULHmojNIBlbUPIY9SCeN4sg5VBRFTVGCtTg2r2kA=="
|
||||
},
|
||||
"react-native-mmkv-storage": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-mmkv-storage/-/react-native-mmkv-storage-0.2.2.tgz",
|
||||
"integrity": "sha512-QBMQzz7wpS2DGjOn5kIppqiD26p+jOJaJ0Am4DG2PImcUGe9iub5mkamz02mlqjyhq+8EhUes533uLK98APtmw=="
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-mmkv-storage/-/react-native-mmkv-storage-0.3.1.tgz",
|
||||
"integrity": "sha512-rNFgtgzvCsDsXU5MnKz5PA5GMkq8TyyixnGSXBZ3tIatwN/3Lq09OJA0YR7UEwsQOuIcnPsEtGEf7suBHo/SwA=="
|
||||
},
|
||||
"react-native-navigation": {
|
||||
"version": "6.3.0",
|
||||
|
|
@ -15330,6 +15335,14 @@
|
|||
"hoist-non-react-statics": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"react-native-screens": {
|
||||
"version": "1.0.0-alpha.23",
|
||||
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-1.0.0-alpha.23.tgz",
|
||||
"integrity": "sha512-tOxHGQUN83MTmQB4ghoQkibqOdGiX4JQEmeyEv96MKWO/x8T2PJv84ECUos9hD3blPRQwVwSpAid1PPPhrVEaw==",
|
||||
"requires": {
|
||||
"debounce": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"react-native-section-list-get-item-layout": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-section-list-get-item-layout/-/react-native-section-list-get-item-layout-2.2.3.tgz",
|
||||
|
|
|
|||
|
|
@ -50,12 +50,13 @@
|
|||
"react-native-keychain": "4.0.5",
|
||||
"react-native-linear-gradient": "2.5.6",
|
||||
"react-native-local-auth": "1.6.0",
|
||||
"react-native-mmkv-storage": "0.2.2",
|
||||
"react-native-mmkv-storage": "0.3.1",
|
||||
"react-native-navigation": "6.3.0",
|
||||
"react-native-notifications": "2.0.6",
|
||||
"react-native-passcode-status": "1.1.2",
|
||||
"react-native-permissions": "2.0.10",
|
||||
"react-native-safe-area": "0.5.1",
|
||||
"react-native-screens": "1.0.0-alpha.23",
|
||||
"react-native-section-list-get-item-layout": "2.2.3",
|
||||
"react-native-slider": "0.11.0",
|
||||
"react-native-status-bar-size": "0.3.3",
|
||||
|
|
@ -143,7 +144,7 @@
|
|||
"check": "eslint --ignore-path .gitignore --ignore-pattern node_modules --quiet . && npm run tsc",
|
||||
"fix": "eslint --ignore-path .gitignore --ignore-pattern node_modules --quiet . --fix",
|
||||
"postinstall": "make post-install",
|
||||
"tsc": "tsc --noEmit --skipLibCheck",
|
||||
"tsc": "NODE_OPTIONS=--max_old_space_size=12000 tsc --noEmit",
|
||||
"test": "jest --forceExit --detectOpenHandles",
|
||||
"test:watch": "jest --watch",
|
||||
"test:coverage": "jest --coverage",
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ module.exports = [
|
|||
'app/actions/helpers/channels.ts',
|
||||
'app/actions/navigation/index.js',
|
||||
'app/actions/views/channel.js',
|
||||
'app/actions/views/emoji.js',
|
||||
'app/actions/views/post.js',
|
||||
'app/actions/views/login.js',
|
||||
'app/actions/views/root.js',
|
||||
'app/actions/views/select_server.js',
|
||||
'app/actions/views/user.js',
|
||||
|
|
@ -27,62 +27,26 @@ module.exports = [
|
|||
'app/init/emm_provider.js',
|
||||
'app/init/fetch.js',
|
||||
'app/init/global_event_handler.js',
|
||||
'app/initial_state.js',
|
||||
'app/mattermost.js',
|
||||
'app/mattermost_bucket/index.js',
|
||||
'app/mattermost_managed/index.js',
|
||||
'app/mattermost_managed/mattermost-managed.android.js',
|
||||
'app/push_notifications/index.js',
|
||||
'app/push_notifications/push_notifications.android.js',
|
||||
'app/reducers/app/build.js',
|
||||
'app/reducers/app/index.js',
|
||||
'app/reducers/app/version.js',
|
||||
'app/reducers/device/connection.js',
|
||||
'app/reducers/device/dimension.js',
|
||||
'app/reducers/device/index.js',
|
||||
'app/reducers/device/is_tablet.js',
|
||||
'app/reducers/device/orientation.js',
|
||||
'app/reducers/device/status_bar.js',
|
||||
'app/reducers/index.js',
|
||||
'app/reducers/navigation/index.js',
|
||||
'app/reducers/views/announcement.js',
|
||||
'app/reducers/views/channel.js',
|
||||
'app/reducers/views/client_upgrade.js',
|
||||
'app/reducers/views/emoji.js',
|
||||
'app/reducers/views/extension.js',
|
||||
'app/reducers/views/i18n.js',
|
||||
'app/reducers/views/index.js',
|
||||
'app/reducers/views/post.js',
|
||||
'app/reducers/views/recent_emojis.js',
|
||||
'app/reducers/views/root.js',
|
||||
'app/reducers/views/search.js',
|
||||
'app/reducers/views/select_server.js',
|
||||
'app/reducers/views/team.js',
|
||||
'app/reducers/views/thread.js',
|
||||
'app/reducers/views/user.js',
|
||||
'app/mm-redux/action_types/bots.ts',
|
||||
'app/mm-redux/action_types/channels.ts',
|
||||
'app/mm-redux/action_types/emojis.ts',
|
||||
'app/mm-redux/action_types/errors.ts',
|
||||
'app/mm-redux/action_types/files.ts',
|
||||
'app/mm-redux/action_types/general.ts',
|
||||
'app/mm-redux/action_types/gifs.ts',
|
||||
'app/mm-redux/action_types/groups.ts',
|
||||
'app/mm-redux/action_types/index.ts',
|
||||
'app/mm-redux/action_types/integrations.ts',
|
||||
'app/mm-redux/action_types/jobs.ts',
|
||||
'app/mm-redux/action_types/plugins.ts',
|
||||
'app/mm-redux/action_types/posts.ts',
|
||||
'app/mm-redux/action_types/preferences.ts',
|
||||
'app/mm-redux/action_types/roles.ts',
|
||||
'app/mm-redux/action_types/schemes.ts',
|
||||
'app/mm-redux/action_types/search.ts',
|
||||
'app/mm-redux/action_types/teams.ts',
|
||||
'app/mm-redux/action_types/users.ts',
|
||||
'app/mm-redux/actions/channels.ts',
|
||||
'app/mm-redux/actions/emojis.ts',
|
||||
'app/mm-redux/actions/errors.ts',
|
||||
'app/mm-redux/actions/files.ts',
|
||||
'app/mm-redux/actions/general.ts',
|
||||
'app/mm-redux/actions/helpers.ts',
|
||||
'app/mm-redux/actions/posts.ts',
|
||||
|
|
@ -95,19 +59,19 @@ module.exports = [
|
|||
'app/mm-redux/client/fetch_etag.ts',
|
||||
'app/mm-redux/client/index.ts',
|
||||
'app/mm-redux/constants/emoji.ts',
|
||||
'app/mm-redux/constants/files.ts',
|
||||
'app/mm-redux/constants/general.ts',
|
||||
'app/mm-redux/constants/groups.ts',
|
||||
'app/mm-redux/constants/index.ts',
|
||||
'app/mm-redux/constants/permissions.ts',
|
||||
'app/mm-redux/constants/plugins.ts',
|
||||
'app/mm-redux/constants/posts.ts',
|
||||
'app/mm-redux/constants/preferences.ts',
|
||||
'app/mm-redux/constants/request_status.ts',
|
||||
'app/mm-redux/constants/roles.ts',
|
||||
'app/mm-redux/constants/stats.ts',
|
||||
'app/mm-redux/constants/teams.ts',
|
||||
'app/mm-redux/constants/users.ts',
|
||||
'app/mm-redux/reducers/entities/bots.ts',
|
||||
'app/mm-redux/reducers/entities/channel_categories.ts',
|
||||
'app/mm-redux/reducers/entities/channels.ts',
|
||||
'app/mm-redux/reducers/entities/emojis.ts',
|
||||
'app/mm-redux/reducers/entities/files.ts',
|
||||
|
|
@ -151,10 +115,6 @@ module.exports = [
|
|||
'app/mm-redux/selectors/entities/teams.ts',
|
||||
'app/mm-redux/selectors/entities/timezone.ts',
|
||||
'app/mm-redux/selectors/entities/users.ts',
|
||||
'app/mm-redux/store/helpers.ts',
|
||||
'app/mm-redux/store/index.ts',
|
||||
'app/mm-redux/store/initial_state.ts',
|
||||
'app/mm-redux/store/middleware.ts',
|
||||
'app/mm-redux/types/actions.ts',
|
||||
'app/mm-redux/utils/channel_utils.ts',
|
||||
'app/mm-redux/utils/emoji_utils.ts',
|
||||
|
|
@ -163,19 +123,54 @@ module.exports = [
|
|||
'app/mm-redux/utils/helpers.ts',
|
||||
'app/mm-redux/utils/i18n_utils.ts',
|
||||
'app/mm-redux/utils/key_mirror.ts',
|
||||
'app/mm-redux/utils/post_list.ts',
|
||||
'app/mm-redux/utils/post_utils.ts',
|
||||
'app/mm-redux/utils/preference_utils.ts',
|
||||
'app/mm-redux/utils/sentry.ts',
|
||||
'app/mm-redux/utils/team_utils.ts',
|
||||
'app/mm-redux/utils/timezone_utils.ts',
|
||||
'app/mm-redux/utils/user_utils.ts',
|
||||
'app/push_notifications/index.js',
|
||||
'app/push_notifications/push_notifications.android.js',
|
||||
'app/reducers/app/build.js',
|
||||
'app/reducers/app/index.js',
|
||||
'app/reducers/app/version.js',
|
||||
'app/reducers/device/connection.js',
|
||||
'app/reducers/device/dimension.js',
|
||||
'app/reducers/device/index.js',
|
||||
'app/reducers/device/is_tablet.js',
|
||||
'app/reducers/device/orientation.js',
|
||||
'app/reducers/device/status_bar.js',
|
||||
'app/reducers/index.js',
|
||||
'app/reducers/views/announcement.js',
|
||||
'app/reducers/views/channel.js',
|
||||
'app/reducers/views/client_upgrade.js',
|
||||
'app/reducers/views/emoji.js',
|
||||
'app/reducers/views/extension.js',
|
||||
'app/reducers/views/i18n.js',
|
||||
'app/reducers/views/index.js',
|
||||
'app/reducers/views/post.js',
|
||||
'app/reducers/views/recent_emojis.js',
|
||||
'app/reducers/views/root.js',
|
||||
'app/reducers/views/search.js',
|
||||
'app/reducers/views/select_server.js',
|
||||
'app/reducers/views/team.js',
|
||||
'app/reducers/views/thread.js',
|
||||
'app/reducers/views/user.js',
|
||||
'app/screens/index.js',
|
||||
'app/selectors/channel.js',
|
||||
'app/selectors/i18n.js',
|
||||
'app/store/ephemeral_store.js',
|
||||
'app/store/index.js',
|
||||
'app/store/middleware.js',
|
||||
'app/store/store.js',
|
||||
'app/store/thunk.js',
|
||||
'app/store/helpers.ts',
|
||||
'app/store/index.ts',
|
||||
'app/store/initial_state.js',
|
||||
'app/store/middlewares/helpers.js',
|
||||
'app/store/middlewares/index.ts',
|
||||
'app/store/middlewares/message_retention.js',
|
||||
'app/store/middlewares/sentry.js',
|
||||
'app/store/middlewares/thunk.js',
|
||||
'app/store/mmkv_adapter.ts',
|
||||
'app/store/store.ts',
|
||||
'app/store/utils.js',
|
||||
'app/telemetry/index.js',
|
||||
'app/telemetry/telemetry.android.js',
|
||||
|
|
@ -184,13 +179,11 @@ module.exports = [
|
|||
'app/utils/file.js',
|
||||
'app/utils/general.js',
|
||||
'app/utils/i18n.js',
|
||||
'app/utils/image_cache_manager.js',
|
||||
'app/utils/preferences.js',
|
||||
'app/utils/push_notifications.js',
|
||||
'app/utils/security.js',
|
||||
'app/utils/segment.js',
|
||||
'app/utils/sentry/index.js',
|
||||
'app/utils/sentry/middleware.js',
|
||||
'app/utils/time_tracker.js',
|
||||
'app/utils/timezone.js',
|
||||
'dist/assets/config.json',
|
||||
|
|
@ -223,6 +216,7 @@ module.exports = [
|
|||
'node_modules/@babel/runtime/helpers/toConsumableArray.js',
|
||||
'node_modules/@babel/runtime/helpers/typeof.js',
|
||||
'node_modules/@babel/runtime/helpers/wrapNativeSuper.js',
|
||||
'node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js',
|
||||
'node_modules/@babel/runtime/regenerator/index.js',
|
||||
'node_modules/@react-native-community/async-storage/lib/AsyncStorage.js',
|
||||
'node_modules/@react-native-community/async-storage/lib/hooks.js',
|
||||
|
|
@ -258,10 +252,8 @@ module.exports = [
|
|||
'node_modules/fbjs/lib/performance.js',
|
||||
'node_modules/fbjs/lib/performanceNow.js',
|
||||
'node_modules/fbjs/lib/warning.js',
|
||||
'node_modules/get-params/index.js',
|
||||
'node_modules/harmony-reflect/reflect.js',
|
||||
'node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.cjs.js',
|
||||
'node_modules/ieee754/index.js',
|
||||
'node_modules/inherits/inherits_browser.js',
|
||||
'node_modules/intl-format-cache/dist/index.js',
|
||||
'node_modules/intl-format-cache/index.js',
|
||||
|
|
@ -285,16 +277,9 @@ module.exports = [
|
|||
'node_modules/intl/index.js',
|
||||
'node_modules/intl/lib/core.js',
|
||||
'node_modules/intl/locale-data/complete.js',
|
||||
'node_modules/invariant/browser.js',
|
||||
'node_modules/is-arguments/index.js',
|
||||
'node_modules/is-generator-function/index.js',
|
||||
'node_modules/jsan/index.js',
|
||||
'node_modules/jsan/lib/cycle.js',
|
||||
'node_modules/jsan/lib/index.js',
|
||||
'node_modules/jsan/lib/path-getter.js',
|
||||
'node_modules/jsan/lib/utils.js',
|
||||
'node_modules/json-stringify-safe/stringify.js',
|
||||
'node_modules/linked-list/_source/linked-list.js',
|
||||
'node_modules/linked-list/index.js',
|
||||
'node_modules/lodash.clonedeep/index.js',
|
||||
'node_modules/lodash.forin/index.js',
|
||||
'node_modules/lodash.get/index.js',
|
||||
|
|
@ -317,8 +302,6 @@ module.exports = [
|
|||
'node_modules/lodash/_apply.js',
|
||||
'node_modules/lodash/_arrayEach.js',
|
||||
'node_modules/lodash/_arrayFilter.js',
|
||||
'node_modules/lodash/_arrayIncludes.js',
|
||||
'node_modules/lodash/_arrayIncludesWith.js',
|
||||
'node_modules/lodash/_arrayLikeKeys.js',
|
||||
'node_modules/lodash/_arrayMap.js',
|
||||
'node_modules/lodash/_arrayPush.js',
|
||||
|
|
@ -332,9 +315,7 @@ module.exports = [
|
|||
'node_modules/lodash/_baseClamp.js',
|
||||
'node_modules/lodash/_baseClone.js',
|
||||
'node_modules/lodash/_baseCreate.js',
|
||||
'node_modules/lodash/_baseDifference.js',
|
||||
'node_modules/lodash/_baseEach.js',
|
||||
'node_modules/lodash/_baseFindIndex.js',
|
||||
'node_modules/lodash/_baseFlatten.js',
|
||||
'node_modules/lodash/_baseFor.js',
|
||||
'node_modules/lodash/_baseForOwn.js',
|
||||
|
|
@ -342,13 +323,11 @@ module.exports = [
|
|||
'node_modules/lodash/_baseGetAllKeys.js',
|
||||
'node_modules/lodash/_baseGetTag.js',
|
||||
'node_modules/lodash/_baseHasIn.js',
|
||||
'node_modules/lodash/_baseIndexOf.js',
|
||||
'node_modules/lodash/_baseIsArguments.js',
|
||||
'node_modules/lodash/_baseIsEqual.js',
|
||||
'node_modules/lodash/_baseIsEqualDeep.js',
|
||||
'node_modules/lodash/_baseIsMap.js',
|
||||
'node_modules/lodash/_baseIsMatch.js',
|
||||
'node_modules/lodash/_baseIsNaN.js',
|
||||
'node_modules/lodash/_baseIsNative.js',
|
||||
'node_modules/lodash/_baseIsSet.js',
|
||||
'node_modules/lodash/_baseIsTypedArray.js',
|
||||
|
|
@ -368,7 +347,6 @@ module.exports = [
|
|||
'node_modules/lodash/_baseTimes.js',
|
||||
'node_modules/lodash/_baseToString.js',
|
||||
'node_modules/lodash/_baseUnary.js',
|
||||
'node_modules/lodash/_baseUniq.js',
|
||||
'node_modules/lodash/_baseUnset.js',
|
||||
'node_modules/lodash/_cacheHas.js',
|
||||
'node_modules/lodash/_castFunction.js',
|
||||
|
|
@ -454,14 +432,12 @@ module.exports = [
|
|||
'node_modules/lodash/_stackGet.js',
|
||||
'node_modules/lodash/_stackHas.js',
|
||||
'node_modules/lodash/_stackSet.js',
|
||||
'node_modules/lodash/_strictIndexOf.js',
|
||||
'node_modules/lodash/_stringToPath.js',
|
||||
'node_modules/lodash/_toKey.js',
|
||||
'node_modules/lodash/_toSource.js',
|
||||
'node_modules/lodash/clone.js',
|
||||
'node_modules/lodash/cloneDeep.js',
|
||||
'node_modules/lodash/constant.js',
|
||||
'node_modules/lodash/difference.js',
|
||||
'node_modules/lodash/endsWith.js',
|
||||
'node_modules/lodash/eq.js',
|
||||
'node_modules/lodash/forEach.js',
|
||||
|
|
@ -490,7 +466,6 @@ module.exports = [
|
|||
'node_modules/lodash/last.js',
|
||||
'node_modules/lodash/lodash.js',
|
||||
'node_modules/lodash/map.js',
|
||||
'node_modules/lodash/mapValues.js',
|
||||
'node_modules/lodash/memoize.js',
|
||||
'node_modules/lodash/merge.js',
|
||||
'node_modules/lodash/noop.js',
|
||||
|
|
@ -502,7 +477,6 @@ module.exports = [
|
|||
'node_modules/lodash/toNumber.js',
|
||||
'node_modules/lodash/toPlainObject.js',
|
||||
'node_modules/lodash/toString.js',
|
||||
'node_modules/lodash/union.js',
|
||||
'node_modules/lodash/uniqueId.js',
|
||||
'node_modules/lodash/unset.js',
|
||||
'node_modules/metro/src/lib/bundle-modules/HMRClient.js',
|
||||
|
|
@ -512,7 +486,6 @@ module.exports = [
|
|||
'node_modules/moment-timezone/index.js',
|
||||
'node_modules/moment-timezone/moment-timezone.js',
|
||||
'node_modules/moment/moment.js',
|
||||
'node_modules/nanoid/non-secure/index.js',
|
||||
'node_modules/nullthrows/nullthrows.js',
|
||||
'node_modules/object-assign/index.js',
|
||||
'node_modules/promise/setimmediate/core.js',
|
||||
|
|
@ -524,9 +497,6 @@ module.exports = [
|
|||
'node_modules/prop-types/factoryWithTypeCheckers.js',
|
||||
'node_modules/prop-types/index.js',
|
||||
'node_modules/prop-types/lib/ReactPropTypesSecret.js',
|
||||
'node_modules/querystring/decode.js',
|
||||
'node_modules/querystring/encode.js',
|
||||
'node_modules/querystring/index.js',
|
||||
'node_modules/querystringify/index.js',
|
||||
'node_modules/react-intl/lib/index.js',
|
||||
'node_modules/react-intl/locale-data/en.js',
|
||||
|
|
@ -536,10 +506,6 @@ module.exports = [
|
|||
'node_modules/react-lifecycles-compat/react-lifecycles-compat.cjs.js',
|
||||
'node_modules/react-native-cookies/index.js',
|
||||
'node_modules/react-native-device-info/deviceinfo.js',
|
||||
'node_modules/react-native-gesture-handler/Directions.js',
|
||||
'node_modules/react-native-gesture-handler/DrawerLayout.js',
|
||||
'node_modules/react-native-gesture-handler/GestureButtons.js',
|
||||
'node_modules/react-native-gesture-handler/GestureComponents.js',
|
||||
'node_modules/react-native-gesture-handler/GestureHandler.js',
|
||||
'node_modules/react-native-gesture-handler/GestureHandlerButton.js',
|
||||
'node_modules/react-native-gesture-handler/GestureHandlerPropTypes.js',
|
||||
|
|
@ -549,19 +515,24 @@ module.exports = [
|
|||
'node_modules/react-native-gesture-handler/PlatformConstants.js',
|
||||
'node_modules/react-native-gesture-handler/RNGestureHandlerModule.js',
|
||||
'node_modules/react-native-gesture-handler/State.js',
|
||||
'node_modules/react-native-gesture-handler/Swipeable.js',
|
||||
'node_modules/react-native-gesture-handler/createHandler.js',
|
||||
'node_modules/react-native-gesture-handler/createNativeWrapper.js',
|
||||
'node_modules/react-native-gesture-handler/gestureHandlerRootHOC.js',
|
||||
'node_modules/react-native-gesture-handler/index.js',
|
||||
'node_modules/react-native-gesture-handler/touchables/GenericTouchable.js',
|
||||
'node_modules/react-native-gesture-handler/touchables/TouchableHighlight.js',
|
||||
'node_modules/react-native-gesture-handler/touchables/TouchableNativeFeedback.android.js',
|
||||
'node_modules/react-native-gesture-handler/touchables/TouchableOpacity.js',
|
||||
'node_modules/react-native-gesture-handler/touchables/TouchableWithoutFeedback.js',
|
||||
'node_modules/react-native-gesture-handler/touchables/index.js',
|
||||
'node_modules/react-native-haptic-feedback/index.js',
|
||||
'node_modules/react-native-keychain/index.js',
|
||||
'node_modules/react-native-mmkv-storage/index.js',
|
||||
'node_modules/react-native-mmkv-storage/src/api.js',
|
||||
'node_modules/react-native-mmkv-storage/src/encryption.js',
|
||||
'node_modules/react-native-mmkv-storage/src/indexer/arrays.js',
|
||||
'node_modules/react-native-mmkv-storage/src/indexer/booleans.js',
|
||||
'node_modules/react-native-mmkv-storage/src/indexer/indexer.js',
|
||||
'node_modules/react-native-mmkv-storage/src/indexer/maps.js',
|
||||
'node_modules/react-native-mmkv-storage/src/indexer/numbers.js',
|
||||
'node_modules/react-native-mmkv-storage/src/indexer/strings.js',
|
||||
'node_modules/react-native-mmkv-storage/src/keygen.js',
|
||||
'node_modules/react-native-mmkv-storage/src/loader.js',
|
||||
'node_modules/react-native-mmkv-storage/src/utils.js',
|
||||
'node_modules/react-native-navigation/lib/dist/Navigation.js',
|
||||
'node_modules/react-native-navigation/lib/dist/adapters/AppRegistryService.js',
|
||||
'node_modules/react-native-navigation/lib/dist/adapters/AssetResolver.js',
|
||||
|
|
@ -829,25 +800,25 @@ module.exports = [
|
|||
'node_modules/react-redux/lib/utils/verifyPlainObject.js',
|
||||
'node_modules/react-redux/lib/utils/warning.js',
|
||||
'node_modules/react-redux/node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.cjs.js',
|
||||
'node_modules/react-refresh/cjs/react-refresh-runtime.production.js',
|
||||
'node_modules/react-refresh/cjs/react-refresh-runtime.production.min.js',
|
||||
'node_modules/react-refresh/runtime.js',
|
||||
'node_modules/react/cjs/react.production.js',
|
||||
'node_modules/react/cjs/react.production.min.js',
|
||||
'node_modules/react/index.js',
|
||||
'node_modules/redux-action-buffer/index.js',
|
||||
'node_modules/redux-batched-actions/lib/index.js',
|
||||
'node_modules/redux-persist-transform-filter/dist/index.js',
|
||||
'node_modules/redux-persist/lib/autoRehydrate.js',
|
||||
'node_modules/redux-persist/lib/constants.js',
|
||||
'node_modules/redux-persist/lib/createPersistor.js',
|
||||
'node_modules/redux-persist/lib/createMigrate.js',
|
||||
'node_modules/redux-persist/lib/createPersistoid.js',
|
||||
'node_modules/redux-persist/lib/createTransform.js',
|
||||
'node_modules/redux-persist/lib/defaults/asyncLocalStorage.js',
|
||||
'node_modules/redux-persist/lib/getStoredState.js',
|
||||
'node_modules/redux-persist/lib/index.js',
|
||||
'node_modules/redux-persist/lib/persistCombineReducers.js',
|
||||
'node_modules/redux-persist/lib/persistReducer.js',
|
||||
'node_modules/redux-persist/lib/persistStore.js',
|
||||
'node_modules/redux-persist/lib/purgeStoredState.js',
|
||||
'node_modules/redux-persist/lib/utils/isStatePlainEnough.js',
|
||||
'node_modules/redux-persist/lib/utils/setImmediate.js',
|
||||
'node_modules/redux-thunk/lib/index.js',
|
||||
'node_modules/redux-persist/lib/stateReconciler/autoMergeLevel1.js',
|
||||
'node_modules/redux-reset/lib/index.js',
|
||||
'node_modules/redux/lib/redux.js',
|
||||
'node_modules/requires-port/index.js',
|
||||
'node_modules/reselect/lib/index.js',
|
||||
|
|
@ -858,13 +829,18 @@ module.exports = [
|
|||
'node_modules/rn-fetch-blob/ios.js',
|
||||
'node_modules/rn-fetch-blob/json-stream.js',
|
||||
'node_modules/rn-fetch-blob/polyfill/Fetch.js',
|
||||
'node_modules/rn-host-detect/index.js',
|
||||
'node_modules/sc-channel/index.js',
|
||||
'node_modules/sc-errors/decycle.js',
|
||||
'node_modules/sc-errors/index.js',
|
||||
'node_modules/sc-formatter/index.js',
|
||||
'node_modules/scheduler/cjs/scheduler-tracing.production.js',
|
||||
'node_modules/scheduler/cjs/scheduler.production.js',
|
||||
'node_modules/rn-fetch-blob/polyfill/File.js',
|
||||
'node_modules/rn-fetch-blob/polyfill/FileReader.js',
|
||||
'node_modules/rn-fetch-blob/polyfill/ProgressEvent.js',
|
||||
'node_modules/rn-fetch-blob/polyfill/XMLHttpRequest.js',
|
||||
'node_modules/rn-fetch-blob/polyfill/XMLHttpRequestEventTarget.js',
|
||||
'node_modules/rn-fetch-blob/polyfill/index.js',
|
||||
'node_modules/rn-fetch-blob/utils/log.js',
|
||||
'node_modules/rn-fetch-blob/utils/unicode.js',
|
||||
'node_modules/rn-fetch-blob/utils/uri.js',
|
||||
'node_modules/rn-fetch-blob/utils/uuid.js',
|
||||
'node_modules/scheduler/cjs/scheduler-tracing.production.min.js',
|
||||
'node_modules/scheduler/cjs/scheduler.production.min.js',
|
||||
'node_modules/scheduler/index.js',
|
||||
'node_modules/scheduler/tracing.js',
|
||||
'node_modules/semver/classes/comparator.js',
|
||||
|
|
@ -914,7 +890,6 @@ module.exports = [
|
|||
'node_modules/stacktrace-parser/dist/stack-trace-parser.cjs.js',
|
||||
'node_modules/symbol-observable/lib/index.js',
|
||||
'node_modules/symbol-observable/lib/ponyfill.js',
|
||||
'node_modules/tslib/tslib.js',
|
||||
'node_modules/url-parse/index.js',
|
||||
'node_modules/util/support/isBufferBrowser.js',
|
||||
'node_modules/util/support/types.js',
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
module.exports = [
|
||||
'./node_modules/app/actions/device/index.js',
|
||||
'./node_modules/app/actions/helpers/channels.ts',
|
||||
'./node_modules/app/actions/navigation/index.js',
|
||||
'./node_modules/app/actions/views/channel.js',
|
||||
'./node_modules/app/actions/views/emoji.js',
|
||||
'./node_modules/app/actions/views/post.js',
|
||||
'./node_modules/app/actions/views/login.js',
|
||||
'./node_modules/app/actions/views/root.js',
|
||||
'./node_modules/app/actions/views/select_server.js',
|
||||
'./node_modules/app/actions/views/user.js',
|
||||
|
|
@ -26,62 +27,26 @@ module.exports = [
|
|||
'./node_modules/app/init/emm_provider.js',
|
||||
'./node_modules/app/init/fetch.js',
|
||||
'./node_modules/app/init/global_event_handler.js',
|
||||
'./node_modules/app/initial_state.js',
|
||||
'./node_modules/app/mattermost.js',
|
||||
'./node_modules/app/mattermost_bucket/index.js',
|
||||
'./node_modules/app/mattermost_managed/index.js',
|
||||
'./node_modules/app/mattermost_managed/mattermost-managed.android.js',
|
||||
'./node_modules/app/push_notifications/index.js',
|
||||
'./node_modules/app/push_notifications/push_notifications.android.js',
|
||||
'./node_modules/app/reducers/app/build.js',
|
||||
'./node_modules/app/reducers/app/index.js',
|
||||
'./node_modules/app/reducers/app/version.js',
|
||||
'./node_modules/app/reducers/device/connection.js',
|
||||
'./node_modules/app/reducers/device/dimension.js',
|
||||
'./node_modules/app/reducers/device/index.js',
|
||||
'./node_modules/app/reducers/device/is_tablet.js',
|
||||
'./node_modules/app/reducers/device/orientation.js',
|
||||
'./node_modules/app/reducers/device/status_bar.js',
|
||||
'./node_modules/app/reducers/index.js',
|
||||
'./node_modules/app/reducers/navigation/index.js',
|
||||
'./node_modules/app/reducers/views/announcement.js',
|
||||
'./node_modules/app/reducers/views/channel.js',
|
||||
'./node_modules/app/reducers/views/client_upgrade.js',
|
||||
'./node_modules/app/reducers/views/emoji.js',
|
||||
'./node_modules/app/reducers/views/extension.js',
|
||||
'./node_modules/app/reducers/views/i18n.js',
|
||||
'./node_modules/app/reducers/views/index.js',
|
||||
'./node_modules/app/reducers/views/post.js',
|
||||
'./node_modules/app/reducers/views/recent_emojis.js',
|
||||
'./node_modules/app/reducers/views/root.js',
|
||||
'./node_modules/app/reducers/views/search.js',
|
||||
'./node_modules/app/reducers/views/select_server.js',
|
||||
'./node_modules/app/reducers/views/team.js',
|
||||
'./node_modules/app/reducers/views/thread.js',
|
||||
'./node_modules/app/reducers/views/user.js',
|
||||
'./node_modules/app/mm-redux/action_types/bots.ts',
|
||||
'./node_modules/app/mm-redux/action_types/channels.ts',
|
||||
'./node_modules/app/mm-redux/action_types/emojis.ts',
|
||||
'./node_modules/app/mm-redux/action_types/errors.ts',
|
||||
'./node_modules/app/mm-redux/action_types/files.ts',
|
||||
'./node_modules/app/mm-redux/action_types/general.ts',
|
||||
'./node_modules/app/mm-redux/action_types/gifs.ts',
|
||||
'./node_modules/app/mm-redux/action_types/groups.ts',
|
||||
'./node_modules/app/mm-redux/action_types/index.ts',
|
||||
'./node_modules/app/mm-redux/action_types/integrations.ts',
|
||||
'./node_modules/app/mm-redux/action_types/jobs.ts',
|
||||
'./node_modules/app/mm-redux/action_types/plugins.ts',
|
||||
'./node_modules/app/mm-redux/action_types/posts.ts',
|
||||
'./node_modules/app/mm-redux/action_types/preferences.ts',
|
||||
'./node_modules/app/mm-redux/action_types/roles.ts',
|
||||
'./node_modules/app/mm-redux/action_types/schemes.ts',
|
||||
'./node_modules/app/mm-redux/action_types/search.ts',
|
||||
'./node_modules/app/mm-redux/action_types/teams.ts',
|
||||
'./node_modules/app/mm-redux/action_types/users.ts',
|
||||
'./node_modules/app/mm-redux/actions/channels.ts',
|
||||
'./node_modules/app/mm-redux/actions/emojis.ts',
|
||||
'./node_modules/app/mm-redux/actions/errors.ts',
|
||||
'./node_modules/app/mm-redux/actions/files.ts',
|
||||
'./node_modules/app/mm-redux/actions/general.ts',
|
||||
'./node_modules/app/mm-redux/actions/helpers.ts',
|
||||
'./node_modules/app/mm-redux/actions/posts.ts',
|
||||
|
|
@ -94,19 +59,19 @@ module.exports = [
|
|||
'./node_modules/app/mm-redux/client/fetch_etag.ts',
|
||||
'./node_modules/app/mm-redux/client/index.ts',
|
||||
'./node_modules/app/mm-redux/constants/emoji.ts',
|
||||
'./node_modules/app/mm-redux/constants/files.ts',
|
||||
'./node_modules/app/mm-redux/constants/general.ts',
|
||||
'./node_modules/app/mm-redux/constants/groups.ts',
|
||||
'./node_modules/app/mm-redux/constants/index.ts',
|
||||
'./node_modules/app/mm-redux/constants/permissions.ts',
|
||||
'./node_modules/app/mm-redux/constants/plugins.ts',
|
||||
'./node_modules/app/mm-redux/constants/posts.ts',
|
||||
'./node_modules/app/mm-redux/constants/preferences.ts',
|
||||
'./node_modules/app/mm-redux/constants/request_status.ts',
|
||||
'./node_modules/app/mm-redux/constants/roles.ts',
|
||||
'./node_modules/app/mm-redux/constants/stats.ts',
|
||||
'./node_modules/app/mm-redux/constants/teams.ts',
|
||||
'./node_modules/app/mm-redux/constants/users.ts',
|
||||
'./node_modules/app/mm-redux/reducers/entities/bots.ts',
|
||||
'./node_modules/app/mm-redux/reducers/entities/channel_categories.ts',
|
||||
'./node_modules/app/mm-redux/reducers/entities/channels.ts',
|
||||
'./node_modules/app/mm-redux/reducers/entities/emojis.ts',
|
||||
'./node_modules/app/mm-redux/reducers/entities/files.ts',
|
||||
|
|
@ -150,10 +115,6 @@ module.exports = [
|
|||
'./node_modules/app/mm-redux/selectors/entities/teams.ts',
|
||||
'./node_modules/app/mm-redux/selectors/entities/timezone.ts',
|
||||
'./node_modules/app/mm-redux/selectors/entities/users.ts',
|
||||
'./node_modules/app/mm-redux/store/helpers.ts',
|
||||
'./node_modules/app/mm-redux/store/index.ts',
|
||||
'./node_modules/app/mm-redux/store/initial_state.ts',
|
||||
'./node_modules/app/mm-redux/store/middleware.ts',
|
||||
'./node_modules/app/mm-redux/types/actions.ts',
|
||||
'./node_modules/app/mm-redux/utils/channel_utils.ts',
|
||||
'./node_modules/app/mm-redux/utils/emoji_utils.ts',
|
||||
|
|
@ -162,19 +123,54 @@ module.exports = [
|
|||
'./node_modules/app/mm-redux/utils/helpers.ts',
|
||||
'./node_modules/app/mm-redux/utils/i18n_utils.ts',
|
||||
'./node_modules/app/mm-redux/utils/key_mirror.ts',
|
||||
'./node_modules/app/mm-redux/utils/post_list.ts',
|
||||
'./node_modules/app/mm-redux/utils/post_utils.ts',
|
||||
'./node_modules/app/mm-redux/utils/preference_utils.ts',
|
||||
'./node_modules/app/mm-redux/utils/sentry.ts',
|
||||
'./node_modules/app/mm-redux/utils/team_utils.ts',
|
||||
'./node_modules/app/mm-redux/utils/timezone_utils.ts',
|
||||
'./node_modules/app/mm-redux/utils/user_utils.ts',
|
||||
'./node_modules/app/push_notifications/index.js',
|
||||
'./node_modules/app/push_notifications/push_notifications.android.js',
|
||||
'./node_modules/app/reducers/app/build.js',
|
||||
'./node_modules/app/reducers/app/index.js',
|
||||
'./node_modules/app/reducers/app/version.js',
|
||||
'./node_modules/app/reducers/device/connection.js',
|
||||
'./node_modules/app/reducers/device/dimension.js',
|
||||
'./node_modules/app/reducers/device/index.js',
|
||||
'./node_modules/app/reducers/device/is_tablet.js',
|
||||
'./node_modules/app/reducers/device/orientation.js',
|
||||
'./node_modules/app/reducers/device/status_bar.js',
|
||||
'./node_modules/app/reducers/index.js',
|
||||
'./node_modules/app/reducers/views/announcement.js',
|
||||
'./node_modules/app/reducers/views/channel.js',
|
||||
'./node_modules/app/reducers/views/client_upgrade.js',
|
||||
'./node_modules/app/reducers/views/emoji.js',
|
||||
'./node_modules/app/reducers/views/extension.js',
|
||||
'./node_modules/app/reducers/views/i18n.js',
|
||||
'./node_modules/app/reducers/views/index.js',
|
||||
'./node_modules/app/reducers/views/post.js',
|
||||
'./node_modules/app/reducers/views/recent_emojis.js',
|
||||
'./node_modules/app/reducers/views/root.js',
|
||||
'./node_modules/app/reducers/views/search.js',
|
||||
'./node_modules/app/reducers/views/select_server.js',
|
||||
'./node_modules/app/reducers/views/team.js',
|
||||
'./node_modules/app/reducers/views/thread.js',
|
||||
'./node_modules/app/reducers/views/user.js',
|
||||
'./node_modules/app/screens/index.js',
|
||||
'./node_modules/app/selectors/channel.js',
|
||||
'./node_modules/app/selectors/i18n.js',
|
||||
'./node_modules/app/store/ephemeral_store.js',
|
||||
'./node_modules/app/store/index.js',
|
||||
'./node_modules/app/store/middleware.js',
|
||||
'./node_modules/app/store/store.js',
|
||||
'./node_modules/app/store/thunk.js',
|
||||
'./node_modules/app/store/helpers.ts',
|
||||
'./node_modules/app/store/index.ts',
|
||||
'./node_modules/app/store/initial_state.js',
|
||||
'./node_modules/app/store/middlewares/helpers.js',
|
||||
'./node_modules/app/store/middlewares/index.ts',
|
||||
'./node_modules/app/store/middlewares/message_retention.js',
|
||||
'./node_modules/app/store/middlewares/sentry.js',
|
||||
'./node_modules/app/store/middlewares/thunk.js',
|
||||
'./node_modules/app/store/mmkv_adapter.ts',
|
||||
'./node_modules/app/store/store.ts',
|
||||
'./node_modules/app/store/utils.js',
|
||||
'./node_modules/app/telemetry/index.js',
|
||||
'./node_modules/app/telemetry/telemetry.android.js',
|
||||
|
|
@ -183,13 +179,11 @@ module.exports = [
|
|||
'./node_modules/app/utils/file.js',
|
||||
'./node_modules/app/utils/general.js',
|
||||
'./node_modules/app/utils/i18n.js',
|
||||
'./node_modules/app/utils/image_cache_manager.js',
|
||||
'./node_modules/app/utils/preferences.js',
|
||||
'./node_modules/app/utils/push_notifications.js',
|
||||
'./node_modules/app/utils/security.js',
|
||||
'./node_modules/app/utils/segment.js',
|
||||
'./node_modules/app/utils/sentry/index.js',
|
||||
'./node_modules/app/utils/sentry/middleware.js',
|
||||
'./node_modules/app/utils/time_tracker.js',
|
||||
'./node_modules/app/utils/timezone.js',
|
||||
'./node_modules/index.js',
|
||||
|
|
@ -220,19 +214,11 @@ module.exports = [
|
|||
'./node_modules/node_modules/@babel/runtime/helpers/toConsumableArray.js',
|
||||
'./node_modules/node_modules/@babel/runtime/helpers/typeof.js',
|
||||
'./node_modules/node_modules/@babel/runtime/helpers/wrapNativeSuper.js',
|
||||
'./node_modules/node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js',
|
||||
'./node_modules/node_modules/@babel/runtime/regenerator/index.js',
|
||||
'./node_modules/node_modules/@react-native-community/async-storage/lib/AsyncStorage.js',
|
||||
'./node_modules/node_modules/@react-native-community/async-storage/lib/hooks.js',
|
||||
'./node_modules/node_modules/@react-native-community/async-storage/lib/index.js',
|
||||
'./node_modules/node_modules/@react-native-community/netinfo/src/index.ts',
|
||||
'./node_modules/node_modules/@react-native-community/netinfo/src/internal/deprecatedState.ts',
|
||||
'./node_modules/node_modules/@react-native-community/netinfo/src/internal/deprecatedTypes.ts',
|
||||
'./node_modules/node_modules/@react-native-community/netinfo/src/internal/deprecatedUtils.ts',
|
||||
'./node_modules/node_modules/@react-native-community/netinfo/src/internal/internetReachability.ts',
|
||||
'./node_modules/node_modules/@react-native-community/netinfo/src/internal/nativeInterface.ts',
|
||||
'./node_modules/node_modules/@react-native-community/netinfo/src/internal/state.ts',
|
||||
'./node_modules/node_modules/@react-native-community/netinfo/src/internal/types.ts',
|
||||
'./node_modules/node_modules/@react-native-community/netinfo/src/internal/utils.ts',
|
||||
'./node_modules/node_modules/base-64/base64.js',
|
||||
'./node_modules/node_modules/base64-js/index.js',
|
||||
'./node_modules/node_modules/buffer/index.js',
|
||||
|
|
@ -255,10 +241,8 @@ module.exports = [
|
|||
'./node_modules/node_modules/fbjs/lib/performance.js',
|
||||
'./node_modules/node_modules/fbjs/lib/performanceNow.js',
|
||||
'./node_modules/node_modules/fbjs/lib/warning.js',
|
||||
'./node_modules/node_modules/get-params/index.js',
|
||||
'./node_modules/node_modules/harmony-reflect/reflect.js',
|
||||
'./node_modules/node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.cjs.js',
|
||||
'./node_modules/node_modules/ieee754/index.js',
|
||||
'./node_modules/node_modules/inherits/inherits_browser.js',
|
||||
'./node_modules/node_modules/intl-format-cache/dist/index.js',
|
||||
'./node_modules/node_modules/intl-format-cache/index.js',
|
||||
|
|
@ -282,16 +266,9 @@ module.exports = [
|
|||
'./node_modules/node_modules/intl/index.js',
|
||||
'./node_modules/node_modules/intl/lib/core.js',
|
||||
'./node_modules/node_modules/intl/locale-data/complete.js',
|
||||
'./node_modules/node_modules/invariant/browser.js',
|
||||
'./node_modules/node_modules/is-arguments/index.js',
|
||||
'./node_modules/node_modules/is-generator-function/index.js',
|
||||
'./node_modules/node_modules/jsan/index.js',
|
||||
'./node_modules/node_modules/jsan/lib/cycle.js',
|
||||
'./node_modules/node_modules/jsan/lib/index.js',
|
||||
'./node_modules/node_modules/jsan/lib/path-getter.js',
|
||||
'./node_modules/node_modules/jsan/lib/utils.js',
|
||||
'./node_modules/node_modules/json-stringify-safe/stringify.js',
|
||||
'./node_modules/node_modules/linked-list/_source/linked-list.js',
|
||||
'./node_modules/node_modules/linked-list/index.js',
|
||||
'./node_modules/node_modules/lodash.clonedeep/index.js',
|
||||
'./node_modules/node_modules/lodash.forin/index.js',
|
||||
'./node_modules/node_modules/lodash.get/index.js',
|
||||
|
|
@ -314,8 +291,6 @@ module.exports = [
|
|||
'./node_modules/node_modules/lodash/_apply.js',
|
||||
'./node_modules/node_modules/lodash/_arrayEach.js',
|
||||
'./node_modules/node_modules/lodash/_arrayFilter.js',
|
||||
'./node_modules/node_modules/lodash/_arrayIncludes.js',
|
||||
'./node_modules/node_modules/lodash/_arrayIncludesWith.js',
|
||||
'./node_modules/node_modules/lodash/_arrayLikeKeys.js',
|
||||
'./node_modules/node_modules/lodash/_arrayMap.js',
|
||||
'./node_modules/node_modules/lodash/_arrayPush.js',
|
||||
|
|
@ -329,9 +304,7 @@ module.exports = [
|
|||
'./node_modules/node_modules/lodash/_baseClamp.js',
|
||||
'./node_modules/node_modules/lodash/_baseClone.js',
|
||||
'./node_modules/node_modules/lodash/_baseCreate.js',
|
||||
'./node_modules/node_modules/lodash/_baseDifference.js',
|
||||
'./node_modules/node_modules/lodash/_baseEach.js',
|
||||
'./node_modules/node_modules/lodash/_baseFindIndex.js',
|
||||
'./node_modules/node_modules/lodash/_baseFlatten.js',
|
||||
'./node_modules/node_modules/lodash/_baseFor.js',
|
||||
'./node_modules/node_modules/lodash/_baseForOwn.js',
|
||||
|
|
@ -339,13 +312,11 @@ module.exports = [
|
|||
'./node_modules/node_modules/lodash/_baseGetAllKeys.js',
|
||||
'./node_modules/node_modules/lodash/_baseGetTag.js',
|
||||
'./node_modules/node_modules/lodash/_baseHasIn.js',
|
||||
'./node_modules/node_modules/lodash/_baseIndexOf.js',
|
||||
'./node_modules/node_modules/lodash/_baseIsArguments.js',
|
||||
'./node_modules/node_modules/lodash/_baseIsEqual.js',
|
||||
'./node_modules/node_modules/lodash/_baseIsEqualDeep.js',
|
||||
'./node_modules/node_modules/lodash/_baseIsMap.js',
|
||||
'./node_modules/node_modules/lodash/_baseIsMatch.js',
|
||||
'./node_modules/node_modules/lodash/_baseIsNaN.js',
|
||||
'./node_modules/node_modules/lodash/_baseIsNative.js',
|
||||
'./node_modules/node_modules/lodash/_baseIsSet.js',
|
||||
'./node_modules/node_modules/lodash/_baseIsTypedArray.js',
|
||||
|
|
@ -365,7 +336,6 @@ module.exports = [
|
|||
'./node_modules/node_modules/lodash/_baseTimes.js',
|
||||
'./node_modules/node_modules/lodash/_baseToString.js',
|
||||
'./node_modules/node_modules/lodash/_baseUnary.js',
|
||||
'./node_modules/node_modules/lodash/_baseUniq.js',
|
||||
'./node_modules/node_modules/lodash/_baseUnset.js',
|
||||
'./node_modules/node_modules/lodash/_cacheHas.js',
|
||||
'./node_modules/node_modules/lodash/_castFunction.js',
|
||||
|
|
@ -451,14 +421,12 @@ module.exports = [
|
|||
'./node_modules/node_modules/lodash/_stackGet.js',
|
||||
'./node_modules/node_modules/lodash/_stackHas.js',
|
||||
'./node_modules/node_modules/lodash/_stackSet.js',
|
||||
'./node_modules/node_modules/lodash/_strictIndexOf.js',
|
||||
'./node_modules/node_modules/lodash/_stringToPath.js',
|
||||
'./node_modules/node_modules/lodash/_toKey.js',
|
||||
'./node_modules/node_modules/lodash/_toSource.js',
|
||||
'./node_modules/node_modules/lodash/clone.js',
|
||||
'./node_modules/node_modules/lodash/cloneDeep.js',
|
||||
'./node_modules/node_modules/lodash/constant.js',
|
||||
'./node_modules/node_modules/lodash/difference.js',
|
||||
'./node_modules/node_modules/lodash/endsWith.js',
|
||||
'./node_modules/node_modules/lodash/eq.js',
|
||||
'./node_modules/node_modules/lodash/forEach.js',
|
||||
|
|
@ -487,7 +455,6 @@ module.exports = [
|
|||
'./node_modules/node_modules/lodash/last.js',
|
||||
'./node_modules/node_modules/lodash/lodash.js',
|
||||
'./node_modules/node_modules/lodash/map.js',
|
||||
'./node_modules/node_modules/lodash/mapValues.js',
|
||||
'./node_modules/node_modules/lodash/memoize.js',
|
||||
'./node_modules/node_modules/lodash/merge.js',
|
||||
'./node_modules/node_modules/lodash/noop.js',
|
||||
|
|
@ -499,7 +466,6 @@ module.exports = [
|
|||
'./node_modules/node_modules/lodash/toNumber.js',
|
||||
'./node_modules/node_modules/lodash/toPlainObject.js',
|
||||
'./node_modules/node_modules/lodash/toString.js',
|
||||
'./node_modules/node_modules/lodash/union.js',
|
||||
'./node_modules/node_modules/lodash/uniqueId.js',
|
||||
'./node_modules/node_modules/lodash/unset.js',
|
||||
'./node_modules/node_modules/metro/src/lib/bundle-modules/HMRClient.js',
|
||||
|
|
@ -508,7 +474,6 @@ module.exports = [
|
|||
'./node_modules/node_modules/moment-timezone/index.js',
|
||||
'./node_modules/node_modules/moment-timezone/moment-timezone.js',
|
||||
'./node_modules/node_modules/moment/moment.js',
|
||||
'./node_modules/node_modules/nanoid/non-secure/index.js',
|
||||
'./node_modules/node_modules/nullthrows/nullthrows.js',
|
||||
'./node_modules/node_modules/object-assign/index.js',
|
||||
'./node_modules/node_modules/promise/setimmediate/core.js',
|
||||
|
|
@ -520,22 +485,15 @@ module.exports = [
|
|||
'./node_modules/node_modules/prop-types/factoryWithTypeCheckers.js',
|
||||
'./node_modules/node_modules/prop-types/index.js',
|
||||
'./node_modules/node_modules/prop-types/lib/ReactPropTypesSecret.js',
|
||||
'./node_modules/node_modules/querystring/decode.js',
|
||||
'./node_modules/node_modules/querystring/encode.js',
|
||||
'./node_modules/node_modules/querystring/index.js',
|
||||
'./node_modules/node_modules/querystringify/index.js',
|
||||
'./node_modules/node_modules/react-intl/lib/index.js',
|
||||
'./node_modules/node_modules/react-intl/locale-data/en.js',
|
||||
'./node_modules/node_modules/react-intl/locale-data/index.js',
|
||||
'./node_modules/node_modules/react-is/cjs/react-is.production.min.js',
|
||||
'./node_modules/node_modules/react-is/cjs/react-is.production.js',
|
||||
'./node_modules/node_modules/react-is/index.js',
|
||||
'./node_modules/node_modules/react-lifecycles-compat/react-lifecycles-compat.cjs.js',
|
||||
'./node_modules/node_modules/react-native-cookies/index.js',
|
||||
'./node_modules/node_modules/react-native-device-info/deviceinfo.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/Directions.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/DrawerLayout.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/GestureButtons.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/GestureComponents.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/GestureHandler.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/GestureHandlerButton.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/GestureHandlerPropTypes.js',
|
||||
|
|
@ -545,19 +503,24 @@ module.exports = [
|
|||
'./node_modules/node_modules/react-native-gesture-handler/PlatformConstants.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/RNGestureHandlerModule.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/State.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/Swipeable.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/createHandler.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/createNativeWrapper.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/gestureHandlerRootHOC.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/index.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/touchables/GenericTouchable.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/touchables/TouchableHighlight.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/touchables/TouchableNativeFeedback.android.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/touchables/TouchableOpacity.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/touchables/TouchableWithoutFeedback.js',
|
||||
'./node_modules/node_modules/react-native-gesture-handler/touchables/index.js',
|
||||
'./node_modules/node_modules/react-native-haptic-feedback/index.js',
|
||||
'./node_modules/node_modules/react-native-keychain/index.js',
|
||||
'./node_modules/node_modules/react-native-mmkv-storage/index.js',
|
||||
'./node_modules/node_modules/react-native-mmkv-storage/src/api.js',
|
||||
'./node_modules/node_modules/react-native-mmkv-storage/src/encryption.js',
|
||||
'./node_modules/node_modules/react-native-mmkv-storage/src/indexer/arrays.js',
|
||||
'./node_modules/node_modules/react-native-mmkv-storage/src/indexer/booleans.js',
|
||||
'./node_modules/node_modules/react-native-mmkv-storage/src/indexer/indexer.js',
|
||||
'./node_modules/node_modules/react-native-mmkv-storage/src/indexer/maps.js',
|
||||
'./node_modules/node_modules/react-native-mmkv-storage/src/indexer/numbers.js',
|
||||
'./node_modules/node_modules/react-native-mmkv-storage/src/indexer/strings.js',
|
||||
'./node_modules/node_modules/react-native-mmkv-storage/src/keygen.js',
|
||||
'./node_modules/node_modules/react-native-mmkv-storage/src/loader.js',
|
||||
'./node_modules/node_modules/react-native-mmkv-storage/src/utils.js',
|
||||
'./node_modules/node_modules/react-native-navigation/lib/dist/Navigation.js',
|
||||
'./node_modules/node_modules/react-native-navigation/lib/dist/adapters/AppRegistryService.js',
|
||||
'./node_modules/node_modules/react-native-navigation/lib/dist/adapters/AssetResolver.js',
|
||||
|
|
@ -743,6 +706,7 @@ module.exports = [
|
|||
'./node_modules/node_modules/react-native/Libraries/ReactPrivate/ReactNativePrivateInitializeCore.js',
|
||||
'./node_modules/node_modules/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js',
|
||||
'./node_modules/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js',
|
||||
'./node_modules/node_modules/react-native/Libraries/Renderer/shims/NativeMethodsMixin.js',
|
||||
'./node_modules/node_modules/react-native/Libraries/Renderer/shims/ReactNative.js',
|
||||
'./node_modules/node_modules/react-native/Libraries/Renderer/shims/ReactNativeViewConfigRegistry.js',
|
||||
'./node_modules/node_modules/react-native/Libraries/Renderer/shims/createReactNativeComponentClass.js',
|
||||
|
|
@ -830,18 +794,18 @@ module.exports = [
|
|||
'./node_modules/node_modules/redux-action-buffer/index.js',
|
||||
'./node_modules/node_modules/redux-batched-actions/lib/index.js',
|
||||
'./node_modules/node_modules/redux-persist-transform-filter/dist/index.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/autoRehydrate.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/constants.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/createPersistor.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/createMigrate.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/createPersistoid.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/createTransform.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/defaults/asyncLocalStorage.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/getStoredState.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/index.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/persistCombineReducers.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/persistReducer.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/persistStore.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/purgeStoredState.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/utils/isStatePlainEnough.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/utils/setImmediate.js',
|
||||
'./node_modules/node_modules/redux-thunk/lib/index.js',
|
||||
'./node_modules/node_modules/redux-persist/lib/stateReconciler/autoMergeLevel1.js',
|
||||
'./node_modules/node_modules/redux-reset/lib/index.js',
|
||||
'./node_modules/node_modules/redux/lib/redux.js',
|
||||
'./node_modules/node_modules/requires-port/index.js',
|
||||
'./node_modules/node_modules/reselect/lib/index.js',
|
||||
|
|
@ -852,11 +816,16 @@ module.exports = [
|
|||
'./node_modules/node_modules/rn-fetch-blob/ios.js',
|
||||
'./node_modules/node_modules/rn-fetch-blob/json-stream.js',
|
||||
'./node_modules/node_modules/rn-fetch-blob/polyfill/Fetch.js',
|
||||
'./node_modules/node_modules/rn-host-detect/index.js',
|
||||
'./node_modules/node_modules/sc-channel/index.js',
|
||||
'./node_modules/node_modules/sc-errors/decycle.js',
|
||||
'./node_modules/node_modules/sc-errors/index.js',
|
||||
'./node_modules/node_modules/sc-formatter/index.js',
|
||||
'./node_modules/node_modules/rn-fetch-blob/polyfill/File.js',
|
||||
'./node_modules/node_modules/rn-fetch-blob/polyfill/FileReader.js',
|
||||
'./node_modules/node_modules/rn-fetch-blob/polyfill/ProgressEvent.js',
|
||||
'./node_modules/node_modules/rn-fetch-blob/polyfill/XMLHttpRequest.js',
|
||||
'./node_modules/node_modules/rn-fetch-blob/polyfill/XMLHttpRequestEventTarget.js',
|
||||
'./node_modules/node_modules/rn-fetch-blob/polyfill/index.js',
|
||||
'./node_modules/node_modules/rn-fetch-blob/utils/log.js',
|
||||
'./node_modules/node_modules/rn-fetch-blob/utils/unicode.js',
|
||||
'./node_modules/node_modules/rn-fetch-blob/utils/uri.js',
|
||||
'./node_modules/node_modules/rn-fetch-blob/utils/uuid.js',
|
||||
'./node_modules/node_modules/scheduler/cjs/scheduler-tracing.production.min.js',
|
||||
'./node_modules/node_modules/scheduler/cjs/scheduler.production.min.js',
|
||||
'./node_modules/node_modules/scheduler/index.js',
|
||||
|
|
@ -908,7 +877,6 @@ module.exports = [
|
|||
'./node_modules/node_modules/stacktrace-parser/dist/stack-trace-parser.cjs.js',
|
||||
'./node_modules/node_modules/symbol-observable/lib/index.js',
|
||||
'./node_modules/node_modules/symbol-observable/lib/ponyfill.js',
|
||||
'./node_modules/node_modules/tslib/tslib.js',
|
||||
'./node_modules/node_modules/url-parse/index.js',
|
||||
'./node_modules/node_modules/util/support/isBufferBrowser.js',
|
||||
'./node_modules/node_modules/util/support/types.js',
|
||||
|
|
|
|||
47
patches/react-native-mmkv-storage+0.3.1.patch
Normal file
47
patches/react-native-mmkv-storage+0.3.1.patch
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
diff --git a/node_modules/react-native-mmkv-storage/ios/MMKVStorage.m b/node_modules/react-native-mmkv-storage/ios/MMKVStorage.m
|
||||
index 6ce0b16..a1b3c56 100644
|
||||
--- a/node_modules/react-native-mmkv-storage/ios/MMKVStorage.m
|
||||
+++ b/node_modules/react-native-mmkv-storage/ios/MMKVStorage.m
|
||||
@@ -45,7 +45,10 @@ - (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
- [MMKV initialize];
|
||||
+ NSString *APP_GROUP_ID = @"group.com.mattermost.rnbeta";
|
||||
+ NSString *groupDir = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:APP_GROUP_ID].path;
|
||||
+ [MMKV initializeMMKV:nil groupDir:groupDir logLevel:MMKVLogInfo];
|
||||
+
|
||||
secureStorage = [[SecureStorage alloc]init];
|
||||
IdStore = [[IDStore alloc] initWithMMKV:[MMKV mmkvWithID:@"mmkvIdStore"]];
|
||||
mmkvMap = [NSMutableDictionary dictionary];
|
||||
diff --git a/node_modules/react-native-mmkv-storage/ios/SecureStorage.m b/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
|
||||
index 70f3a01..07e1af0 100644
|
||||
--- a/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
|
||||
+++ b/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
|
||||
@@ -184,7 +184,7 @@ - (BOOL)searchKeychainCopyMatchingExists:(NSString *)identifier {
|
||||
}
|
||||
|
||||
- (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier options: (NSDictionary * __nullable)options {
|
||||
- CFStringRef accessible = accessibleValue(options);
|
||||
+ CFStringRef accessible = secureStoreAccessibleValue(options);
|
||||
NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];
|
||||
|
||||
NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding];
|
||||
@@ -201,7 +201,7 @@ - (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifi
|
||||
|
||||
- (BOOL)updateKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier options:(NSDictionary * __nullable)options {
|
||||
|
||||
- CFStringRef accessible = accessibleValue(options);
|
||||
+ CFStringRef accessible = secureStoreAccessibleValue(options);
|
||||
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
|
||||
NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
|
||||
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
|
||||
@@ -255,7 +255,7 @@ - (void)handleAppUninstallation
|
||||
|
||||
|
||||
|
||||
-CFStringRef accessibleValue(NSDictionary *options)
|
||||
+CFStringRef secureStoreAccessibleValue(NSDictionary *options)
|
||||
{
|
||||
if (options && options[@"accessible"] != nil) {
|
||||
NSDictionary *keyMap = @{
|
||||
14
patches/redux-persist+6.0.0.patch
Normal file
14
patches/redux-persist+6.0.0.patch
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
diff --git a/node_modules/redux-persist/types/types.d.ts b/node_modules/redux-persist/types/types.d.ts
|
||||
index b3733bc..882f4de 100644
|
||||
--- a/node_modules/redux-persist/types/types.d.ts
|
||||
+++ b/node_modules/redux-persist/types/types.d.ts
|
||||
@@ -42,7 +42,8 @@ declare module "redux-persist/es/types" {
|
||||
*/
|
||||
getStoredState?: (config: PersistConfig<S, RS, HSS, ESS>) => Promise<PersistedState>;
|
||||
debug?: boolean;
|
||||
- serialize?: boolean;
|
||||
+ serialize?: boolean | Function;
|
||||
+ deserialize?: boolean | Function;
|
||||
timeout?: number;
|
||||
writeFailHandler?: (err: Error) => void;
|
||||
}
|
||||
|
|
@ -5,10 +5,12 @@ import React, {PureComponent} from 'react';
|
|||
import {Provider} from 'react-redux';
|
||||
import {IntlProvider} from 'react-intl';
|
||||
|
||||
import {getTranslations} from 'app/i18n';
|
||||
import {getCurrentLocale} from 'app/selectors/i18n';
|
||||
import store from 'app/store';
|
||||
import {waitForHydration} from 'app/store/utils';
|
||||
import {getTranslations} from '@i18n';
|
||||
import {getCurrentLocale} from '@selectors/i18n';
|
||||
import configureStore from '@store';
|
||||
import getStorage from '@store/mmkv_adapter';
|
||||
import Store from '@store/store';
|
||||
import {waitForHydration} from '@store/utils';
|
||||
|
||||
import {extensionSelectTeamId} from './actions';
|
||||
let Extension;
|
||||
|
|
@ -26,8 +28,24 @@ export default class ShareApp extends PureComponent {
|
|||
|
||||
componentDidMount() {
|
||||
this.mounted = true;
|
||||
waitForHydration(store, () => {
|
||||
const {dispatch, getState} = store;
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
initialize = async () => {
|
||||
if (Store.redux) {
|
||||
this.hydrate();
|
||||
return;
|
||||
}
|
||||
|
||||
getStorage().then(this.hydrate);
|
||||
};
|
||||
|
||||
hydrate = (MMKVStorage) => {
|
||||
if (MMKVStorage) {
|
||||
configureStore(MMKVStorage);
|
||||
}
|
||||
waitForHydration(Store.redux, () => {
|
||||
const {dispatch, getState} = Store.redux;
|
||||
const {currentTeamId} = getState().entities.teams;
|
||||
dispatch(extensionSelectTeamId(currentTeamId));
|
||||
this.setState({init: true});
|
||||
|
|
@ -39,10 +57,10 @@ export default class ShareApp extends PureComponent {
|
|||
return null;
|
||||
}
|
||||
|
||||
const locale = getCurrentLocale(store.getState());
|
||||
const locale = getCurrentLocale(Store.redux.getState());
|
||||
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<Provider store={Store.redux}>
|
||||
<IntlProvider
|
||||
locale={locale}
|
||||
messages={getTranslations(locale)}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import {AsyncNodeStorage} from 'redux-persist-node-storage';
|
||||
import {createTransform} from 'redux-persist';
|
||||
|
||||
import configureStore from '@mm-redux/store';
|
||||
import configureStore from '@store';
|
||||
|
||||
export default async function testConfigureStore(preloadedState) {
|
||||
const storageTransform = createTransform(
|
||||
|
|
@ -15,12 +15,13 @@ export default async function testConfigureStore(preloadedState) {
|
|||
key: 'root',
|
||||
storage: new AsyncNodeStorage('./.tmp'),
|
||||
whitelist: [],
|
||||
serialize: true,
|
||||
deserialize: true,
|
||||
transforms: [
|
||||
storageTransform,
|
||||
],
|
||||
};
|
||||
|
||||
const {store} = configureStore(preloadedState, {}, persistConfig, () => ({}), {enableBuffer: false});
|
||||
const {store} = configureStore(null, preloadedState, persistConfig, {enableBuffer: false});
|
||||
|
||||
const wait = () => new Promise((resolve) => setTimeout(resolve), 300); //eslint-disable-line
|
||||
await wait();
|
||||
|
|
|
|||
|
|
@ -34,10 +34,11 @@
|
|||
"@constants/*": ["app/constants/*"],
|
||||
"@constants": ["app/constants/index"],
|
||||
"@i18n": ["app/i18n/index"],
|
||||
"@mm-redux/*": ["app/mm-redux/*"],
|
||||
"@selectors/*": ["app/selectors/*"],
|
||||
"@store/*": ["app/store"],
|
||||
"@telemetry/*": ["/app/telemetry/*"],
|
||||
"@utils/*": ["app/utils/*"],
|
||||
"@mm-redux/*": ["app/mm-redux/*"],
|
||||
"@websocket": ["app/client/websocket"],
|
||||
"*": ["./*", "node_modules/*"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue