[MM-23698] [MM-19559] Remove redux-offline and updated redux-persist (#4120)
* Remove redux-offline and configure redux-persist * Fix typo * Fix configure store * Upgrade to redux-persist 6.0.0 * Add migration from redux-persist v4 to v6 * Replace AsyncStorage with MMKVStorage to boost storage speed * Mock RNFastStorage * Fix reactions test * Fix clearing the store on logout * Remove the need for LOGOUT_SUCCESS * No need to pass persistConfig to middlewares() * Remove unused imports * Export connection Accidentally removed this export. * Add batch action name Co-Authored-By: Elias Nahum <nahumhbl@gmail.com> * Add batch action name Co-Authored-By: Elias Nahum <nahumhbl@gmail.com> * Add batch action name Co-Authored-By: Elias Nahum <nahumhbl@gmail.com> * Add batch action name Co-Authored-By: Elias Nahum <nahumhbl@gmail.com> * Fix delete post * Fix leave channel Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
parent
8bb43ca4c2
commit
7afa9aed01
78 changed files with 876 additions and 1454 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -87,6 +87,7 @@ ios/sentry.properties
|
|||
# Testing
|
||||
.nyc_output
|
||||
coverage
|
||||
.tmp
|
||||
|
||||
# Bundle artifact
|
||||
*.jsbundle
|
||||
|
|
|
|||
35
NOTICE.txt
35
NOTICE.txt
|
|
@ -2410,41 +2410,6 @@ SOFTWARE.
|
|||
|
||||
---
|
||||
|
||||
## redux-offline
|
||||
|
||||
This product contains 'redux-offline' by redux-offline.
|
||||
|
||||
Build Offline-First Apps for Web and React Native
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/redux-offline/redux-offline
|
||||
|
||||
* LICENSE: MIT
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Jani Eväkallio
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## redux-persist
|
||||
|
||||
This product contains 'redux-persist' by Zack Story.
|
||||
|
|
|
|||
|
|
@ -1,21 +1,16 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {networkStatusChangedAction} from 'redux-offline';
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
|
||||
import {DeviceTypes} from 'app/constants';
|
||||
|
||||
export function connection(isOnline) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
if (isOnline !== undefined && isOnline !== state.device.connection) { //eslint-disable-line no-undefined
|
||||
dispatch(batchActions([
|
||||
networkStatusChangedAction(isOnline), {
|
||||
type: DeviceTypes.CONNECTION_CHANGED,
|
||||
data: isOnline,
|
||||
},
|
||||
], 'BATCH_CONNECTION_CHANGED'));
|
||||
dispatch({
|
||||
type: DeviceTypes.CONNECTION_CHANGED,
|
||||
data: isOnline,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
import configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
import {Client4} from '@mm-redux/client';
|
||||
import * as GeneralActions from '@mm-redux/actions/general';
|
||||
|
||||
import {handleSuccessfulLogin} from 'app/actions/views/login';
|
||||
|
|
@ -46,8 +47,8 @@ describe('Actions.Views.Login', () => {
|
|||
});
|
||||
|
||||
test('handleSuccessfulLogin gets config and license ', async () => {
|
||||
const getClientConfig = jest.spyOn(GeneralActions, 'getClientConfig');
|
||||
const getLicenseConfig = jest.spyOn(GeneralActions, 'getLicenseConfig');
|
||||
const getClientConfig = jest.spyOn(Client4, 'getClientConfigOld');
|
||||
const getLicenseConfig = jest.spyOn(Client4, 'getClientLicenseOld');
|
||||
|
||||
await store.dispatch(handleSuccessfulLogin());
|
||||
expect(getClientConfig).toHaveBeenCalled();
|
||||
|
|
|
|||
|
|
@ -4,15 +4,19 @@
|
|||
import {batchActions} from 'redux-batched-actions';
|
||||
|
||||
import {ChannelTypes, GeneralTypes, TeamTypes} from '@mm-redux/action_types';
|
||||
import {Client4} from '@mm-redux/client';
|
||||
import {General} from '@mm-redux/constants';
|
||||
import {fetchMyChannelsAndMembers} from '@mm-redux/actions/channels';
|
||||
import {getClientConfig, getDataRetentionPolicy, getLicenseConfig} from '@mm-redux/actions/general';
|
||||
import {receivedNewPost} from '@mm-redux/actions/posts';
|
||||
import {getMyTeams, getMyTeamMembers} from '@mm-redux/actions/teams';
|
||||
import {Client4} from '@mm-redux/client';
|
||||
import {General} from '@mm-redux/constants';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
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 {markChannelViewedAndRead} from './channel';
|
||||
|
|
@ -29,24 +33,36 @@ export function startDataCleanup() {
|
|||
export function loadConfigAndLicense() {
|
||||
return async (dispatch, getState) => {
|
||||
const {currentUserId} = getState().entities.users;
|
||||
const [configData, licenseData] = await Promise.all([
|
||||
getClientConfig()(dispatch, getState),
|
||||
getLicenseConfig()(dispatch, getState),
|
||||
]);
|
||||
|
||||
const config = configData.data || {};
|
||||
const license = licenseData.data || {};
|
||||
try {
|
||||
const [config, license] = await Promise.all([
|
||||
Client4.getClientConfigOld(),
|
||||
Client4.getClientLicenseOld(),
|
||||
]);
|
||||
|
||||
if (currentUserId) {
|
||||
if (config.DataRetentionEnableMessageDeletion && config.DataRetentionEnableMessageDeletion === 'true' &&
|
||||
license.IsLicensed === 'true' && license.DataRetention === 'true') {
|
||||
dispatch(getDataRetentionPolicy());
|
||||
} else {
|
||||
dispatch({type: GeneralTypes.RECEIVED_DATA_RETENTION_POLICY, data: {}});
|
||||
const actions = [{
|
||||
type: GeneralTypes.CLIENT_CONFIG_RECEIVED,
|
||||
data: config,
|
||||
}, {
|
||||
type: GeneralTypes.CLIENT_LICENSE_RECEIVED,
|
||||
data: license,
|
||||
}];
|
||||
|
||||
if (currentUserId) {
|
||||
if (config.DataRetentionEnableMessageDeletion && config.DataRetentionEnableMessageDeletion === 'true' &&
|
||||
license.IsLicensed === 'true' && license.DataRetention === 'true') {
|
||||
dispatch(getDataRetentionPolicy());
|
||||
} else {
|
||||
actions.push({type: GeneralTypes.RECEIVED_DATA_RETENTION_POLICY, data: {}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {config, license};
|
||||
dispatch(batchActions(actions, 'BATCH_LOAD_CONFIG_AND_LICENSE'));
|
||||
|
||||
return {config, license};
|
||||
} catch (error) {
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +142,18 @@ export function handleSelectTeamAndChannel(teamId, channelId) {
|
|||
}
|
||||
|
||||
export function purgeOfflineStore() {
|
||||
return {type: General.OFFLINE_STORE_PURGE};
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
// A non-optimistic version of the createPost action in app/mm-redux with the file handling
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@
|
|||
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
|
||||
import {NavigationTypes} from 'app/constants';
|
||||
import {GeneralTypes, RoleTypes, UserTypes} from '@mm-redux/action_types';
|
||||
import {getDataRetentionPolicy} from '@mm-redux/actions/general';
|
||||
import * as HelperActions from '@mm-redux/actions/helpers';
|
||||
import {autoUpdateTimezone} from '@mm-redux/actions/timezone';
|
||||
import {Client4} from '@mm-redux/client';
|
||||
import {General} from '@mm-redux/constants';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
import {getConfig, getLicense} from '@mm-redux/selectors/entities/general';
|
||||
import {isTimezoneEnabled} from '@mm-redux/selectors/entities/timezone';
|
||||
import {getCurrentUserId, getStatusForUserId} from '@mm-redux/selectors/entities/users';
|
||||
|
|
@ -194,7 +196,7 @@ export function ssoLogin(token) {
|
|||
}
|
||||
|
||||
export function logout(skipServerLogout = false) {
|
||||
return async (dispatch) => {
|
||||
return async () => {
|
||||
if (!skipServerLogout) {
|
||||
try {
|
||||
Client4.logout();
|
||||
|
|
@ -203,7 +205,8 @@ export function logout(skipServerLogout = false) {
|
|||
}
|
||||
}
|
||||
|
||||
dispatch({type: UserTypes.LOGOUT_SUCCESS});
|
||||
EventEmitter.emit(NavigationTypes.NAVIGATION_RESET);
|
||||
return {data: true};
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
import {shallowWithIntl} from 'test/intl-test-helper.js';
|
||||
|
||||
import Reactions from './reactions';
|
||||
|
||||
|
|
@ -31,12 +31,12 @@ describe('Reactions', () => {
|
|||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(<Reactions {...baseProps}/>);
|
||||
const wrapper = shallowWithIntl(<Reactions {...baseProps}/>);
|
||||
expect(wrapper.getElement()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot with canAddReaction = false', () => {
|
||||
const wrapper = shallow(
|
||||
const wrapper = shallowWithIntl(
|
||||
<Reactions
|
||||
{...baseProps}
|
||||
canAddReaction={false}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ function makeMapStateToProps() {
|
|||
isArchived,
|
||||
isChannelMuted: isChannelMuted(member),
|
||||
currentUserId,
|
||||
hasDraft: Boolean(channelDraft.draft.trim() || channelDraft.files.length),
|
||||
hasDraft: Boolean(channelDraft.draft.trim() || channelDraft?.files?.length),
|
||||
mentions: member ? member.mention_count : 0,
|
||||
shouldHideChannel,
|
||||
showUnreadForMsgs,
|
||||
|
|
|
|||
|
|
@ -15,27 +15,27 @@ import {close as closeWebSocket} from '@actions/websocket';
|
|||
import {GeneralTypes} from '@mm-redux/action_types';
|
||||
import {Client4} from '@mm-redux/client';
|
||||
import {General} from '@mm-redux/constants';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
import {getCurrentChannelId} from '@mm-redux/selectors/entities/channels';
|
||||
import {getCurrentUserId, getUser} from '@mm-redux/selectors/entities/users';
|
||||
import {isTimezoneEnabled} from '@mm-redux/selectors/entities/timezone';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
|
||||
import {setDeviceDimensions, setDeviceOrientation, setDeviceAsTablet, setStatusBarHeight} from '@actions/device';
|
||||
import {selectDefaultChannel} from '@actions/views/channel';
|
||||
import {showOverlay} from '@actions/navigation';
|
||||
import {loadConfigAndLicense, setDeepLinkURL, startDataCleanup} from '@actions/views/root';
|
||||
import {loadMe, logout} from '@actions/views/user';
|
||||
import {NavigationTypes, ViewTypes} from '@constants';
|
||||
import {getTranslations, resetMomentLocale} from '@i18n';
|
||||
import PushNotifications from 'app/push_notifications';
|
||||
import {getCurrentLocale} from '@selectors/i18n';
|
||||
import {t} from '@utils/i18n';
|
||||
import {deleteFileCache} from '@utils/file';
|
||||
import {getDeviceTimezoneAsync} from '@utils/timezone';
|
||||
|
||||
import {setDeviceDimensions, setDeviceOrientation, setDeviceAsTablet, setStatusBarHeight} from 'app/actions/device';
|
||||
import {selectDefaultChannel} from 'app/actions/views/channel';
|
||||
import {showOverlay} from 'app/actions/navigation';
|
||||
import {loadConfigAndLicense, setDeepLinkURL, startDataCleanup} from 'app/actions/views/root';
|
||||
import {loadMe, logout} from 'app/actions/views/user';
|
||||
import {NavigationTypes, ViewTypes} from 'app/constants';
|
||||
import {getTranslations, resetMomentLocale} from 'app/i18n';
|
||||
import initialState from 'app/initial_state';
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
import mattermostManaged from 'app/mattermost_managed';
|
||||
import PushNotifications from 'app/push_notifications';
|
||||
import {getCurrentLocale} from 'app/selectors/i18n';
|
||||
import {t} from 'app/utils/i18n';
|
||||
import {deleteFileCache} from 'app/utils/file';
|
||||
import {getDeviceTimezoneAsync} from 'app/utils/timezone';
|
||||
|
||||
import LocalConfig from 'assets/config';
|
||||
|
||||
import {getAppCredentials, removeAppCredentials} from './credentials';
|
||||
|
|
@ -274,23 +274,32 @@ class GlobalEventHandler {
|
|||
try {
|
||||
await AsyncStorage.clear();
|
||||
const state = this.store.getState();
|
||||
this.store.dispatch(batchActions([
|
||||
{
|
||||
type: General.OFFLINE_STORE_RESET,
|
||||
data: initialState,
|
||||
const newState = {
|
||||
...initialState,
|
||||
entities: {
|
||||
...initialState.entities,
|
||||
general: {
|
||||
...initialState.entities.general,
|
||||
deviceToken: state.entities.general.deviceToken,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: General.STORE_REHYDRATION_COMPLETE,
|
||||
views: {
|
||||
i18n: {
|
||||
locale: DeviceInfo.getDeviceLocale().split('-')[0],
|
||||
},
|
||||
root: {
|
||||
hydrationComplete: true,
|
||||
},
|
||||
selectServer: {
|
||||
serverUrl: state.views.selectServer.serverUrl,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: ViewTypes.SERVER_URL_CHANGED,
|
||||
serverUrl: state.entities.general.credentials.url || state.views.selectServer.serverUrl,
|
||||
},
|
||||
{
|
||||
type: GeneralTypes.RECEIVED_APP_DEVICE_TOKEN,
|
||||
data: state.entities.general.deviceToken,
|
||||
},
|
||||
], 'BATCH_RESET_STATE'));
|
||||
};
|
||||
|
||||
this.store.dispatch({
|
||||
type: General.OFFLINE_STORE_PURGE,
|
||||
state: newState,
|
||||
});
|
||||
} catch (e) {
|
||||
// clear error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
import Config from 'assets/config.json';
|
||||
|
||||
const state = {
|
||||
app: {
|
||||
version: '',
|
||||
build: '',
|
||||
},
|
||||
entities: {
|
||||
general: {
|
||||
appState: false,
|
||||
|
|
@ -111,10 +115,6 @@ const state = {
|
|||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
logout: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
autocompleteUsers: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
|
|
@ -128,7 +128,6 @@ const state = {
|
|||
device: {
|
||||
connection: true,
|
||||
},
|
||||
navigation: '',
|
||||
views: {
|
||||
channel: {
|
||||
drafts: {},
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import 'app/init/device';
|
|||
import 'app/init/fetch';
|
||||
import globalEventHandler from 'app/init/global_event_handler';
|
||||
import {registerScreens} from 'app/screens';
|
||||
import store from 'app/store';
|
||||
import store, {persistor} from 'app/store';
|
||||
import {waitForHydration} from 'app/store/utils';
|
||||
import EphemeralStore from 'app/store/ephemeral_store';
|
||||
import telemetry from 'app/telemetry';
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ export default keyMirror({
|
|||
LOGIN_FAILURE: null,
|
||||
|
||||
LOGOUT_REQUEST: null,
|
||||
LOGOUT_SUCCESS: null,
|
||||
LOGOUT_FAILURE: null,
|
||||
|
||||
REVOKE_ALL_USER_SESSIONS_SUCCESS: null,
|
||||
|
|
|
|||
|
|
@ -130,23 +130,4 @@ describe('Actions.Bots', () => {
|
|||
const botsResult = state.entities.bots.accounts[bot.user_id];
|
||||
assert.equal(bot.owner_id, botsResult.owner_id);
|
||||
});
|
||||
|
||||
it('logout', async () => {
|
||||
// Fill redux store with somthing
|
||||
const bot = TestHelper.fakeBot();
|
||||
nock(Client4.getBaseRoute()).
|
||||
post('/bots').
|
||||
reply(200, bot);
|
||||
await store.dispatch(BotActions.createBot(bot));
|
||||
|
||||
// Should be cleared by logout
|
||||
nock(Client4.getUsersRoute()).
|
||||
post('/logout').
|
||||
reply(200, {status: 'OK'});
|
||||
await store.dispatch(UserActions.logout());
|
||||
|
||||
// Check is clear
|
||||
const state = store.getState();
|
||||
assert.equal(0, Object.keys(state.entities.bots.accounts).length);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,7 +23,14 @@ describe('Actions.Channels', () => {
|
|||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
store = await configureStore();
|
||||
const initialState = {
|
||||
entities: {
|
||||
users: {
|
||||
currentUserId: TestHelper.basicUser.id,
|
||||
},
|
||||
},
|
||||
};
|
||||
store = await configureStore(initialState);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
|
@ -1954,52 +1961,38 @@ describe('Actions.Channels', () => {
|
|||
assert.deepEqual(channel.purpose, purpose);
|
||||
});
|
||||
|
||||
it('leaveChannel', (done) => {
|
||||
async function test() {
|
||||
TestHelper.mockLogin();
|
||||
await store.dispatch(login(TestHelper.basicUser.email, 'password1'));
|
||||
nock(Client4.getBaseRoute()).
|
||||
get(`/channels/${TestHelper.basicChannel.id}`).
|
||||
reply(200, TestHelper.basicChannel);
|
||||
it('leaveChannel', async () => {
|
||||
TestHelper.mockLogin();
|
||||
await store.dispatch(login(TestHelper.basicUser.email, 'password1'));
|
||||
nock(Client4.getBaseRoute()).
|
||||
get(`/channels/${TestHelper.basicChannel.id}`).
|
||||
reply(200, TestHelper.basicChannel);
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
post(`/channels/${TestHelper.basicChannel.id}/members`).
|
||||
reply(201, {channel_id: TestHelper.basicChannel.id, roles: 'channel_user', user_id: TestHelper.basicUser.id});
|
||||
nock(Client4.getBaseRoute()).
|
||||
post(`/channels/${TestHelper.basicChannel.id}/members`).
|
||||
reply(201, {channel_id: TestHelper.basicChannel.id, roles: 'channel_user', user_id: TestHelper.basicUser.id});
|
||||
|
||||
await store.dispatch(Actions.joinChannel(TestHelper.basicUser.id, TestHelper.basicTeam.id, TestHelper.basicChannel.id));
|
||||
await store.dispatch(Actions.joinChannel(TestHelper.basicUser.id, TestHelper.basicTeam.id, TestHelper.basicChannel.id));
|
||||
|
||||
const {channels, myMembers} = store.getState().entities.channels;
|
||||
assert.ok(channels[TestHelper.basicChannel.id]);
|
||||
assert.ok(myMembers[TestHelper.basicChannel.id]);
|
||||
let channelsState = store.getState().entities.channels;
|
||||
assert.ok(channelsState.channels[TestHelper.basicChannel.id]);
|
||||
assert.ok(channelsState.myMembers[TestHelper.basicChannel.id]);
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
delete(`/channels/${TestHelper.basicChannel.id}/members/${TestHelper.basicUser.id}`).
|
||||
reply(400, {});
|
||||
nock(Client4.getBaseRoute()).
|
||||
delete(`/channels/${TestHelper.basicChannel.id}/members/${TestHelper.basicUser.id}`).
|
||||
reply(200, OK_RESPONSE);
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
delete(`/channels/${TestHelper.basicChannel.id}/members/${TestHelper.basicUser.id}`).
|
||||
reply(200, OK_RESPONSE);
|
||||
await store.dispatch(Actions.leaveChannel(TestHelper.basicChannel.id));
|
||||
|
||||
// This action will retry after 1000ms
|
||||
await store.dispatch(Actions.leaveChannel(TestHelper.basicChannel.id));
|
||||
channelsState = store.getState().entities.channels;
|
||||
|
||||
setTimeout(test2, 300);
|
||||
}
|
||||
|
||||
async function test2() {
|
||||
// retry will have completed and should have left the channel successfully
|
||||
const {channels, myMembers} = store.getState().entities.channels;
|
||||
|
||||
assert.ok(channels[TestHelper.basicChannel.id]);
|
||||
assert.ifError(myMembers[TestHelper.basicChannel.id]);
|
||||
done();
|
||||
}
|
||||
|
||||
test();
|
||||
assert.ok(channelsState.channels[TestHelper.basicChannel.id]);
|
||||
assert.ifError(channelsState.myMembers[TestHelper.basicChannel.id]);
|
||||
});
|
||||
|
||||
it('leave private channel', async () => {
|
||||
const newChannel = {
|
||||
...TestHelper.fakeChannelWithId(TestHelper.basicChannel.id),
|
||||
team_id: TestHelper.basicTeam.id,
|
||||
name: 'redux-test-private',
|
||||
display_name: 'Redux Test',
|
||||
|
|
@ -2010,14 +2003,14 @@ describe('Actions.Channels', () => {
|
|||
|
||||
nock(Client4.getBaseRoute()).
|
||||
post('/channels').
|
||||
reply(201, {...TestHelper.fakeChannelWithId(TestHelper.basicTeam.id), ...newChannel});
|
||||
reply(201, newChannel);
|
||||
|
||||
const {data: channel} = await store.dispatch(Actions.createChannel(newChannel, TestHelper.basicUser.id));
|
||||
let channels = store.getState().entities.channels.channels;
|
||||
assert.ok(channels[channel.id]);
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
delete(`/channels/${TestHelper.basicChannel.id}/members/${TestHelper.basicUser.id}`).
|
||||
delete(`/channels/${channel.id}/members/${TestHelper.basicUser.id}`).
|
||||
reply(200, OK_RESPONSE);
|
||||
|
||||
await store.dispatch(Actions.leaveChannel(channel.id));
|
||||
|
|
|
|||
|
|
@ -636,26 +636,23 @@ export function leaveChannel(channelId: string): ActionFunc {
|
|||
team_id: channel.team_id,
|
||||
type: channel.type,
|
||||
},
|
||||
meta: {
|
||||
offline: {
|
||||
effect: () => Client4.removeFromChannel(currentUserId, channelId),
|
||||
commit: {type: 'do_nothing'}, // redux-offline always needs to dispatch something on commit
|
||||
rollback: () => {
|
||||
dispatch(batchActions([
|
||||
{
|
||||
type: ChannelTypes.RECEIVED_CHANNEL,
|
||||
data: channel,
|
||||
},
|
||||
{
|
||||
type: ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER,
|
||||
data: member,
|
||||
},
|
||||
]));
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
await Client4.removeFromChannel(currentUserId, channelId);
|
||||
} catch (error) {
|
||||
dispatch(batchActions([
|
||||
{
|
||||
type: ChannelTypes.RECEIVED_CHANNEL,
|
||||
data: channel,
|
||||
},
|
||||
{
|
||||
type: ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER,
|
||||
data: member,
|
||||
},
|
||||
]));
|
||||
}
|
||||
|
||||
return {data: true};
|
||||
};
|
||||
}
|
||||
|
|
@ -1399,7 +1396,7 @@ export function favoriteChannel(channelId: string): ActionFunc {
|
|||
|
||||
Client4.trackEvent('action', 'action_channels_favorite');
|
||||
|
||||
return savePreferences(currentUserId, [preference])(dispatch);
|
||||
return dispatch(savePreferences(currentUserId, [preference]));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,28 +60,6 @@ describe('Actions.Helpers', () => {
|
|||
assert.deepEqual(dispatch.actions, []);
|
||||
});
|
||||
|
||||
it('should trigger logout when passed a 401 server error', async () => {
|
||||
const store = await configureStore({
|
||||
entities: {
|
||||
users: {
|
||||
currentUserId: 'user',
|
||||
},
|
||||
},
|
||||
});
|
||||
const dispatch = mockDispatch(store.dispatch);
|
||||
|
||||
const error = new ClientError(Client4.getUrl(), {
|
||||
message: 'Failed to do something',
|
||||
status_code: 401,
|
||||
url: '/api/v4/foo/bar',
|
||||
});
|
||||
|
||||
forceLogoutIfNecessary(error, dispatch, store.getState);
|
||||
|
||||
assert.notEqual(Client4.token, token);
|
||||
assert.deepEqual(dispatch.actions, [{type: UserTypes.LOGOUT_SUCCESS, data: {}}]);
|
||||
});
|
||||
|
||||
it('should do nothing when failing to log in', async () => {
|
||||
const store = await configureStore({
|
||||
entities: {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {Client4} from '@mm-redux/client';
|
||||
import {UserTypes} from '@mm-redux/action_types';
|
||||
|
||||
import {logout} from '@actions/views/user';
|
||||
|
||||
import {Client4Error} from '@mm-redux/types/client4';
|
||||
import {batchActions, Action, ActionFunc, GenericAction, DispatchFunc, GetStateFunc} from '@mm-redux/types/actions';
|
||||
|
|
@ -13,8 +13,7 @@ export function forceLogoutIfNecessary(err: Client4Error, dispatch: DispatchFunc
|
|||
const {currentUserId} = getState().entities.users;
|
||||
|
||||
if ('status_code' in err && err.status_code === HTTP_UNAUTHORIZED && err.url && err.url.indexOf('/login') === -1 && currentUserId) {
|
||||
Client4.setToken('');
|
||||
dispatch({type: UserTypes.LOGOUT_SUCCESS, data: {}});
|
||||
dispatch(logout(false));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -206,6 +206,10 @@ describe('Actions.Posts', () => {
|
|||
await Actions.createPost(TestHelper.fakePost(channelId))(store.dispatch, store.getState);
|
||||
const initialPosts = store.getState().entities.posts;
|
||||
const postId = Object.keys(initialPosts.posts)[0];
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
delete(`/posts/${postId}`).
|
||||
reply(200, {});
|
||||
await Actions.deletePost(initialPosts.posts[postId])(store.dispatch, store.getState);
|
||||
|
||||
const state = store.getState();
|
||||
|
|
@ -243,6 +247,9 @@ describe('Actions.Posts', () => {
|
|||
assert.ok(reactions[post1.id]);
|
||||
assert.ok(reactions[post1.id][TestHelper.basicUser.id + '-' + emojiName]);
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
delete(`/posts/${post1.id}`).
|
||||
reply(200, {});
|
||||
await Actions.deletePost(post1)(store.dispatch, store.getState);
|
||||
|
||||
reactions = store.getState().entities.posts.reactions;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Client4, DEFAULT_LIMIT_AFTER, DEFAULT_LIMIT_BEFORE} from '@mm-redux/client';
|
||||
import {General, Preferences, Posts} from '../constants';
|
||||
import {General, Preferences, Posts} from '@mm-redux/constants';
|
||||
import {WebsocketEvents} from '@constants';
|
||||
import {PostTypes, ChannelTypes, FileTypes, IntegrationTypes} from '@mm-redux/action_types';
|
||||
|
||||
|
|
@ -33,7 +33,6 @@ import {Action, ActionResult, batchActions, DispatchFunc, GetStateFunc, GenericA
|
|||
import {ChannelUnread} from '@mm-redux/types/channels';
|
||||
import {GlobalState} from '@mm-redux/types/store';
|
||||
import {Post} from '@mm-redux/types/posts';
|
||||
import {Error} from '@mm-redux/types/errors';
|
||||
import {Reaction} from '@mm-redux/types/reactions';
|
||||
import {UserProfile} from '@mm-redux/types/users';
|
||||
import {Dictionary} from '@mm-redux/types/utilities';
|
||||
|
|
@ -169,9 +168,9 @@ export function createPost(post: Post, files: any[] = []) {
|
|||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
const state = getState();
|
||||
const currentUserId = state.entities.users.currentUserId;
|
||||
|
||||
const timestamp = Date.now();
|
||||
const pendingPostId = post.pending_post_id || `${currentUserId}:${timestamp}`;
|
||||
let actions: Array<Action> = [];
|
||||
|
||||
if (Selectors.isPostIdSending(state, pendingPostId)) {
|
||||
return {data: true};
|
||||
|
|
@ -197,79 +196,78 @@ export function createPost(post: Post, files: any[] = []) {
|
|||
file_ids: fileIds,
|
||||
};
|
||||
|
||||
dispatch({
|
||||
actions.push({
|
||||
type: FileTypes.RECEIVED_FILES_FOR_POST,
|
||||
postId: pendingPostId,
|
||||
data: files,
|
||||
});
|
||||
}
|
||||
|
||||
dispatch({
|
||||
actions.push({
|
||||
type: PostTypes.RECEIVED_NEW_POST,
|
||||
data: {
|
||||
id: pendingPostId,
|
||||
...newPost,
|
||||
},
|
||||
meta: {
|
||||
offline: {
|
||||
effect: () => Client4.createPost({...newPost, create_at: 0}),
|
||||
commit: (result: any, payload: any) => {
|
||||
const actions: Action[] = [
|
||||
receivedPost(payload),
|
||||
{
|
||||
type: PostTypes.CREATE_POST_SUCCESS,
|
||||
},
|
||||
{
|
||||
type: ChannelTypes.INCREMENT_TOTAL_MSG_COUNT,
|
||||
data: {
|
||||
channelId: newPost.channel_id,
|
||||
amount: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: ChannelTypes.DECREMENT_UNREAD_MSG_COUNT,
|
||||
data: {
|
||||
channelId: newPost.channel_id,
|
||||
amount: 1,
|
||||
},
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
if (files) {
|
||||
actions.push({
|
||||
type: FileTypes.RECEIVED_FILES_FOR_POST,
|
||||
postId: payload.id,
|
||||
data: files,
|
||||
});
|
||||
}
|
||||
dispatch(batchActions(actions, 'BATCH_CREATE_POST_INIT'));
|
||||
|
||||
dispatch(batchActions(actions));
|
||||
},
|
||||
maxRetry: 0,
|
||||
offlineRollback: true,
|
||||
rollback: (result: any, error: Error) => {
|
||||
const data = {
|
||||
...newPost,
|
||||
id: pendingPostId,
|
||||
failed: true,
|
||||
update_at: Date.now(),
|
||||
};
|
||||
dispatch({type: PostTypes.CREATE_POST_FAILURE, error});
|
||||
try {
|
||||
const created = await Client4.createPost({...newPost, create_at: 0});
|
||||
|
||||
// If the failure was because: the root post was deleted or
|
||||
// TownSquareIsReadOnly=true then remove the post
|
||||
if (error.server_error_id === 'api.post.create_post.root_id.app_error' ||
|
||||
error.server_error_id === 'api.post.create_post.town_square_read_only' ||
|
||||
error.server_error_id === 'plugin.message_will_be_posted.dismiss_post'
|
||||
) {
|
||||
dispatch(removePost(data) as any);
|
||||
} else {
|
||||
dispatch(receivedPost(data));
|
||||
}
|
||||
actions = [
|
||||
receivedPost(created),
|
||||
{
|
||||
type: PostTypes.CREATE_POST_SUCCESS,
|
||||
},
|
||||
{
|
||||
type: ChannelTypes.INCREMENT_TOTAL_MSG_COUNT,
|
||||
data: {
|
||||
channelId: newPost.channel_id,
|
||||
amount: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
{
|
||||
type: ChannelTypes.DECREMENT_UNREAD_MSG_COUNT,
|
||||
data: {
|
||||
channelId: newPost.channel_id,
|
||||
amount: 1,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (files) {
|
||||
actions.push({
|
||||
type: FileTypes.RECEIVED_FILES_FOR_POST,
|
||||
postId: created.id,
|
||||
data: files,
|
||||
});
|
||||
}
|
||||
|
||||
dispatch(batchActions(actions, 'BATCH_CREATE_POST'));
|
||||
} catch (error) {
|
||||
const data = {
|
||||
...newPost,
|
||||
id: pendingPostId,
|
||||
failed: true,
|
||||
update_at: Date.now(),
|
||||
};
|
||||
actions = [{type: PostTypes.CREATE_POST_FAILURE, error}];
|
||||
|
||||
// If the failure was because: the root post was deleted or
|
||||
// TownSquareIsReadOnly=true then remove the post
|
||||
if (error.server_error_id === 'api.post.create_post.root_id.app_error' ||
|
||||
error.server_error_id === 'api.post.create_post.town_square_read_only' ||
|
||||
error.server_error_id === 'plugin.message_will_be_posted.dismiss_post'
|
||||
) {
|
||||
actions.push(removePost(data) as any);
|
||||
} else {
|
||||
actions.push(receivedPost(data));
|
||||
}
|
||||
|
||||
dispatch(batchActions(actions, 'BATCH_CREATE_POST_FAILED'));
|
||||
}
|
||||
|
||||
return {data: true};
|
||||
};
|
||||
|
|
@ -363,11 +361,10 @@ export function resetCreatePostRequest() {
|
|||
}
|
||||
|
||||
export function deletePost(post: ExtendedPost) {
|
||||
return (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
const state = getState();
|
||||
const delPost = {...post};
|
||||
if (delPost.type === Posts.POST_TYPES.COMBINED_USER_ACTIVITY && delPost.system_post_ids) {
|
||||
delPost.system_post_ids.forEach((systemPostId) => {
|
||||
if (post.type === Posts.POST_TYPES.COMBINED_USER_ACTIVITY && post.system_post_ids) {
|
||||
post.system_post_ids.forEach((systemPostId) => {
|
||||
const systemPost = Selectors.getPost(state, systemPostId);
|
||||
if (systemPost) {
|
||||
dispatch(deletePost(systemPost));
|
||||
|
|
@ -376,15 +373,14 @@ export function deletePost(post: ExtendedPost) {
|
|||
} else {
|
||||
dispatch({
|
||||
type: PostTypes.POST_DELETED,
|
||||
data: delPost,
|
||||
meta: {
|
||||
offline: {
|
||||
effect: () => Client4.deletePost(post.id),
|
||||
commit: {type: 'do_nothing'}, // redux-offline always needs to dispatch something on commit
|
||||
rollback: receivedPost(delPost),
|
||||
},
|
||||
},
|
||||
data: post,
|
||||
});
|
||||
|
||||
try {
|
||||
await Client4.deletePost(post.id);
|
||||
} catch (error) {
|
||||
dispatch(receivedPost(post));
|
||||
}
|
||||
}
|
||||
|
||||
return {data: true};
|
||||
|
|
|
|||
|
|
@ -21,7 +21,14 @@ describe('Actions.Preferences', () => {
|
|||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
store = await configureStore();
|
||||
const initialState = {
|
||||
entities: {
|
||||
users: {
|
||||
currentUserId: TestHelper.basicUser.id,
|
||||
},
|
||||
},
|
||||
};
|
||||
store = await configureStore(initialState);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
|
@ -241,7 +248,7 @@ describe('Actions.Preferences', () => {
|
|||
nock(Client4.getUsersRoute()).
|
||||
get('/me/preferences').
|
||||
reply(200, existingPreferences);
|
||||
await Actions.getMyPreferences()(store.dispatch, store.getState);
|
||||
await store.dispatch(Actions.getMyPreferences());
|
||||
|
||||
const newTheme = {
|
||||
type: 'Mattermost Dark',
|
||||
|
|
@ -249,7 +256,7 @@ describe('Actions.Preferences', () => {
|
|||
nock(Client4.getUsersRoute()).
|
||||
put(`/${TestHelper.basicUser.id}/preferences`).
|
||||
reply(200, OK_RESPONSE);
|
||||
await Actions.saveTheme(team.id, newTheme)(store.dispatch, store.getState);
|
||||
await store.dispatch(Actions.saveTheme(team.id, newTheme));
|
||||
|
||||
const state = store.getState();
|
||||
const {myPreferences} = state.entities.preferences;
|
||||
|
|
|
|||
|
|
@ -14,28 +14,26 @@ import {PreferenceType} from '@mm-redux/types/preferences';
|
|||
import {bindClientFunc} from './helpers';
|
||||
import {getProfilesByIds, getProfilesInChannel} from './users';
|
||||
import {getChannelAndMyMember, getMyChannelMember} from './channels';
|
||||
|
||||
export function deletePreferences(userId: string, preferences: Array<PreferenceType>): ActionFunc {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
const state = getState();
|
||||
const myPreferences = getMyPreferencesSelector(state);
|
||||
const currentPreferences = preferences.map((pref) => myPreferences[getPreferenceKey(pref.category, pref.name)]);
|
||||
|
||||
dispatch({
|
||||
type: PreferenceTypes.DELETED_PREFERENCES,
|
||||
data: preferences,
|
||||
meta: {
|
||||
offline: {
|
||||
effect: () => Client4.deletePreferences(userId, preferences),
|
||||
commit: {
|
||||
type: PreferenceTypes.DELETED_PREFERENCES,
|
||||
},
|
||||
rollback: {
|
||||
type: PreferenceTypes.RECEIVED_PREFERENCES,
|
||||
data: currentPreferences,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
try {
|
||||
dispatch({
|
||||
type: PreferenceTypes.DELETED_PREFERENCES,
|
||||
data: preferences,
|
||||
});
|
||||
|
||||
await Client4.deletePreferences(userId, preferences);
|
||||
} catch {
|
||||
dispatch({
|
||||
type: PreferenceTypes.RECEIVED_PREFERENCES,
|
||||
data: currentPreferences,
|
||||
});
|
||||
}
|
||||
|
||||
return {data: true};
|
||||
};
|
||||
|
|
@ -104,22 +102,21 @@ export function makeGroupMessageVisibleIfNecessary(channelId: string): ActionFun
|
|||
|
||||
export function savePreferences(userId: string, preferences: Array<PreferenceType>) {
|
||||
return async (dispatch: DispatchFunc) => {
|
||||
dispatch({
|
||||
type: PreferenceTypes.RECEIVED_PREFERENCES,
|
||||
data: preferences,
|
||||
meta: {
|
||||
offline: {
|
||||
effect: () => Client4.savePreferences(userId, preferences),
|
||||
commit: {
|
||||
type: PreferenceTypes.RECEIVED_PREFERENCES,
|
||||
},
|
||||
rollback: {
|
||||
type: PreferenceTypes.DELETED_PREFERENCES,
|
||||
data: preferences,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
try {
|
||||
// Optimistic action
|
||||
dispatch({
|
||||
type: PreferenceTypes.RECEIVED_PREFERENCES,
|
||||
data: preferences,
|
||||
});
|
||||
|
||||
await Client4.savePreferences(userId, preferences);
|
||||
} catch (error) {
|
||||
dispatch({
|
||||
type: PreferenceTypes.DELETED_PREFERENCES,
|
||||
data: preferences,
|
||||
});
|
||||
return {error};
|
||||
}
|
||||
|
||||
return {data: true};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,14 +5,20 @@ import assert from 'assert';
|
|||
import nock from 'nock';
|
||||
import fs from 'fs';
|
||||
|
||||
import {NavigationTypes} from '@constants';
|
||||
import {logout} from '@actions/views/user';
|
||||
import * as Actions from '@mm-redux/actions/users';
|
||||
import {Client4} from '@mm-redux/client';
|
||||
import {RequestStatus} from '../constants';
|
||||
import TestHelper from 'test/test_helper';
|
||||
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';
|
||||
|
||||
const OK_RESPONSE = {status: 'OK'};
|
||||
const UNAUTHORIZED = {status_code: 401};
|
||||
|
||||
describe('Actions.Users', () => {
|
||||
let store;
|
||||
|
|
@ -21,7 +27,17 @@ describe('Actions.Users', () => {
|
|||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
store = await configureStore();
|
||||
const initial = {
|
||||
...initialState,
|
||||
entities: {
|
||||
...initialState.entities,
|
||||
users: {
|
||||
...initialState.entities.users,
|
||||
currentUserId: 'current-user-id',
|
||||
},
|
||||
},
|
||||
};
|
||||
store = await configureStore(initial);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
|
@ -171,50 +187,13 @@ describe('Actions.Users', () => {
|
|||
});
|
||||
|
||||
it('logout', async () => {
|
||||
const emit = jest.spyOn(EventEmitter, 'emit');
|
||||
nock(Client4.getBaseRoute()).
|
||||
post('/users/logout').
|
||||
reply(200, OK_RESPONSE);
|
||||
|
||||
await Actions.logout()(store.dispatch, store.getState);
|
||||
|
||||
const state = store.getState();
|
||||
const logoutRequest = state.requests.users.logout;
|
||||
const general = state.entities.general;
|
||||
const users = state.entities.users;
|
||||
const teams = state.entities.teams;
|
||||
const channels = state.entities.channels;
|
||||
const posts = state.entities.posts;
|
||||
const preferences = state.entities.preferences;
|
||||
|
||||
if (logoutRequest.status === RequestStatus.FAILURE) {
|
||||
throw new Error(JSON.stringify(logoutRequest.error));
|
||||
}
|
||||
|
||||
assert.deepStrictEqual(general.config, {}, 'config not empty');
|
||||
assert.deepStrictEqual(general.license, {}, 'license not empty');
|
||||
assert.strictEqual(users.currentUserId, '', 'current user id not empty');
|
||||
assert.deepStrictEqual(users.mySessions, [], 'user sessions not empty');
|
||||
assert.deepStrictEqual(users.myAudits, [], 'user audits not empty');
|
||||
assert.deepStrictEqual(users.profiles, {}, 'user profiles not empty');
|
||||
assert.deepStrictEqual(users.profilesInTeam, {}, 'users profiles in team not empty');
|
||||
assert.deepStrictEqual(users.profilesInChannel, {}, 'users profiles in channel not empty');
|
||||
assert.deepStrictEqual(users.profilesNotInChannel, {}, 'users profiles NOT in channel not empty');
|
||||
assert.deepStrictEqual(users.statuses, {}, 'users statuses not empty');
|
||||
assert.strictEqual(teams.currentTeamId, '', 'current team id is not empty');
|
||||
assert.deepStrictEqual(teams.teams, {}, 'teams is not empty');
|
||||
assert.deepStrictEqual(teams.myMembers, {}, 'team members is not empty');
|
||||
assert.deepStrictEqual(teams.membersInTeam, {}, 'members in team is not empty');
|
||||
assert.deepStrictEqual(teams.stats, {}, 'team stats is not empty');
|
||||
assert.strictEqual(channels.currentChannelId, '', 'current channel id is not empty');
|
||||
assert.deepStrictEqual(channels.channels, {}, 'channels is not empty');
|
||||
assert.deepStrictEqual(channels.channelsInTeam, {}, 'channelsInTeam is not empty');
|
||||
assert.deepStrictEqual(channels.myMembers, {}, 'channel members is not empty');
|
||||
assert.deepStrictEqual(channels.stats, {}, 'channel stats is not empty');
|
||||
assert.strictEqual(posts.selectedPostId, '', 'selected post id is not empty');
|
||||
assert.strictEqual(posts.currentFocusedPostId, '', 'current focused post id is not empty');
|
||||
assert.deepStrictEqual(posts.posts, {}, 'posts is not empty');
|
||||
assert.deepStrictEqual(posts.postsInChannel, {}, 'posts by channel is not empty');
|
||||
assert.deepStrictEqual(preferences.myPreferences, {}, 'user preferences not empty');
|
||||
await store.dispatch(logout(false));
|
||||
expect(emit).toHaveBeenCalledWith(NavigationTypes.NAVIGATION_RESET);
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
post('/users/login').
|
||||
|
|
@ -625,7 +604,7 @@ describe('Actions.Users', () => {
|
|||
assert.strictEqual(sessions.length, 0);
|
||||
|
||||
TestHelper.mockLogin();
|
||||
await Actions.loginById(user.id, 'password1')(store.dispatch, store.getState);
|
||||
await store.dispatch(Actions.loginById(user.id, 'password1'));
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
post('/users/login').
|
||||
|
|
@ -635,7 +614,7 @@ describe('Actions.Users', () => {
|
|||
nock(Client4.getBaseRoute()).
|
||||
get(`/users/${user.id}/sessions`).
|
||||
reply(200, [{id: TestHelper.generateId(), create_at: 1507756921338, expires_at: 1510348921338, last_activity_at: 1507821125630, user_id: TestHelper.basicUser.id, device_id: '', roles: 'system_admin system_user'}, {id: TestHelper.generateId(), create_at: 1507756921338, expires_at: 1510348921338, last_activity_at: 1507821125630, user_id: TestHelper.basicUser.id, device_id: '', roles: 'system_admin system_user'}]);
|
||||
await Actions.getSessions(user.id)(store.dispatch, store.getState);
|
||||
await store.dispatch(Actions.getSessions(user.id));
|
||||
|
||||
sessions = store.getState().entities.users.mySessions;
|
||||
assert.ok(sessions.length > 1);
|
||||
|
|
@ -643,23 +622,14 @@ describe('Actions.Users', () => {
|
|||
nock(Client4.getBaseRoute()).
|
||||
post(`/users/${user.id}/sessions/revoke/all`).
|
||||
reply(200, OK_RESPONSE);
|
||||
const {data} = await Actions.revokeAllSessionsForUser(user.id)(store.dispatch, store.getState);
|
||||
const {data} = await store.dispatch(Actions.revokeAllSessionsForUser(user.id));
|
||||
assert.deepEqual(data, true);
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
get('/users').
|
||||
query(true).
|
||||
reply(401, {});
|
||||
await Actions.getProfiles(0)(store.dispatch, store.getState);
|
||||
|
||||
const logoutRequest = store.getState().requests.users.logout;
|
||||
if (logoutRequest.status === RequestStatus.FAILURE) {
|
||||
throw new Error(JSON.stringify(logoutRequest.error));
|
||||
}
|
||||
|
||||
sessions = store.getState().entities.users.mySessions;
|
||||
|
||||
assert.strictEqual(sessions.length, 0);
|
||||
reply(401, UNAUTHORIZED);
|
||||
await store.dispatch(Actions.getProfiles(0));
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
post('/users/login').
|
||||
|
|
@ -669,6 +639,11 @@ describe('Actions.Users', () => {
|
|||
|
||||
it('revokeSessionsForAllUsers', async () => {
|
||||
const user = TestHelper.basicUser;
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
post('/roles/names').
|
||||
reply(200, []);
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
post('/users/logout').
|
||||
reply(200, OK_RESPONSE);
|
||||
|
|
@ -678,7 +653,7 @@ describe('Actions.Users', () => {
|
|||
assert.strictEqual(sessions.length, 0);
|
||||
|
||||
TestHelper.mockLogin();
|
||||
await Actions.loginById(user.id, 'password1')(store.dispatch, store.getState);
|
||||
await store.dispatch(Actions.loginById(user.id, 'password1'));
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
post('/users/login').
|
||||
|
|
@ -688,7 +663,7 @@ describe('Actions.Users', () => {
|
|||
nock(Client4.getBaseRoute()).
|
||||
get(`/users/${user.id}/sessions`).
|
||||
reply(200, [{id: TestHelper.generateId(), create_at: 1507756921338, expires_at: 1510348921338, last_activity_at: 1507821125630, user_id: TestHelper.basicUser.id, device_id: '', roles: 'system_admin system_user'}, {id: TestHelper.generateId(), create_at: 1507756921338, expires_at: 1510348921338, last_activity_at: 1507821125630, user_id: TestHelper.basicUser.id, device_id: '', roles: 'system_admin system_user'}]);
|
||||
await Actions.getSessions(user.id)(store.dispatch, store.getState);
|
||||
await store.dispatch(Actions.getSessions(user.id));
|
||||
|
||||
sessions = store.getState().entities.users.mySessions;
|
||||
assert.ok(sessions.length > 1);
|
||||
|
|
@ -696,24 +671,9 @@ describe('Actions.Users', () => {
|
|||
nock(Client4.getBaseRoute()).
|
||||
post('/users/sessions/revoke/all').
|
||||
reply(200, OK_RESPONSE);
|
||||
const {data} = await Actions.revokeSessionsForAllUsers(user.id)(store.dispatch, store.getState);
|
||||
const {data} = await store.dispatch(Actions.revokeSessionsForAllUsers(user.id));
|
||||
assert.deepEqual(data, true);
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
get('/users').
|
||||
query(true).
|
||||
reply(401, {});
|
||||
await Actions.getProfiles(0)(store.dispatch, store.getState);
|
||||
|
||||
const logoutRequest = store.getState().requests.users.logout;
|
||||
if (logoutRequest.status === RequestStatus.FAILURE) {
|
||||
throw new Error(JSON.stringify(logoutRequest.error));
|
||||
}
|
||||
|
||||
sessions = store.getState().entities.users.mySessions;
|
||||
|
||||
assert.strictEqual(sessions.length, 0);
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
post('/users/login').
|
||||
reply(200, TestHelper.basicUser);
|
||||
|
|
|
|||
|
|
@ -242,22 +242,6 @@ export function loadMe(): ActionFunc {
|
|||
};
|
||||
}
|
||||
|
||||
export function logout(): ActionFunc {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
dispatch({type: UserTypes.LOGOUT_REQUEST, data: null});
|
||||
|
||||
try {
|
||||
await Client4.logout();
|
||||
} catch (error) {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
dispatch({type: UserTypes.LOGOUT_SUCCESS, data: null});
|
||||
|
||||
return {data: true};
|
||||
};
|
||||
}
|
||||
|
||||
export function getTotalUsersStats(): ActionFunc {
|
||||
return bindClientFunc({
|
||||
clientFunc: Client4.getTotalUsersStats,
|
||||
|
|
@ -1421,7 +1405,6 @@ export default {
|
|||
checkMfa,
|
||||
generateMfaSecret,
|
||||
login,
|
||||
logout,
|
||||
getProfiles,
|
||||
getProfilesByIds,
|
||||
getProfilesInTeam,
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export default {
|
|||
GM_CHANNEL: 'G',
|
||||
PUSH_NOTIFY_APPLE_REACT_NATIVE: 'apple_rn',
|
||||
PUSH_NOTIFY_ANDROID_REACT_NATIVE: 'android_rn',
|
||||
STORE_REHYDRATION_COMPLETE: 'store_hydation_complete',
|
||||
STORE_REHYDRATION_COMPLETE: 'store_hydration_complete',
|
||||
OFFLINE_STORE_RESET: 'offline_store_reset',
|
||||
OFFLINE_STORE_PURGE: 'offline_store_purge',
|
||||
TEAMMATE_NAME_DISPLAY: {
|
||||
|
|
@ -68,4 +68,5 @@ export default {
|
|||
DISABLED: 'disabled',
|
||||
DEFAULT_ON: 'default_on',
|
||||
DEFAULT_OFF: 'default_off',
|
||||
REHYDRATED: 'app/REHYDRATED',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {combineReducers} from 'redux';
|
||||
import {BotTypes, UserTypes} from '@mm-redux/action_types';
|
||||
import {BotTypes} from '@mm-redux/action_types';
|
||||
import {GenericAction} from '@mm-redux/types/actions';
|
||||
import {Dictionary} from '@mm-redux/types/utilities';
|
||||
import {Bot} from '@mm-redux/types/bots';
|
||||
|
|
@ -22,8 +22,6 @@ function accounts(state: Dictionary<Bot> = {}, action: GenericAction) {
|
|||
nextState[bot.user_id] = bot;
|
||||
return nextState;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {combineReducers} from 'redux';
|
|||
|
||||
import {CategoryTypes} from '../../constants/channel_categories';
|
||||
|
||||
import {ChannelCategoryTypes, TeamTypes, UserTypes} from '@mm-redux/action_types';
|
||||
import {ChannelCategoryTypes, TeamTypes} from '@mm-redux/action_types';
|
||||
|
||||
import {GenericAction} from '@mm-redux/types/actions';
|
||||
import {ChannelCategory} from '@mm-redux/types/channel_categories';
|
||||
|
|
@ -79,8 +79,6 @@ export function byId(state: IDMappedObjects<ChannelCategory> = {}, action: Gener
|
|||
return nextState;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -141,8 +139,6 @@ export function orderByTeam(state: RelationOneToOne<Team, $ID<ChannelCategory>[]
|
|||
return nextState;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -433,18 +433,6 @@ describe('channels', () => {
|
|||
});
|
||||
expect(nextState.channel1).toBe(undefined);
|
||||
});
|
||||
test('remove all marks if user logs out', () => {
|
||||
const state = deepFreeze({
|
||||
channel1: true,
|
||||
channel231: false,
|
||||
});
|
||||
const nextState = Reducers.manuallyUnread(state, {
|
||||
type: UserTypes.LOGOUT_SUCCESS,
|
||||
data: {},
|
||||
});
|
||||
expect(nextState.channel1).toBe(undefined);
|
||||
expect(nextState.channel231).toBe(undefined);
|
||||
});
|
||||
});
|
||||
describe('RECEIVED_CHANNELS', () => {
|
||||
test('should not remove current channel', () => {
|
||||
|
|
|
|||
|
|
@ -42,8 +42,6 @@ function currentChannelId(state = '', action: GenericAction) {
|
|||
switch (action.type) {
|
||||
case ChannelTypes.SELECT_CHANNEL:
|
||||
return action.data;
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return '';
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -182,8 +180,6 @@ function channels(state: IDMappedObjects<Channel> = {}, action: GenericAction) {
|
|||
return hasNewValues ? nextState : state;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -217,8 +213,6 @@ function channelsInTeam(state: RelationOneToMany<Team, Channel> = {}, action: Ge
|
|||
};
|
||||
return channelListToSet(state, values);
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -396,8 +390,6 @@ function myMembers(state: RelationOneToOne<Channel, ChannelMembership> = {}, act
|
|||
return state;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -462,8 +454,6 @@ function membersInChannel(state: RelationOneToOne<Channel, UserIDMappedObjects<C
|
|||
case ChannelTypes.UPDATED_CHANNEL_MEMBER_SCHEME_ROLES: {
|
||||
return updateChannelMemberSchemeRoles(state, action);
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -546,8 +536,6 @@ function stats(state: RelationOneToOne<Channel, ChannelStats> = {}, action: Gene
|
|||
|
||||
return state;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -633,11 +621,6 @@ export function manuallyUnread(state: RelationOneToOne<Channel, boolean> = {}, a
|
|||
}
|
||||
return state;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS: {
|
||||
// user is logging out, remove any reference
|
||||
return {};
|
||||
}
|
||||
|
||||
case ChannelTypes.ADD_MANUALLY_UNREAD:
|
||||
case ChannelTypes.POST_UNREAD_SUCCESS: {
|
||||
return {...state, [action.data.channelId]: true};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {combineReducers} from 'redux';
|
||||
import {EmojiTypes, PostTypes, UserTypes} from '@mm-redux/action_types';
|
||||
import {EmojiTypes, PostTypes} from '@mm-redux/action_types';
|
||||
import {EmojisState, CustomEmoji} from '@mm-redux/types/emojis';
|
||||
import * as types from '@mm-redux/types';
|
||||
|
||||
|
|
@ -25,7 +25,6 @@ export function customEmoji(state: types.utilities.IDMappedObjects<CustomEmoji>
|
|||
return nextState;
|
||||
}
|
||||
case EmojiTypes.CLEAR_CUSTOM_EMOJIS:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
case PostTypes.RECEIVED_NEW_POST:
|
||||
|
|
@ -94,7 +93,6 @@ function nonExistentEmoji(state: Set<string> = new Set(), action: types.actions.
|
|||
return changed ? nextState : state;
|
||||
}
|
||||
case EmojiTypes.CLEAR_CUSTOM_EMOJIS:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return new Set();
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {combineReducers} from 'redux';
|
||||
import {FileTypes, PostTypes, UserTypes} from '@mm-redux/action_types';
|
||||
import {FileTypes, PostTypes} from '@mm-redux/action_types';
|
||||
import {GenericAction} from '@mm-redux/types/actions';
|
||||
import {Post} from '@mm-redux/types/posts';
|
||||
import {FileInfo} from '@mm-redux/types/files';
|
||||
|
|
@ -50,8 +50,6 @@ export function files(state: Dictionary<FileInfo> = {}, action: GenericAction) {
|
|||
return state;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -109,8 +107,6 @@ export function fileIdsByPostId(state: Dictionary<Array<string>> = {}, action: G
|
|||
return state;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -132,8 +128,6 @@ function filePublicLink(state: {link: string} = {link: ''}, action: GenericActio
|
|||
case FileTypes.RECEIVED_FILE_PUBLIC_LINK: {
|
||||
return action.data;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {link: ''};
|
||||
|
||||
default:
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ function config(state: Partial<Config> = {}, action: GenericAction) {
|
|||
case GeneralTypes.SET_CONFIG_AND_LICENSE:
|
||||
return Object.assign({}, state, action.data.config);
|
||||
case GeneralTypes.CLIENT_CONFIG_RESET:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -40,8 +38,6 @@ function credentials(state: any = {}, action: GenericAction) {
|
|||
return {
|
||||
url: action.data.url,
|
||||
};
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -51,8 +47,6 @@ function dataRetentionPolicy(state: any = {}, action: GenericAction) {
|
|||
switch (action.type) {
|
||||
case GeneralTypes.RECEIVED_DATA_RETENTION_POLICY:
|
||||
return action.data;
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -74,8 +68,6 @@ function license(state: any = {}, action: GenericAction) {
|
|||
case GeneralTypes.SET_CONFIG_AND_LICENSE:
|
||||
return Object.assign({}, state, action.data.license);
|
||||
case GeneralTypes.CLIENT_LICENSE_RESET:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -85,8 +77,6 @@ function timezones(state: string[] = [], action: GenericAction) {
|
|||
switch (action.type) {
|
||||
case GeneralTypes.SUPPORTED_TIMEZONES_RECEIVED:
|
||||
return action.data;
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return [];
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -96,8 +86,6 @@ function serverVersion(state = '', action: GenericAction) {
|
|||
switch (action.type) {
|
||||
case GeneralTypes.RECEIVED_SERVER_VERSION:
|
||||
return action.data;
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return '';
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {combineReducers} from 'redux';
|
||||
import {IntegrationTypes, UserTypes, ChannelTypes} from '@mm-redux/action_types';
|
||||
import {IntegrationTypes, ChannelTypes} from '@mm-redux/action_types';
|
||||
import {GenericAction} from '@mm-redux/types/actions';
|
||||
import {Command, IncomingWebhook, OutgoingWebhook, OAuthApp} from '@mm-redux/types/integrations';
|
||||
import {Dictionary, IDMappedObjects} from '@mm-redux/types/utilities';
|
||||
|
|
@ -42,8 +42,6 @@ function incomingHooks(state: IDMappedObjects<IncomingWebhook> = {}, action: Gen
|
|||
|
||||
return state;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
default:
|
||||
return state;
|
||||
|
|
@ -85,8 +83,6 @@ function outgoingHooks(state: IDMappedObjects<OutgoingWebhook> = {}, action: Gen
|
|||
|
||||
return state;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
default:
|
||||
return state;
|
||||
|
|
@ -131,8 +127,6 @@ function commands(state: IDMappedObjects<Command> = {}, action: GenericAction) {
|
|||
Reflect.deleteProperty(nextState, action.data.id);
|
||||
return nextState;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
default:
|
||||
return state;
|
||||
|
|
@ -159,8 +153,6 @@ function systemCommands(state: IDMappedObjects<Command> = {}, action: GenericAct
|
|||
}
|
||||
|
||||
return state;
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
default:
|
||||
return state;
|
||||
|
|
@ -186,8 +178,6 @@ function oauthApps(state: IDMappedObjects<OAuthApp> = {}, action: GenericAction)
|
|||
Reflect.deleteProperty(nextState, action.data.id);
|
||||
return nextState;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
default:
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {ChannelTypes, GeneralTypes, PostTypes, UserTypes} from '@mm-redux/action_types';
|
||||
import {ChannelTypes, GeneralTypes, PostTypes} from '@mm-redux/action_types';
|
||||
import {Posts} from '../../constants';
|
||||
import {comparePosts} from '@mm-redux/utils/post_utils';
|
||||
import {Post, PostsState, PostOrderBlock, MessageHistory} from '@mm-redux/types/posts';
|
||||
|
|
@ -165,9 +165,6 @@ export function handlePosts(state: RelationOneToOne<Post, Post> = {}, action: Ge
|
|||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -653,9 +650,6 @@ export function postsInChannel(state: Dictionary<Array<PostOrderBlock>> = {}, ac
|
|||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -937,9 +931,6 @@ export function postsInThread(state: RelationOneToMany<Post, Post> = {}, action:
|
|||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -949,8 +940,6 @@ function selectedPostId(state = '', action: GenericAction) {
|
|||
switch (action.type) {
|
||||
case PostTypes.RECEIVED_POST_SELECTED:
|
||||
return action.data;
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return '';
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -960,8 +949,6 @@ function currentFocusedPostId(state = '', action: GenericAction) {
|
|||
switch (action.type) {
|
||||
case PostTypes.RECEIVED_FOCUSED_POST:
|
||||
return action.data;
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return '';
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -1033,8 +1020,6 @@ export function reactions(state: RelationOneToOne<Post, Dictionary<Reaction>> =
|
|||
return state;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -1079,8 +1064,6 @@ export function openGraph(state: RelationOneToOne<Post, any> = {}, action: Gener
|
|||
return posts.reduce(storeOpenGraphForPost, state);
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -1165,16 +1148,6 @@ function messagesHistory(state: Partial<MessageHistory> = {}, action: GenericAct
|
|||
index: nextIndex,
|
||||
};
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS: {
|
||||
const index: Dictionary<number> = {};
|
||||
index[Posts.MESSAGE_TYPES.POST] = -1;
|
||||
index[Posts.MESSAGE_TYPES.COMMENT] = -1;
|
||||
|
||||
return {
|
||||
messages: [],
|
||||
index,
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,9 +53,6 @@ function myPreferences(state: Dictionary<PreferenceType> = {}, action: GenericAc
|
|||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {combineReducers} from 'redux';
|
||||
import {RoleTypes, UserTypes} from '@mm-redux/action_types';
|
||||
import {RoleTypes} from '@mm-redux/action_types';
|
||||
import {GenericAction} from '@mm-redux/types/actions';
|
||||
import {Dictionary} from '@mm-redux/types/utilities';
|
||||
import {Role} from '@mm-redux/types/roles';
|
||||
|
|
@ -11,8 +11,6 @@ function pending(state: Set<string> = new Set(), action: GenericAction) {
|
|||
switch (action.type) {
|
||||
case RoleTypes.SET_PENDING_ROLES:
|
||||
return action.data;
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return new Set();
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -49,9 +47,6 @@ function roles(state: Dictionary<Role> = {}, action: GenericAction) {
|
|||
|
||||
return state;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {combineReducers} from 'redux';
|
||||
import {SchemeTypes, UserTypes} from '@mm-redux/action_types';
|
||||
import {SchemeTypes} from '@mm-redux/action_types';
|
||||
import {GenericAction} from '@mm-redux/types/actions';
|
||||
import {SchemesState, Scheme} from '@mm-redux/types/schemes';
|
||||
|
||||
|
|
@ -34,9 +34,6 @@ function schemes(state: {
|
|||
return nextState;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,17 +100,6 @@ describe('reducers.entities.search', () => {
|
|||
const actualState = reducer({results: inputState}, action);
|
||||
assert.deepEqual(actualState.results, expectedState);
|
||||
});
|
||||
|
||||
describe('UserTypes.LOGOUT_SUCCESS', () => {
|
||||
const inputState = ['abcd', 'efgh'];
|
||||
const action = {
|
||||
type: UserTypes.LOGOUT_SUCCESS,
|
||||
};
|
||||
const expectedState = [];
|
||||
|
||||
const actualState = reducer({results: inputState}, action);
|
||||
assert.deepEqual(actualState.results, expectedState);
|
||||
});
|
||||
});
|
||||
|
||||
describe('matches', () => {
|
||||
|
|
@ -251,20 +240,6 @@ describe('reducers.entities.search', () => {
|
|||
const actualState = reducer({matches: inputState}, action);
|
||||
assert.deepEqual(actualState.matches, expectedState);
|
||||
});
|
||||
|
||||
describe('UserTypes.LOGOUT_SUCCESS', () => {
|
||||
const inputState = {
|
||||
abcd: ['test', 'testing'],
|
||||
efgh: ['tests'],
|
||||
};
|
||||
const action = {
|
||||
type: UserTypes.LOGOUT_SUCCESS,
|
||||
};
|
||||
const expectedState = [];
|
||||
|
||||
const actualState = reducer({matches: inputState}, action);
|
||||
assert.deepEqual(actualState.matches, expectedState);
|
||||
});
|
||||
});
|
||||
|
||||
describe('pinned', () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {combineReducers} from 'redux';
|
||||
import {PostTypes, PreferenceTypes, SearchTypes, UserTypes} from '@mm-redux/action_types';
|
||||
import {PostTypes, PreferenceTypes, SearchTypes} from '@mm-redux/action_types';
|
||||
import {Preferences} from '../../constants';
|
||||
import {PreferenceType} from '@mm-redux/types/preferences';
|
||||
import {GenericAction} from '@mm-redux/types/actions';
|
||||
|
|
@ -28,7 +28,6 @@ function results(state: Array<string> = [], action: GenericAction) {
|
|||
return state;
|
||||
}
|
||||
case SearchTypes.REMOVE_SEARCH_POSTS:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return [];
|
||||
|
||||
default:
|
||||
|
|
@ -53,7 +52,6 @@ function matches(state: Dictionary<Array<string>> = {}, action: GenericAction) {
|
|||
return newState;
|
||||
}
|
||||
case SearchTypes.REMOVE_SEARCH_POSTS:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return [];
|
||||
|
||||
default:
|
||||
|
|
@ -115,7 +113,6 @@ function flagged(state: Array<string> = [], action: GenericAction) {
|
|||
return state;
|
||||
}
|
||||
case SearchTypes.REMOVE_SEARCH_POSTS:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return [];
|
||||
|
||||
default:
|
||||
|
|
@ -187,8 +184,6 @@ function pinned(state: Dictionary<Array<string>> = {}, action: GenericAction) {
|
|||
|
||||
return state;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return [];
|
||||
|
||||
default:
|
||||
return state;
|
||||
|
|
@ -232,8 +227,6 @@ function recent(state: Dictionary<Array<Search>> = {}, action: GenericAction) {
|
|||
|
||||
return nextState;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
default:
|
||||
return state;
|
||||
|
|
@ -254,8 +247,6 @@ function current(state: any = {}, action: GenericAction) {
|
|||
},
|
||||
};
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
default:
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ function currentTeamId(state = '', action: GenericAction) {
|
|||
case TeamTypes.SELECT_TEAM:
|
||||
return action.data;
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return '';
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -61,9 +59,6 @@ function teams(state: IDMappedObjects<Team> = {}, action: GenericAction) {
|
|||
return {...state, [teamId]: {...team, scheme_id: schemeId}};
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -248,8 +243,6 @@ function myMembers(state: RelationOneToOne<Team, TeamMembership> = {}, action: G
|
|||
|
||||
return nextState;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -328,8 +321,6 @@ function membersInTeam(state: RelationOneToOne<Team, RelationOneToOne<UserProfil
|
|||
case TeamTypes.UPDATED_TEAM_MEMBER_SCHEME_ROLES: {
|
||||
return updateTeamMemberSchemeRoles(state, action);
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -354,8 +345,6 @@ function stats(state: any = {}, action: GenericAction) {
|
|||
|
||||
return state;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,24 +266,6 @@ describe('Reducers.users', () => {
|
|||
const newState = reducer(state, action);
|
||||
assert.deepEqual(newState.profilesInChannel, expectedState.profilesInChannel);
|
||||
});
|
||||
|
||||
it('UserTypes.LOGOUT_SUCCESS, existing profiles', () => {
|
||||
const state = {
|
||||
profilesInChannel: {
|
||||
id: new Set().add('old_user_id'),
|
||||
other_id: new Set().add('other_user_id'),
|
||||
},
|
||||
};
|
||||
const action = {
|
||||
type: UserTypes.LOGOUT_SUCCESS,
|
||||
};
|
||||
const expectedState = {
|
||||
profilesInChannel: {},
|
||||
};
|
||||
|
||||
const newState = reducer(state, action);
|
||||
assert.deepEqual(newState.profilesInChannel, expectedState.profilesInChannel);
|
||||
});
|
||||
});
|
||||
|
||||
describe('profilesNotInChannel', () => {
|
||||
|
|
@ -545,23 +527,5 @@ describe('Reducers.users', () => {
|
|||
const newState = reducer(state, action);
|
||||
assert.deepEqual(newState.profilesNotInChannel, expectedState.profilesNotInChannel);
|
||||
});
|
||||
|
||||
it('UserTypes.LOGOUT_SUCCESS, existing profiles', () => {
|
||||
const state = {
|
||||
profilesNotInChannel: {
|
||||
id: new Set().add('old_user_id'),
|
||||
other_id: new Set().add('other_user_id'),
|
||||
},
|
||||
};
|
||||
const action = {
|
||||
type: UserTypes.LOGOUT_SUCCESS,
|
||||
};
|
||||
const expectedState = {
|
||||
profilesNotInChannel: {},
|
||||
};
|
||||
|
||||
const newState = reducer(state, action);
|
||||
assert.deepEqual(newState.profilesNotInChannel, expectedState.profilesNotInChannel);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -102,10 +102,7 @@ function currentUserId(state = '', action: GenericAction) {
|
|||
|
||||
return user ? user.id : state;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return '';
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
|
@ -139,9 +136,6 @@ function mySessions(state: Array<{id: string}> = [], action: GenericAction) {
|
|||
case UserTypes.REVOKE_SESSIONS_FOR_ALL_USERS_SUCCESS:
|
||||
return [];
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return [];
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -152,9 +146,6 @@ function myAudits(state = [], action: GenericAction) {
|
|||
case UserTypes.RECEIVED_AUDITS:
|
||||
return [...action.data];
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return [];
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -212,8 +203,6 @@ function profiles(state: IDMappedObjects<UserProfile> = {}, action: GenericActio
|
|||
|
||||
return state;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
case UserTypes.RECEIVED_TERMS_OF_SERVICE_STATUS: {
|
||||
const data = action.data || action.payload;
|
||||
return {
|
||||
|
|
@ -255,9 +244,6 @@ function profilesInTeam(state: RelationOneToMany<Team, UserProfile> = {}, action
|
|||
case UserTypes.RECEIVED_PROFILES_LIST_NOT_IN_TEAM:
|
||||
return removeProfileListFromSet(state, action);
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
case UserTypes.PROFILE_NO_LONGER_VISIBLE:
|
||||
return removeProfileFromTeams(state, action);
|
||||
|
||||
|
|
@ -283,9 +269,6 @@ function profilesNotInTeam(state: RelationOneToMany<Team, UserProfile> = {}, act
|
|||
case UserTypes.RECEIVED_PROFILES_LIST_IN_TEAM:
|
||||
return removeProfileListFromSet(state, action);
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
case UserTypes.PROFILE_NO_LONGER_VISIBLE:
|
||||
return removeProfileFromTeams(state, action);
|
||||
|
||||
|
|
@ -312,9 +295,6 @@ function profilesWithoutTeam(state: Set<string> = new Set(), action: GenericActi
|
|||
nextSet.delete(action.data.id);
|
||||
return nextSet;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return new Set();
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -342,9 +322,6 @@ function profilesInChannel(state: RelationOneToMany<Channel, UserProfile> = {},
|
|||
user_id: action.data.user_id,
|
||||
}});
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
case UserTypes.PROFILE_NO_LONGER_VISIBLE:
|
||||
return removeProfileFromTeams(state, action);
|
||||
|
||||
|
|
@ -396,9 +373,6 @@ function profilesNotInChannel(state: RelationOneToMany<Channel, UserProfile> = {
|
|||
user_id: action.data.user_id,
|
||||
}});
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
case UserTypes.PROFILE_NO_LONGER_VISIBLE:
|
||||
return removeProfileFromTeams(state, action);
|
||||
|
||||
|
|
@ -424,8 +398,6 @@ function statuses(state: RelationOneToOne<UserProfile, string> = {}, action: Gen
|
|||
|
||||
return nextState;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
case UserTypes.PROFILE_NO_LONGER_VISIBLE: {
|
||||
if (state[action.data.user_id]) {
|
||||
const newState = {...state};
|
||||
|
|
@ -483,8 +455,6 @@ function isManualStatus(state: RelationOneToOne<UserProfile, boolean> = {}, acti
|
|||
|
||||
return nextState;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
case UserTypes.PROFILE_NO_LONGER_VISIBLE: {
|
||||
if (state[action.data.user_id]) {
|
||||
const newState = {...state};
|
||||
|
|
@ -541,7 +511,6 @@ function myUserAccessTokens(state: any = {}, action: GenericAction) {
|
|||
}
|
||||
|
||||
case UserTypes.CLEAR_MY_USER_ACCESS_TOKENS:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ function checkMfa(state: RequestStatusType = initialRequestState(), action: Gene
|
|||
case UserTypes.CHECK_MFA_FAILURE:
|
||||
return {...state, status: RequestStatus.FAILURE, error: action.error};
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {...state, status: RequestStatus.NOT_STARTED, error: null};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -39,28 +36,6 @@ function login(state: RequestStatusType = initialRequestState(), action: Generic
|
|||
case UserTypes.LOGIN_FAILURE:
|
||||
return {...state, status: RequestStatus.FAILURE, error: action.error};
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {...state, status: RequestStatus.NOT_STARTED, error: null};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
function logout(state: RequestStatusType = initialRequestState(), action: GenericAction): RequestStatusType {
|
||||
switch (action.type) {
|
||||
case UserTypes.LOGOUT_REQUEST:
|
||||
return {...state, status: RequestStatus.STARTED};
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {...state, status: RequestStatus.SUCCESS, error: null};
|
||||
|
||||
case UserTypes.LOGOUT_FAILURE:
|
||||
return {...state, status: RequestStatus.FAILURE, error: action.error};
|
||||
|
||||
case UserTypes.RESET_LOGOUT_STATE:
|
||||
return initialRequestState();
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
@ -72,7 +47,7 @@ function autocompleteUsers(state: RequestStatusType = initialRequestState(), act
|
|||
UserTypes.AUTOCOMPLETE_USERS_SUCCESS,
|
||||
UserTypes.AUTOCOMPLETE_USERS_FAILURE,
|
||||
state,
|
||||
action
|
||||
action,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -82,14 +57,13 @@ function updateMe(state: RequestStatusType = initialRequestState(), action: Gene
|
|||
UserTypes.UPDATE_ME_SUCCESS,
|
||||
UserTypes.UPDATE_ME_FAILURE,
|
||||
state,
|
||||
action
|
||||
action,
|
||||
);
|
||||
}
|
||||
|
||||
export default (combineReducers({
|
||||
checkMfa,
|
||||
login,
|
||||
logout,
|
||||
autocompleteUsers,
|
||||
updateMe,
|
||||
}) as (b: UsersRequestsStatuses, a: GenericAction) => UsersRequestsStatuses);
|
||||
|
|
|
|||
|
|
@ -27,9 +27,5 @@ export default function(state = getInitialState(), action: GenericAction) {
|
|||
};
|
||||
}
|
||||
|
||||
if (action.type === UserTypes.LOGOUT_SUCCESS) {
|
||||
return getInitialState();
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/* eslint-disable no-undefined */
|
||||
import * as redux from 'redux';
|
||||
import {createOfflineReducer, networkStatusChangedAction, offlineCompose} from 'redux-offline';
|
||||
import defaultOfflineConfig from 'redux-offline/lib/defaults';
|
||||
import reducerRegistry from './reducer_registry';
|
||||
import devTools from 'remote-redux-devtools';
|
||||
|
||||
const globalAny = global as any;
|
||||
const window = globalAny.window;
|
||||
|
||||
const devToolsEnhancer = typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__ ? // eslint-disable-line no-underscore-dangle
|
||||
window.__REDUX_DEVTOOLS_EXTENSION__ : // eslint-disable-line no-underscore-dangle
|
||||
() => {
|
||||
return devTools({
|
||||
name: 'Mattermost',
|
||||
hostname: 'localhost',
|
||||
port: 5678,
|
||||
realtime: true,
|
||||
});
|
||||
};
|
||||
import serviceReducer from '../reducers';
|
||||
import deepFreezeAndThrowOnMutation from '@mm-redux/utils/deep_freeze';
|
||||
import initialState from './initial_state';
|
||||
import {offlineConfig, createReducer} from './helpers';
|
||||
import {createMiddleware} from './middleware';
|
||||
import {Reducer, Action} from '@mm-redux/types/actions';
|
||||
import {GlobalState} from '@mm-redux/types/store';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* userOfflineConfig - Any additional configuration data to be passed into redux-offline aside from the default values.
|
||||
* 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.
|
||||
*/
|
||||
export default function configureServiceStore(preloadedState: any, appReducer: any, userOfflineConfig: any, getAppReducer: any, clientOptions: any) {
|
||||
const baseOfflineConfig = Object.assign({}, defaultOfflineConfig, offlineConfig, userOfflineConfig);
|
||||
const baseState = Object.assign({}, initialState, preloadedState);
|
||||
|
||||
const loadReduxDevtools = process.env.NODE_ENV !== 'test'; //eslint-disable-line no-process-env
|
||||
|
||||
const store = redux.createStore(
|
||||
createOfflineReducer(createDevReducer(baseState, serviceReducer, appReducer)),
|
||||
baseState,
|
||||
offlineCompose(baseOfflineConfig)(
|
||||
createMiddleware(clientOptions),
|
||||
loadReduxDevtools ? [devToolsEnhancer()] : []
|
||||
)
|
||||
);
|
||||
|
||||
reducerRegistry.setChangeListener((reducers: any) => {
|
||||
store.replaceReducer(createOfflineReducer(createDevReducer(baseState, reducers)));
|
||||
});
|
||||
|
||||
// launch store persistor
|
||||
if (baseOfflineConfig.persist) {
|
||||
baseOfflineConfig.persist(store, baseOfflineConfig.persistOptions, baseOfflineConfig.persistCallback);
|
||||
}
|
||||
|
||||
if (baseOfflineConfig.detectNetwork) {
|
||||
baseOfflineConfig.detectNetwork((online: boolean) => {
|
||||
store.dispatch(networkStatusChangedAction(online));
|
||||
});
|
||||
}
|
||||
|
||||
if ((module as any).hot) {
|
||||
// Enable Webpack hot module replacement for reducers
|
||||
(module as any).hot.accept(() => {
|
||||
const nextServiceReducer = require('../reducers').default; // eslint-disable-line global-require
|
||||
let nextAppReducer;
|
||||
if (getAppReducer) {
|
||||
nextAppReducer = getAppReducer(); // eslint-disable-line global-require
|
||||
}
|
||||
store.replaceReducer(createDevReducer(baseState, reducerRegistry.getReducers(), nextServiceReducer, nextAppReducer));
|
||||
});
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
function createDevReducer(baseState: any, ...reducers: any) {
|
||||
return enableFreezing(createReducer(baseState, ...reducers));
|
||||
}
|
||||
|
||||
function enableFreezing(reducer: Reducer) {
|
||||
return (state: GlobalState, action: Action) => {
|
||||
const nextState = reducer(state, action);
|
||||
|
||||
if (nextState !== state) {
|
||||
deepFreezeAndThrowOnMutation(nextState);
|
||||
}
|
||||
|
||||
return nextState;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import * as redux from 'redux';
|
||||
import reducerRegistry from './reducer_registry';
|
||||
|
||||
import serviceReducer from '../reducers';
|
||||
|
||||
import {offlineConfig, createReducer} from './helpers';
|
||||
import initialState from './initial_state';
|
||||
import {createMiddleware} from './middleware';
|
||||
|
||||
import {createOfflineReducer, networkStatusChangedAction, offlineCompose} from 'redux-offline';
|
||||
import defaultOfflineConfig from 'redux-offline/lib/defaults';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* userOfflineConfig - Any additional configuration data to be passed into redux-offline aside from the default values.
|
||||
* 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.
|
||||
*/
|
||||
export default function configureOfflineServiceStore(preloadedState: any, appReducer: any, userOfflineConfig: any, getAppReducer: any, clientOptions = {}) {
|
||||
const baseState = Object.assign({}, initialState, preloadedState);
|
||||
|
||||
const baseOfflineConfig = Object.assign({}, defaultOfflineConfig, offlineConfig, userOfflineConfig);
|
||||
|
||||
const store = redux.createStore(
|
||||
createOfflineReducer(createReducer(baseState, serviceReducer as any, appReducer)),
|
||||
baseState,
|
||||
offlineCompose(baseOfflineConfig)(
|
||||
createMiddleware(clientOptions),
|
||||
[]
|
||||
)
|
||||
);
|
||||
|
||||
reducerRegistry.setChangeListener((reducers: any) => {
|
||||
store.replaceReducer(createOfflineReducer(createReducer(baseState, reducers)));
|
||||
});
|
||||
|
||||
// launch store persistor
|
||||
if (baseOfflineConfig.persist) {
|
||||
baseOfflineConfig.persist(store, baseOfflineConfig.persistOptions, baseOfflineConfig.persistCallback);
|
||||
}
|
||||
|
||||
if (baseOfflineConfig.detectNetwork) {
|
||||
baseOfflineConfig.detectNetwork((online: boolean) => {
|
||||
store.dispatch(networkStatusChangedAction(online));
|
||||
});
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
|
|
@ -1,42 +1,38 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {combineReducers} from 'redux';
|
||||
import {General} from '../constants';
|
||||
import reducerRegistry from './reducer_registry';
|
||||
import {enableBatching, Action, Reducer} from '@mm-redux/types/actions';
|
||||
export const offlineConfig = {
|
||||
effect: (effect: Function, action: Action) => {
|
||||
if (typeof effect !== 'function') {
|
||||
throw new Error('Offline Action: effect must be a function.');
|
||||
} else if (!('meta' in action && action.meta && action.meta.offline.commit)) {
|
||||
throw new Error('Offline Action: commit action must be present.');
|
||||
}
|
||||
import {enableBatching, Reducer} from '@mm-redux/types/actions';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
|
||||
return effect();
|
||||
},
|
||||
discard: (error: Error, action: Action, retries: number) => {
|
||||
if ('meta' in action && action.meta && action.meta.offline.hasOwnProperty('maxRetry')) {
|
||||
return retries >= action.meta.offline.maxRetry;
|
||||
}
|
||||
/* eslint-disable no-console */
|
||||
|
||||
return retries > 10;
|
||||
},
|
||||
};
|
||||
|
||||
export function createReducer(baseState: any, ...reducers: Reducer[]) {
|
||||
export function createReducer(...reducers: Reducer[]) {
|
||||
reducerRegistry.setReducers(Object.assign({}, ...reducers));
|
||||
const baseReducer = combineReducers(reducerRegistry.getReducers());
|
||||
|
||||
// Root reducer wrapper that listens for reset events.
|
||||
// Returns whatever is passed for the data property
|
||||
// as the new state.
|
||||
function offlineReducer(state = {}, action: Action) {
|
||||
if ('type' in action && 'data' in action && action.type === General.OFFLINE_STORE_RESET) {
|
||||
return baseReducer(action.data || baseState, action);
|
||||
}
|
||||
return enableBatching(baseReducer);
|
||||
}
|
||||
|
||||
return baseReducer(state, action as any);
|
||||
const KEY_PREFIX = 'reduxPersist:';
|
||||
|
||||
export async function getStoredState() {
|
||||
const restoredState: Record<string, any> = {};
|
||||
let storeKeys: Array<string> = [];
|
||||
|
||||
try {
|
||||
const allKeys: Array<string> = await AsyncStorage.getAllKeys();
|
||||
storeKeys = allKeys.filter((key) => key.includes(KEY_PREFIX));
|
||||
|
||||
const values = await AsyncStorage.multiGet(storeKeys);
|
||||
|
||||
values.forEach(([key, data]: [string, string]) => {
|
||||
restoredState[key.slice(KEY_PREFIX.length)] = JSON.parse(data);
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('ERROR GETTING FROM AsyncStorage', error);
|
||||
}
|
||||
|
||||
return enableBatching(offlineReducer);
|
||||
}
|
||||
return {storeKeys, restoredState};
|
||||
}
|
||||
|
|
@ -1,6 +1,108 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/* eslint-disable global-require, no-process-env */
|
||||
const config = process.env.NODE_ENV === 'production' ? require('./configureStore.prod').default : require('./configureStore.dev').default;
|
||||
export default config;
|
||||
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};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,10 +205,6 @@ const state: GlobalState = {
|
|||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
logout: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
},
|
||||
autocompleteUsers: {
|
||||
status: 'not_started',
|
||||
error: null,
|
||||
|
|
|
|||
|
|
@ -3,20 +3,15 @@
|
|||
import thunk, {ThunkMiddleware} from 'redux-thunk';
|
||||
|
||||
import createActionBuffer from 'redux-action-buffer';
|
||||
import {REHYDRATE} from 'redux-persist/constants';
|
||||
import {PERSIST} from 'redux-persist';
|
||||
import {General} from '@mm-redux/constants';
|
||||
|
||||
const defaultOptions = {
|
||||
additionalMiddleware: [],
|
||||
enableBuffer: true,
|
||||
enableThunk: true,
|
||||
};
|
||||
export function createMiddleware(clientOptions: any): ThunkMiddleware[] {
|
||||
const options = Object.assign({}, defaultOptions, clientOptions);
|
||||
const {
|
||||
additionalMiddleware,
|
||||
enableBuffer,
|
||||
enableThunk,
|
||||
} = options;
|
||||
} = clientOptions;
|
||||
const middleware: ThunkMiddleware[] = [];
|
||||
|
||||
if (enableThunk) {
|
||||
|
|
@ -32,7 +27,7 @@ export function createMiddleware(clientOptions: any): ThunkMiddleware[] {
|
|||
}
|
||||
|
||||
if (enableBuffer) {
|
||||
middleware.push(createActionBuffer(REHYDRATE));
|
||||
middleware.push(createActionBuffer({breaker: General.REHYDRATED, passthrough: [PERSIST]}));
|
||||
}
|
||||
|
||||
return middleware;
|
||||
|
|
|
|||
142
app/mm-redux/store/mmkv_adapter.ts
Normal file
142
app/mm-redux/store/mmkv_adapter.ts
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
// 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;
|
||||
2
app/mm-redux/types/module.d.ts
vendored
2
app/mm-redux/types/module.d.ts
vendored
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
declare module 'gfycat-sdk';
|
||||
declare module 'redux-offline';
|
||||
declare module 'redux-offline/lib/defaults';
|
||||
declare module 'remote-redux-devtools';
|
||||
declare module 'redux-persist';
|
||||
declare module 'redux-persist/constants';
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ export type TeamsRequestsStatuses = {
|
|||
export type UsersRequestsStatuses = {
|
||||
checkMfa: RequestStatusType;
|
||||
login: RequestStatusType;
|
||||
logout: RequestStatusType;
|
||||
autocompleteUsers: RequestStatusType;
|
||||
updateMe: RequestStatusType;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {UserTypes} from '@mm-redux/action_types';
|
||||
import {DeviceTypes} from 'app/constants';
|
||||
import {DeviceTypes} from '@constants';
|
||||
|
||||
export default function connection(state = true, action) {
|
||||
switch (action.type) {
|
||||
case DeviceTypes.CONNECTION_CHANGED:
|
||||
return action.data;
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return true;
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
|
||||
import {Dimensions} from 'react-native';
|
||||
|
||||
import {UserTypes} from '@mm-redux/action_types';
|
||||
import {DeviceTypes} from 'app/constants';
|
||||
import {DeviceTypes} from '@constants';
|
||||
|
||||
const {height, width} = Dimensions.get('window');
|
||||
const initialState = {
|
||||
|
|
@ -21,8 +20,6 @@ export default function dimension(state = initialState, action) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return initialState;
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -3,12 +3,10 @@
|
|||
|
||||
import app from './app';
|
||||
import device from './device';
|
||||
import navigation from './navigation';
|
||||
import views from './views';
|
||||
|
||||
export default {
|
||||
app,
|
||||
device,
|
||||
navigation,
|
||||
views,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {NavigationTypes} from 'app/constants';
|
||||
import {UserTypes} from '@mm-redux/action_types';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
|
||||
export default function(state = '', action) {
|
||||
switch (action.type) {
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
setTimeout(() => {
|
||||
EventEmitter.emit(NavigationTypes.NAVIGATION_RESET);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
|
@ -1,16 +1,12 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {UserTypes} from '@mm-redux/action_types';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import {ViewTypes} from '@constants';
|
||||
|
||||
export default function banner(state = '', action) {
|
||||
switch (action.type) {
|
||||
case ViewTypes.ANNOUNCEMENT_BANNER:
|
||||
return action.data;
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return '';
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,14 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {combineReducers} from 'redux';
|
||||
|
||||
import {ViewTypes} from '@constants';
|
||||
import {
|
||||
ChannelTypes,
|
||||
FileTypes,
|
||||
PostTypes,
|
||||
UserTypes,
|
||||
} from '@mm-redux/action_types';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
function displayName(state = '', action) {
|
||||
switch (action.type) {
|
||||
case ViewTypes.SET_CHANNEL_DISPLAY_NAME:
|
||||
|
|
@ -355,9 +354,6 @@ function keepChannelIdAsUnread(state = null, action) {
|
|||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return null;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,13 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {combineReducers} from 'redux';
|
||||
import {UserTypes} from '@mm-redux/action_types';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import {ViewTypes} from '@constants';
|
||||
|
||||
function emojiPickerCustomPage(state = 0, action) {
|
||||
switch (action.type) {
|
||||
case ViewTypes.INCREMENT_EMOJI_PICKER_PAGE:
|
||||
return state + 1;
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return 0;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ function locale(state = defaultLocale, action) {
|
|||
const data = action.data || action.payload;
|
||||
return data.locale;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return defaultLocale;
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -31,8 +31,6 @@ function submittedMenuActions(state = {}, action) {
|
|||
|
||||
return nextState;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,13 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import {UserTypes} from '@mm-redux/action_types';
|
||||
import {ViewTypes} from '@constants';
|
||||
|
||||
export default function search(state = '', action) {
|
||||
switch (action.type) {
|
||||
case ViewTypes.SEARCH_DRAFT_CHANGED: {
|
||||
return action.text;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return '';
|
||||
|
||||
default:
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
import initialState from 'app/initial_state';
|
||||
import configureAppStore from './store';
|
||||
|
||||
const store = configureAppStore(initialState);
|
||||
const {store, persistor} = configureAppStore(initialState);
|
||||
|
||||
export {persistor};
|
||||
export default store;
|
||||
|
|
|
|||
|
|
@ -3,22 +3,14 @@
|
|||
|
||||
import {Platform} from 'react-native';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import {purgeStoredState} from 'redux-persist';
|
||||
import {REHYDRATE} from 'redux-persist';
|
||||
|
||||
import {NavigationTypes, ViewTypes} from 'app/constants';
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import initialState from 'app/initial_state';
|
||||
import {throttle} from 'app/utils/general';
|
||||
|
||||
import {General} from '@mm-redux/constants';
|
||||
import {ErrorTypes, GeneralTypes} from '@mm-redux/action_types';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
|
||||
import {getStateForReset} from './utils';
|
||||
|
||||
import {
|
||||
captureException,
|
||||
LOGGER_JAVASCRIPT_WARNING,
|
||||
|
|
@ -35,115 +27,92 @@ const SAVE_STATE_ACTIONS = [
|
|||
'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.
|
||||
const saveShareExtensionState = (store) => {
|
||||
function saveShareExtensionState(store) {
|
||||
return (next) => (action) => {
|
||||
if (SAVE_STATE_ACTIONS.includes(action.type)) {
|
||||
throttle(saveStateToFile(store));
|
||||
}
|
||||
return next(action);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const saveStateToFile = async (store) => {
|
||||
if (Platform.OS === 'ios') {
|
||||
const state = store.getState();
|
||||
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]);
|
||||
});
|
||||
if (state.entities) {
|
||||
const channelsInTeam = {...state.entities.channels.channelsInTeam};
|
||||
Object.keys(channelsInTeam).forEach((teamId) => {
|
||||
channelsInTeam[teamId] = Array.from(channelsInTeam[teamId]);
|
||||
});
|
||||
|
||||
const profilesInChannel = {...state.entities.users.profilesInChannel};
|
||||
Object.keys(profilesInChannel).forEach((channelId) => {
|
||||
profilesInChannel[channelId] = Array.from(profilesInChannel[channelId]);
|
||||
});
|
||||
const 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;
|
||||
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 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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const purgeAppCacheWrapper = (persistConfig) => (store) => {
|
||||
return (next) => (action) => {
|
||||
if (action.type === General.OFFLINE_STORE_PURGE) {
|
||||
purgeStoredState({...persistConfig, storage: AsyncStorage});
|
||||
|
||||
const state = store.getState();
|
||||
const resetState = getStateForReset(initialState, state);
|
||||
|
||||
store.dispatch(batchActions([
|
||||
{
|
||||
type: General.OFFLINE_STORE_RESET,
|
||||
data: resetState,
|
||||
},
|
||||
{
|
||||
type: ErrorTypes.RESTORE_ERRORS,
|
||||
data: [...state.errors],
|
||||
},
|
||||
{
|
||||
type: GeneralTypes.RECEIVED_APP_DEVICE_TOKEN,
|
||||
data: state.entities.general.deviceToken,
|
||||
},
|
||||
{
|
||||
type: GeneralTypes.RECEIVED_APP_CREDENTIALS,
|
||||
data: {
|
||||
url: state.entities.general.credentials.url,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: ViewTypes.SERVER_URL_CHANGED,
|
||||
serverUrl: state.entities.general.credentials.url || state.views.selectServer.serverUrl,
|
||||
},
|
||||
{
|
||||
type: GeneralTypes.RECEIVED_SERVER_VERSION,
|
||||
data: state.entities.general.serverVersion,
|
||||
},
|
||||
{
|
||||
type: General.STORE_REHYDRATION_COMPLETE,
|
||||
},
|
||||
], 'BATCH_FOR_RESTART'));
|
||||
|
||||
setTimeout(() => {
|
||||
EventEmitter.emit(NavigationTypes.RESTART_APP);
|
||||
}, 500);
|
||||
}
|
||||
return next(action);
|
||||
};
|
||||
};
|
||||
|
||||
const messageRetention = (store) => {
|
||||
return (next) => (action) => {
|
||||
if (action.type === 'persist/REHYDRATE') {
|
||||
const {app} = action.payload;
|
||||
const {entities, views} = action.payload;
|
||||
|
||||
|
|
@ -153,25 +122,24 @@ const messageRetention = (store) => {
|
|||
|
||||
// When a new version of the app has been detected
|
||||
if (!app || !app.version || app.version !== DeviceInfo.getVersion() || app.build !== DeviceInfo.getBuildNumber()) {
|
||||
return next(resetStateForNewVersion(action));
|
||||
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
|
||||
let nextAction;
|
||||
try {
|
||||
nextAction = cleanUpState(action);
|
||||
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);
|
||||
nextAction = action;
|
||||
}
|
||||
|
||||
return next(nextAction);
|
||||
return next(action);
|
||||
} else if (action.type === ViewTypes.DATA_CLEANUP) {
|
||||
const nextAction = cleanUpState(action, true);
|
||||
return next(nextAction);
|
||||
action.payload = cleanUpState(action.payload, true);
|
||||
return next(action);
|
||||
}
|
||||
|
||||
/* Uncomment the following lines to log the actions being dispatched */
|
||||
|
|
@ -185,10 +153,9 @@ const messageRetention = (store) => {
|
|||
|
||||
return next(action);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function resetStateForNewVersion(action) {
|
||||
const {payload} = action;
|
||||
function resetStateForNewVersion(payload) {
|
||||
const lastChannelForTeam = getLastChannelForTeam(payload);
|
||||
|
||||
let general = initialState.entities.general;
|
||||
|
|
@ -317,29 +284,28 @@ function resetStateForNewVersion(action) {
|
|||
},
|
||||
};
|
||||
|
||||
return {
|
||||
type: action.type,
|
||||
payload: nextState,
|
||||
error: action.error,
|
||||
};
|
||||
return nextState;
|
||||
}
|
||||
|
||||
function getLastChannelForTeam(payload) {
|
||||
const lastChannelForTeam = {...payload.views.team.lastChannelForTeam};
|
||||
const convertLastChannelForTeam = Object.values(lastChannelForTeam).some((value) => !Array.isArray(value));
|
||||
if (payload?.views?.team?.lastChannelForTeam) {
|
||||
const lastChannelForTeam = {...payload.views.team.lastChannelForTeam};
|
||||
const convertLastChannelForTeam = Object.values(lastChannelForTeam).some((value) => !Array.isArray(value));
|
||||
|
||||
if (convertLastChannelForTeam) {
|
||||
Object.keys(lastChannelForTeam).forEach((id) => {
|
||||
lastChannelForTeam[id] = [lastChannelForTeam[id]];
|
||||
});
|
||||
if (convertLastChannelForTeam) {
|
||||
Object.keys(lastChannelForTeam).forEach((id) => {
|
||||
lastChannelForTeam[id] = [lastChannelForTeam[id]];
|
||||
});
|
||||
}
|
||||
|
||||
return lastChannelForTeam;
|
||||
}
|
||||
|
||||
return lastChannelForTeam;
|
||||
return {};
|
||||
}
|
||||
|
||||
export function cleanUpState(action, keepCurrent = false) {
|
||||
const {payload: resetPayload} = resetStateForNewVersion(action);
|
||||
const {payload} = action;
|
||||
export function cleanUpState(payload, keepCurrent = false) {
|
||||
const resetPayload = resetStateForNewVersion(payload);
|
||||
const {currentChannelId} = payload.entities.channels;
|
||||
|
||||
const {lastChannelForTeam} = resetPayload.views.team;
|
||||
|
|
@ -470,6 +436,9 @@ export function cleanUpState(action, keepCurrent = false) {
|
|||
...resetPayload.views.channel,
|
||||
...payload.views.channel,
|
||||
},
|
||||
root: {
|
||||
hydrationComplete: true,
|
||||
},
|
||||
},
|
||||
websocket: {
|
||||
lastConnectAt: payload.websocket?.lastConnectAt,
|
||||
|
|
@ -479,11 +448,7 @@ export function cleanUpState(action, keepCurrent = false) {
|
|||
|
||||
nextState.errors = payload.errors;
|
||||
|
||||
return {
|
||||
type: action.type,
|
||||
payload: nextState,
|
||||
error: action.error,
|
||||
};
|
||||
return nextState;
|
||||
}
|
||||
|
||||
// cleanUpPostsInChannel returns a copy of postsInChannel where only the most recent posts in each channel are kept
|
||||
|
|
@ -564,17 +529,3 @@ function removePendingPost(pendingPostIds, id) {
|
|||
pendingPostIds.splice(pendingIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
export const middlewares = (persistConfig) => {
|
||||
const middlewareFunctions = [
|
||||
messageRetention,
|
||||
purgeAppCacheWrapper(persistConfig),
|
||||
];
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
middlewareFunctions.push(saveShareExtensionState);
|
||||
}
|
||||
|
||||
return middlewareFunctions;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -102,13 +102,13 @@ describe('cleanUpState', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const result = cleanUpState({payload: state});
|
||||
const result = cleanUpState(state);
|
||||
|
||||
expect(result.payload.entities.posts.posts.post1).toBeDefined();
|
||||
expect(result.payload.entities.posts.posts.post2).toBeUndefined();
|
||||
expect(result.payload.entities.posts.postsInChannel.channel1).toEqual([{order: ['post1'], recent: true}]);
|
||||
expect(result.payload.entities.search.results).toEqual(['post1', 'post2']);
|
||||
expect(result.payload.entities.search.flagged).toEqual(['post1', 'post2', 'post3']);
|
||||
expect(result.entities.posts.posts.post1).toBeDefined();
|
||||
expect(result.entities.posts.posts.post2).toBeUndefined();
|
||||
expect(result.entities.posts.postsInChannel.channel1).toEqual([{order: ['post1'], recent: true}]);
|
||||
expect(result.entities.search.results).toEqual(['post1', 'post2']);
|
||||
expect(result.entities.search.flagged).toEqual(['post1', 'post2', 'post3']);
|
||||
});
|
||||
|
||||
test('should keep failed pending post', () => {
|
||||
|
|
@ -145,11 +145,11 @@ describe('cleanUpState', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const result = cleanUpState({payload: state});
|
||||
const result = cleanUpState(state);
|
||||
|
||||
expect(result.payload.entities.posts.pendingPostIds).toEqual(['pending']);
|
||||
expect(result.payload.entities.posts.posts.pending).toBeDefined();
|
||||
expect(result.payload.entities.posts.postsInChannel.channel1).toEqual([{order: ['pending', 'post1', 'post2'], recent: true}]);
|
||||
expect(result.entities.posts.pendingPostIds).toEqual(['pending']);
|
||||
expect(result.entities.posts.posts.pending).toBeDefined();
|
||||
expect(result.entities.posts.postsInChannel.channel1).toEqual([{order: ['pending', 'post1', 'post2'], recent: true}]);
|
||||
});
|
||||
|
||||
test('should remove non-failed pending post', () => {
|
||||
|
|
@ -186,11 +186,11 @@ describe('cleanUpState', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const result = cleanUpState({payload: state});
|
||||
const result = cleanUpState(state);
|
||||
|
||||
expect(result.payload.entities.posts.pendingPostIds).toEqual([]);
|
||||
expect(result.payload.entities.posts.posts.pending).toBeUndefined();
|
||||
expect(result.payload.entities.posts.postsInChannel.channel1).toEqual([{order: ['post1', 'post2'], recent: true}]);
|
||||
expect(result.entities.posts.pendingPostIds).toEqual([]);
|
||||
expect(result.entities.posts.posts.pending).toBeUndefined();
|
||||
expect(result.entities.posts.postsInChannel.channel1).toEqual([{order: ['post1', 'post2'], recent: true}]);
|
||||
});
|
||||
|
||||
test('should remove non-existent pending post', () => {
|
||||
|
|
@ -226,10 +226,10 @@ describe('cleanUpState', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const result = cleanUpState({payload: state});
|
||||
const result = cleanUpState(state);
|
||||
|
||||
expect(result.payload.entities.posts.pendingPostIds).toEqual([]);
|
||||
expect(result.payload.entities.posts.postsInChannel.channel1).toEqual([{order: ['post1', 'post2'], recent: true}]);
|
||||
expect(result.entities.posts.pendingPostIds).toEqual([]);
|
||||
expect(result.entities.posts.postsInChannel.channel1).toEqual([{order: ['post1', 'post2'], recent: true}]);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +1,21 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import {createBlacklistFilter} from 'redux-persist-transform-filter';
|
||||
import {createTransform, persistStore} from 'redux-persist';
|
||||
import {createTransform} from 'redux-persist';
|
||||
import reduxReset from 'redux-reset';
|
||||
|
||||
import {General} from '@mm-redux/constants';
|
||||
import {getConfig} from '@mm-redux/selectors/entities/general';
|
||||
import configureStore from '@mm-redux/store';
|
||||
import MMKVStorageAdapter from '@mm-redux/store/mmkv_adapter';
|
||||
|
||||
import appReducer from 'app/reducers';
|
||||
import {getSiteUrl, setSiteUrl} from 'app/utils/image_cache_manager';
|
||||
import {createSentryMiddleware} from 'app/utils/sentry/middleware';
|
||||
|
||||
import {middlewares} from './middleware';
|
||||
import {createThunkMiddleware} from './thunk';
|
||||
import {transformSet} from './utils';
|
||||
|
||||
function getAppReducer() {
|
||||
return require('../../app/reducers'); // eslint-disable-line global-require
|
||||
}
|
||||
|
||||
const usersSetTransform = [
|
||||
'profilesInChannel',
|
||||
'profilesNotInChannel',
|
||||
|
|
@ -56,8 +51,9 @@ const channelViewBlackList = {loading: true, refreshing: true, loadingPosts: tru
|
|||
const channelViewBlackListFilter = createTransform(
|
||||
(inboundState) => {
|
||||
const channel = {};
|
||||
const keys = inboundState.channel ? Object.keys(inboundState.channel) : [];
|
||||
|
||||
for (const channelKey of Object.keys(inboundState.channel)) {
|
||||
for (const channelKey of keys) {
|
||||
if (!channelViewBlackList[channelKey]) {
|
||||
channel[channelKey] = inboundState.channel[channelKey];
|
||||
}
|
||||
|
|
@ -76,8 +72,9 @@ const emojiBlackList = {nonExistentEmoji: true};
|
|||
const emojiBlackListFilter = createTransform(
|
||||
(inboundState) => {
|
||||
const emojis = {};
|
||||
const keys = inboundState.emojis ? Object.keys(inboundState.emojis) : [];
|
||||
|
||||
for (const emojiKey of Object.keys(inboundState.emojis)) {
|
||||
for (const emojiKey of keys) {
|
||||
if (!emojiBlackList[emojiKey]) {
|
||||
emojis[emojiKey] = inboundState.emojis[emojiKey];
|
||||
}
|
||||
|
|
@ -124,47 +121,16 @@ const setTransformer = createTransform(
|
|||
);
|
||||
|
||||
const persistConfig = {
|
||||
effect: (effect, action) => {
|
||||
if (typeof effect !== 'function') {
|
||||
throw new Error('Offline Action: effect must be a function.');
|
||||
} else if (!action.meta.offline.commit) {
|
||||
throw new Error('Offline Action: commit action must be present.');
|
||||
}
|
||||
|
||||
return effect();
|
||||
},
|
||||
persist: (store, options) => {
|
||||
const persistor = persistStore(store, {storage: AsyncStorage, ...options}, () => {
|
||||
store.dispatch({
|
||||
type: General.STORE_REHYDRATION_COMPLETE,
|
||||
});
|
||||
});
|
||||
|
||||
store.subscribe(async () => {
|
||||
const state = store.getState();
|
||||
const config = getConfig(state);
|
||||
|
||||
if (getSiteUrl() !== config?.SiteURL) {
|
||||
setSiteUrl(config.SiteURL);
|
||||
}
|
||||
});
|
||||
|
||||
return persistor;
|
||||
},
|
||||
persistOptions: {
|
||||
autoRehydrate: {
|
||||
log: false,
|
||||
},
|
||||
blacklist: ['device', 'navigation', 'offline', 'requests'],
|
||||
debounce: 500,
|
||||
transforms: [
|
||||
setTransformer,
|
||||
viewsBlackListFilter,
|
||||
typingBlackListFilter,
|
||||
channelViewBlackListFilter,
|
||||
emojiBlackListFilter,
|
||||
],
|
||||
},
|
||||
key: 'root',
|
||||
storage: MMKVStorageAdapter,
|
||||
blacklist: ['device', 'navigation', 'offline', 'requests'],
|
||||
transforms: [
|
||||
setTransformer,
|
||||
viewsBlackListFilter,
|
||||
typingBlackListFilter,
|
||||
channelViewBlackListFilter,
|
||||
emojiBlackListFilter,
|
||||
],
|
||||
};
|
||||
|
||||
export default function configureAppStore(initialState) {
|
||||
|
|
@ -172,10 +138,11 @@ export default function configureAppStore(initialState) {
|
|||
additionalMiddleware: [
|
||||
createThunkMiddleware(),
|
||||
createSentryMiddleware(),
|
||||
...middlewares(persistConfig),
|
||||
...middlewares(),
|
||||
],
|
||||
enableThunk: false, // We override the default thunk middleware
|
||||
enhancers: [reduxReset(General.OFFLINE_STORE_PURGE)],
|
||||
};
|
||||
|
||||
return configureStore(initialState, appReducer, persistConfig, getAppReducer, clientOptions);
|
||||
return configureStore(initialState, appReducer, persistConfig, clientOptions);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,14 +45,14 @@ 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) {
|
||||
if (store.getState().views?.root?.hydrationComplete && !executed) {
|
||||
if (callback && typeof callback === 'function') {
|
||||
executed = true;
|
||||
callback();
|
||||
}
|
||||
} else {
|
||||
const subscription = () => {
|
||||
if (store.getState().views.root.hydrationComplete && !executed) {
|
||||
if (store.getState().views?.root?.hydrationComplete && !executed) {
|
||||
unsubscribeFromStore();
|
||||
if (callback && typeof callback === 'function') {
|
||||
executed = true;
|
||||
|
|
@ -73,6 +73,7 @@ export function getStateForReset(initialState, currentState) {
|
|||
|
||||
const resetState = merge(initialState, {
|
||||
entities: {
|
||||
general: currentState.entities.general,
|
||||
users: {
|
||||
currentUserId,
|
||||
profiles: {
|
||||
|
|
@ -84,6 +85,15 @@ export function getStateForReset(initialState, currentState) {
|
|||
},
|
||||
preferences,
|
||||
},
|
||||
errors: currentState.errors,
|
||||
views: {
|
||||
selectServer: {
|
||||
serverUrl: currentState.views?.selectServer?.serverUrl,
|
||||
},
|
||||
root: {
|
||||
hydrationComplete: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return resetState;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,11 @@ describe('getStateForReset', () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
views: {
|
||||
selectServer: {
|
||||
serverUrl: 'localhost:8065',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('should keep the current user\'s ID and profile', () => {
|
||||
|
|
|
|||
|
|
@ -80,13 +80,9 @@ class PushNotificationUtils {
|
|||
if (foreground) {
|
||||
EventEmitter.emit(ViewTypes.NOTIFICATION_IN_APP, notification);
|
||||
} else if (userInteraction && !notification?.data?.localNotification) {
|
||||
if (getState().views.root.hydrationComplete) { //TODO: Replace when realm is ready
|
||||
waitForHydration(this.store, () => {
|
||||
this.loadFromNotification(notification);
|
||||
} else {
|
||||
waitForHydration(this.store, () => {
|
||||
this.loadFromNotification(notification);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ module.exports = {
|
|||
assets: './dist/assets',
|
||||
'@actions': './app/actions',
|
||||
'@constants': './app/constants',
|
||||
'@i18n': './app/i18n',
|
||||
'@selectors': './app/selectors',
|
||||
'@telemetry': './app/telemetry',
|
||||
'@utils': './app/utils',
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ PODS:
|
|||
- libwebp/mux (1.1.0):
|
||||
- libwebp/demux
|
||||
- libwebp/webp (1.1.0)
|
||||
- MMKV (1.0.24)
|
||||
- Permission-Camera (2.0.10):
|
||||
- RNPermissions
|
||||
- Permission-PhotoLibrary (2.0.10):
|
||||
|
|
@ -212,6 +213,9 @@ PODS:
|
|||
- React
|
||||
- react-native-image-picker (2.3.1):
|
||||
- React
|
||||
- react-native-mmkv-storage (0.2.2):
|
||||
- MMKV (= 1.0.24)
|
||||
- React
|
||||
- react-native-netinfo (4.4.0):
|
||||
- React
|
||||
- react-native-notifications (2.0.6):
|
||||
|
|
@ -338,6 +342,7 @@ DEPENDENCIES:
|
|||
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
|
||||
- react-native-hw-keyboard-event (from `../node_modules/react-native-hw-keyboard-event`)
|
||||
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
|
||||
- react-native-mmkv-storage (from `../node_modules/react-native-mmkv-storage`)
|
||||
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
||||
- react-native-notifications (from `../node_modules/react-native-notifications`)
|
||||
- react-native-passcode-status (from `../node_modules/react-native-passcode-status`)
|
||||
|
|
@ -378,6 +383,7 @@ SPEC REPOS:
|
|||
https://github.com/cocoapods/specs.git:
|
||||
- boost-for-react-native
|
||||
- libwebp
|
||||
- MMKV
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- Sentry
|
||||
|
|
@ -434,6 +440,8 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native-hw-keyboard-event"
|
||||
react-native-image-picker:
|
||||
:path: "../node_modules/react-native-image-picker"
|
||||
react-native-mmkv-storage:
|
||||
:path: "../node_modules/react-native-mmkv-storage"
|
||||
react-native-netinfo:
|
||||
:path: "../node_modules/@react-native-community/netinfo"
|
||||
react-native-notifications:
|
||||
|
|
@ -509,6 +517,7 @@ SPEC CHECKSUMS:
|
|||
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
|
||||
jail-monkey: d7c5048b2336f22ee9c9e0efa145f1f917338ea9
|
||||
libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
|
||||
MMKV: 758b2edee46b08bdd958db4169191afb9a6d4ebd
|
||||
Permission-Camera: 8f0e5decca5f28f70f28a8dc31f012c9bad40ad8
|
||||
Permission-PhotoLibrary: b209bf23b784c9e1409a57d81c6d11ab1d3079c1
|
||||
RCTRequired: b153add4da6e7dbc44aebf93f3cf4fcae392ddf1
|
||||
|
|
@ -526,6 +535,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-netinfo: 892a5130be97ff8bb69c523739c424a2ffc296d1
|
||||
react-native-notifications: d5cb54ef8bf3004dcb56c887650dea08ecbddee7
|
||||
react-native-passcode-status: 88c4f6e074328bc278bd127646b6c694ad5a530a
|
||||
|
|
|
|||
342
package-lock.json
generated
342
package-lock.json
generated
|
|
@ -6896,16 +6896,6 @@
|
|||
"node-int64": "^0.4.0"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "5.4.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz",
|
||||
"integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base64-js": "^1.0.2",
|
||||
"ieee754": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"buffer-crc32": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
||||
|
|
@ -7158,12 +7148,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"clone": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz",
|
||||
"integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=",
|
||||
"dev": true
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
|
|
@ -7266,9 +7250,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -9801,25 +9785,25 @@
|
|||
"dependencies": {
|
||||
"abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||
"optional": true
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
|
||||
"optional": true
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -9829,13 +9813,13 @@
|
|||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -9845,37 +9829,37 @@
|
|||
},
|
||||
"chownr": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
|
||||
"optional": true
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
|
||||
"optional": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -9884,25 +9868,25 @@
|
|||
},
|
||||
"deep-extend": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
|
||||
"optional": true
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
|
||||
"optional": true
|
||||
},
|
||||
"detect-libc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
|
||||
"optional": true
|
||||
},
|
||||
"fs-minipass": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -9911,13 +9895,13 @@
|
|||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"optional": true
|
||||
},
|
||||
"gauge": {
|
||||
"version": "2.7.4",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -9933,7 +9917,7 @@
|
|||
},
|
||||
"glob": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -9947,13 +9931,13 @@
|
|||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
|
||||
"optional": true
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -9962,7 +9946,7 @@
|
|||
},
|
||||
"ignore-walk": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -9971,7 +9955,7 @@
|
|||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -9981,19 +9965,19 @@
|
|||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
|
||||
"optional": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10002,13 +9986,13 @@
|
|||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
|
||||
"optional": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10017,13 +10001,13 @@
|
|||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10033,7 +10017,7 @@
|
|||
},
|
||||
"minizlib": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10042,7 +10026,7 @@
|
|||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10051,13 +10035,13 @@
|
|||
},
|
||||
"ms": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
|
||||
"optional": true
|
||||
},
|
||||
"needle": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10068,7 +10052,7 @@
|
|||
},
|
||||
"node-pre-gyp": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10086,7 +10070,7 @@
|
|||
},
|
||||
"nopt": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10096,13 +10080,13 @@
|
|||
},
|
||||
"npm-bundled": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==",
|
||||
"optional": true
|
||||
},
|
||||
"npm-packlist": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10112,7 +10096,7 @@
|
|||
},
|
||||
"npmlog": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10124,19 +10108,19 @@
|
|||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
|
||||
"optional": true
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10145,19 +10129,19 @@
|
|||
},
|
||||
"os-homedir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
|
||||
"optional": true
|
||||
},
|
||||
"os-tmpdir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
|
||||
"optional": true
|
||||
},
|
||||
"osenv": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10167,19 +10151,19 @@
|
|||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"optional": true
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
|
||||
"optional": true
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10199,7 +10183,7 @@
|
|||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10214,7 +10198,7 @@
|
|||
},
|
||||
"rimraf": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10223,43 +10207,43 @@
|
|||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"optional": true
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
|
||||
"optional": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
|
||||
"optional": true
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
|
||||
"optional": true
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
|
||||
"optional": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10270,7 +10254,7 @@
|
|||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10279,7 +10263,7 @@
|
|||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10288,13 +10272,13 @@
|
|||
},
|
||||
"strip-json-comments": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
|
||||
"optional": true
|
||||
},
|
||||
"tar": {
|
||||
"version": "4.4.8",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10309,13 +10293,13 @@
|
|||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
||||
"optional": true
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -10324,13 +10308,13 @@
|
|||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
|
||||
"optional": true
|
||||
}
|
||||
|
|
@ -10435,12 +10419,6 @@
|
|||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
|
||||
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
|
||||
},
|
||||
"get-params": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/get-params/-/get-params-0.1.2.tgz",
|
||||
"integrity": "sha1-uuDfq6WIoMYNeDTA2Nwv9g7u8v4=",
|
||||
"dev": true
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
|
||||
|
|
@ -10931,12 +10909,6 @@
|
|||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
|
||||
"dev": true
|
||||
},
|
||||
"ignore": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
|
||||
|
|
@ -10973,9 +10945,9 @@
|
|||
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
|
||||
},
|
||||
"in-publish": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
|
||||
"integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E="
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz",
|
||||
"integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ=="
|
||||
},
|
||||
"indent-string": {
|
||||
"version": "4.0.0",
|
||||
|
|
@ -16858,12 +16830,6 @@
|
|||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"jsan": {
|
||||
"version": "3.1.13",
|
||||
"resolved": "https://registry.npmjs.org/jsan/-/jsan-3.1.13.tgz",
|
||||
"integrity": "sha512-9kGpCsGHifmw6oJet+y8HaCl14y7qgAsxVdV3pCHDySNR3BfDC30zgkssd7x5LRVAT22dnpbe9JdzzmXZnq9/g==",
|
||||
"dev": true
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
|
|
@ -16977,7 +16943,8 @@
|
|||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
|
||||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "2.1.1",
|
||||
|
|
@ -17106,12 +17073,6 @@
|
|||
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
|
||||
"dev": true
|
||||
},
|
||||
"linked-list": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/linked-list/-/linked-list-0.1.0.tgz",
|
||||
"integrity": "sha1-eYsP+X0bkqT9CEgPVa6k6dSdN78=",
|
||||
"dev": true
|
||||
},
|
||||
"load-json-file": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
|
||||
|
|
@ -17180,11 +17141,6 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||
},
|
||||
"lodash-es": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz",
|
||||
"integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ=="
|
||||
},
|
||||
"lodash._baseisequal": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz",
|
||||
|
|
@ -18357,12 +18313,6 @@
|
|||
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
|
||||
"optional": true
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.7.tgz",
|
||||
"integrity": "sha512-fmS3qwDldm4bE01HCIRqNk+f255CNjnAoeV3Zzzv0KemObHKqYgirVaZA9DtKcjogicWjYcHkJs4D5A8CjnuVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"nanomatch": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
|
||||
|
|
@ -19721,12 +19671,6 @@
|
|||
"strict-uri-encode": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"querystring": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
|
||||
"dev": true
|
||||
},
|
||||
"querystringify": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz",
|
||||
|
|
@ -20310,6 +20254,11 @@
|
|||
"resolved": "https://registry.npmjs.org/react-native-local-auth/-/react-native-local-auth-1.6.0.tgz",
|
||||
"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=="
|
||||
},
|
||||
"react-native-navigation": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-navigation/-/react-native-navigation-6.3.0.tgz",
|
||||
|
|
@ -20771,29 +20720,6 @@
|
|||
"resolved": "https://registry.npmjs.org/redux-batched-actions/-/redux-batched-actions-0.4.1.tgz",
|
||||
"integrity": "sha512-r6tLDyBP3U9cXNLEHs0n1mX5TQfmk6xE0Y9uinYZ5HOyAWDgIJxYqRRkU/bC6XrJ4nS7tasNbxaHJHVmf9UdkA=="
|
||||
},
|
||||
"redux-devtools-core": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/redux-devtools-core/-/redux-devtools-core-0.2.1.tgz",
|
||||
"integrity": "sha512-RAGOxtUFdr/1USAvxrWd+Gq/Euzgw7quCZlO5TgFpDfG7rB5tMhZUrNyBjpzgzL2yMk0eHnPYIGm7NkIfRzHxQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"get-params": "^0.1.2",
|
||||
"jsan": "^3.1.13",
|
||||
"lodash": "^4.17.11",
|
||||
"nanoid": "^2.0.0",
|
||||
"remotedev-serialize": "^0.1.8"
|
||||
}
|
||||
},
|
||||
"redux-devtools-instrument": {
|
||||
"version": "1.9.6",
|
||||
"resolved": "https://registry.npmjs.org/redux-devtools-instrument/-/redux-devtools-instrument-1.9.6.tgz",
|
||||
"integrity": "sha512-MwvY4cLEB2tIfWWBzrUR02UM9qRG2i7daNzywRvabOSVdvAY7s9BxSwMmVRH1Y/7QWjplNtOwgT0apKhHg2Qew==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash": "^4.2.0",
|
||||
"symbol-observable": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"redux-mock-store": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.5.4.tgz",
|
||||
|
|
@ -20803,23 +20729,10 @@
|
|||
"lodash.isplainobject": "^4.0.6"
|
||||
}
|
||||
},
|
||||
"redux-offline": {
|
||||
"version": "github:mattermost/redux-offline#885024de96b6ec73650c340c8928066585c413df",
|
||||
"from": "github:mattermost/redux-offline#885024de96b6ec73650c340c8928066585c413df",
|
||||
"requires": {
|
||||
"@react-native-community/netinfo": "^4.1.3",
|
||||
"redux-persist": "^4.5.0"
|
||||
}
|
||||
},
|
||||
"redux-persist": {
|
||||
"version": "4.10.2",
|
||||
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-4.10.2.tgz",
|
||||
"integrity": "sha512-U+e0ieMGC69Zr72929iJW40dEld7Mflh6mu0eJtVMLGfMq/aJqjxUM1hzyUWMR1VUyAEEdPHuQmeq5ti9krIgg==",
|
||||
"requires": {
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"lodash": "^4.17.4",
|
||||
"lodash-es": "^4.17.4"
|
||||
}
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz",
|
||||
"integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ=="
|
||||
},
|
||||
"redux-persist-node-storage": {
|
||||
"version": "2.0.0",
|
||||
|
|
@ -20844,6 +20757,11 @@
|
|||
"lodash.unset": "^4.5.2"
|
||||
}
|
||||
},
|
||||
"redux-reset": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/redux-reset/-/redux-reset-0.3.0.tgz",
|
||||
"integrity": "sha1-q4B5W0ENWyhomYbqxAy4R6ac0BI="
|
||||
},
|
||||
"redux-thunk": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
|
||||
|
|
@ -20999,29 +20917,6 @@
|
|||
"es6-error": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"remote-redux-devtools": {
|
||||
"version": "0.5.16",
|
||||
"resolved": "https://registry.npmjs.org/remote-redux-devtools/-/remote-redux-devtools-0.5.16.tgz",
|
||||
"integrity": "sha512-xZ2D1VRIWzat5nsvcraT6fKEX9Cfi+HbQBCwzNnUAM8Uicm/anOc60XGalcaDPrVmLug7nhDl2nimEa3bL3K9w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"jsan": "^3.1.13",
|
||||
"querystring": "^0.2.0",
|
||||
"redux-devtools-core": "^0.2.1",
|
||||
"redux-devtools-instrument": "^1.9.4",
|
||||
"rn-host-detect": "^1.1.5",
|
||||
"socketcluster-client": "^14.2.1"
|
||||
}
|
||||
},
|
||||
"remotedev-serialize": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/remotedev-serialize/-/remotedev-serialize-0.1.8.tgz",
|
||||
"integrity": "sha512-3YG/FDcOmiK22bl5oMRM8RRnbGrFEuPGjbcDG+z2xi5aQaNQNZ8lqoRnZTwXVfaZtutXuiAQOgPRrogzQk8edg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"jsan": "^3.1.13"
|
||||
}
|
||||
},
|
||||
"remove-trailing-separator": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
||||
|
|
@ -21236,12 +21131,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"rn-host-detect": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/rn-host-detect/-/rn-host-detect-1.2.0.tgz",
|
||||
"integrity": "sha512-btNg5kzHcjZZ7t7mvvV/4wNJ9e3MPgrWivkRgWURzXL0JJ0pwWlU4zrbmdlz3HHzHOxhBhHB4D+/dbMFfu4/4A==",
|
||||
"dev": true
|
||||
},
|
||||
"rn-placeholder": {
|
||||
"version": "github:mattermost/rn-placeholder#02c629c65d0123a2eee623ada0fd17186415d3c3",
|
||||
"from": "github:mattermost/rn-placeholder#02c629c65d0123a2eee623ada0fd17186415d3c3",
|
||||
|
|
@ -21359,27 +21248,6 @@
|
|||
"xmlchars": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"sc-channel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/sc-channel/-/sc-channel-1.2.0.tgz",
|
||||
"integrity": "sha512-M3gdq8PlKg0zWJSisWqAsMmTVxYRTpVRqw4CWAdKBgAfVKumFcTjoCV0hYu7lgUXccCtCD8Wk9VkkE+IXCxmZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"component-emitter": "1.2.1"
|
||||
}
|
||||
},
|
||||
"sc-errors": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sc-errors/-/sc-errors-2.0.1.tgz",
|
||||
"integrity": "sha512-JoVhq3Ud+3Ujv2SIG7W0XtjRHsrNgl6iXuHHsh0s+Kdt5NwI6N2EGAZD4iteitdDv68ENBkpjtSvN597/wxPSQ==",
|
||||
"dev": true
|
||||
},
|
||||
"sc-formatter": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/sc-formatter/-/sc-formatter-3.0.2.tgz",
|
||||
"integrity": "sha512-9PbqYBpCq+OoEeRQ3QfFIGE6qwjjBcd2j7UjgDlhnZbtSnuGgHdcRklPKYGuYFH82V/dwd+AIpu8XvA1zqTd+A==",
|
||||
"dev": true
|
||||
},
|
||||
"scheduler": {
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz",
|
||||
|
|
@ -21793,32 +21661,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"socketcluster-client": {
|
||||
"version": "14.3.1",
|
||||
"resolved": "https://registry.npmjs.org/socketcluster-client/-/socketcluster-client-14.3.1.tgz",
|
||||
"integrity": "sha512-Sd/T0K/9UlqTfz+HUuFq90dshA5OBJPQbdkRzGtcKIOm52fkdsBTt0FYpiuzzxv5VrU7PWpRm6KIfNXyPwlLpw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer": "^5.2.1",
|
||||
"clone": "2.1.1",
|
||||
"component-emitter": "1.2.1",
|
||||
"linked-list": "0.1.0",
|
||||
"querystring": "0.2.0",
|
||||
"sc-channel": "^1.2.0",
|
||||
"sc-errors": "^2.0.1",
|
||||
"sc-formatter": "^3.0.1",
|
||||
"uuid": "3.2.1",
|
||||
"ws": "7.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
|
||||
"integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort-json": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-json/-/sort-json-2.0.0.tgz",
|
||||
|
|
@ -23015,9 +22857,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"webpack-cli": {
|
||||
"version": "3.3.10",
|
||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.10.tgz",
|
||||
"integrity": "sha512-u1dgND9+MXaEt74sJR4PR7qkPxXUSQ0RXYq8x1L6Jg1MYVEmGPrH6Ah6C4arD4r0J1P5HKjRqpab36k0eIzPqg==",
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.11.tgz",
|
||||
"integrity": "sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "2.4.2",
|
||||
|
|
@ -23119,9 +22961,9 @@
|
|||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "13.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
|
||||
"integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
|
||||
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
"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-navigation": "6.3.0",
|
||||
"react-native-notifications": "2.0.6",
|
||||
"react-native-passcode-status": "1.1.2",
|
||||
|
|
@ -71,9 +72,9 @@
|
|||
"redux": "4.0.5",
|
||||
"redux-action-buffer": "1.2.0",
|
||||
"redux-batched-actions": "0.4.1",
|
||||
"redux-offline": "github:mattermost/redux-offline#885024de96b6ec73650c340c8928066585c413df",
|
||||
"redux-persist": "4.10.2",
|
||||
"redux-persist": "6.0.0",
|
||||
"redux-persist-transform-filter": "0.0.20",
|
||||
"redux-reset": "0.3.0",
|
||||
"redux-thunk": "2.3.0",
|
||||
"reselect": "4.0.0",
|
||||
"rn-fetch-blob": "0.12.0",
|
||||
|
|
@ -133,7 +134,6 @@
|
|||
"react-test-renderer": "16.13.0",
|
||||
"redux-mock-store": "1.5.4",
|
||||
"redux-persist-node-storage": "2.0.0",
|
||||
"remote-redux-devtools": "0.5.16",
|
||||
"socketcluster": "16.0.1",
|
||||
"ts-jest": "25.2.1",
|
||||
"typescript": "3.8.3",
|
||||
|
|
|
|||
|
|
@ -151,12 +151,10 @@ 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/configureStore.prod.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/store/reducer_registry.ts',
|
||||
'app/mm-redux/types/actions.ts',
|
||||
'app/mm-redux/utils/channel_utils.ts',
|
||||
'app/mm-redux/utils/emoji_utils.ts',
|
||||
|
|
@ -837,19 +835,6 @@ module.exports = [
|
|||
'node_modules/react/index.js',
|
||||
'node_modules/redux-action-buffer/index.js',
|
||||
'node_modules/redux-batched-actions/lib/index.js',
|
||||
'node_modules/redux-offline/lib/actions.js',
|
||||
'node_modules/redux-offline/lib/config.js',
|
||||
'node_modules/redux-offline/lib/constants.js',
|
||||
'node_modules/redux-offline/lib/defaults/batch.js',
|
||||
'node_modules/redux-offline/lib/defaults/detectNetwork.native.js',
|
||||
'node_modules/redux-offline/lib/defaults/discard.js',
|
||||
'node_modules/redux-offline/lib/defaults/effect.js',
|
||||
'node_modules/redux-offline/lib/defaults/index.js',
|
||||
'node_modules/redux-offline/lib/defaults/persist.native.js',
|
||||
'node_modules/redux-offline/lib/defaults/retry.js',
|
||||
'node_modules/redux-offline/lib/index.js',
|
||||
'node_modules/redux-offline/lib/middleware.js',
|
||||
'node_modules/redux-offline/lib/updater.js',
|
||||
'node_modules/redux-persist-transform-filter/dist/index.js',
|
||||
'node_modules/redux-persist/lib/autoRehydrate.js',
|
||||
'node_modules/redux-persist/lib/constants.js',
|
||||
|
|
|
|||
|
|
@ -150,12 +150,10 @@ 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/configureStore.prod.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/store/reducer_registry.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',
|
||||
|
|
@ -831,19 +829,6 @@ module.exports = [
|
|||
'./node_modules/node_modules/react/index.js',
|
||||
'./node_modules/node_modules/redux-action-buffer/index.js',
|
||||
'./node_modules/node_modules/redux-batched-actions/lib/index.js',
|
||||
'./node_modules/node_modules/redux-offline/lib/actions.js',
|
||||
'./node_modules/node_modules/redux-offline/lib/config.js',
|
||||
'./node_modules/node_modules/redux-offline/lib/constants.js',
|
||||
'./node_modules/node_modules/redux-offline/lib/defaults/batch.js',
|
||||
'./node_modules/node_modules/redux-offline/lib/defaults/detectNetwork.native.js',
|
||||
'./node_modules/node_modules/redux-offline/lib/defaults/discard.js',
|
||||
'./node_modules/node_modules/redux-offline/lib/defaults/effect.js',
|
||||
'./node_modules/node_modules/redux-offline/lib/defaults/index.js',
|
||||
'./node_modules/node_modules/redux-offline/lib/defaults/persist.native.js',
|
||||
'./node_modules/node_modules/redux-offline/lib/defaults/retry.js',
|
||||
'./node_modules/node_modules/redux-offline/lib/index.js',
|
||||
'./node_modules/node_modules/redux-offline/lib/middleware.js',
|
||||
'./node_modules/node_modules/redux-offline/lib/updater.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',
|
||||
|
|
|
|||
|
|
@ -87,6 +87,10 @@ jest.doMock('react-native', () => {
|
|||
pick: jest.fn(),
|
||||
},
|
||||
RNPermissions: {},
|
||||
RNFastStorage: {
|
||||
setupLibrary: jest.fn(),
|
||||
setStringAsync: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
return Object.setPrototypeOf({
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {AsyncNodeStorage} from 'redux-persist-node-storage';
|
||||
import {createTransform, persistStore} from 'redux-persist';
|
||||
import {createTransform} from 'redux-persist';
|
||||
|
||||
import configureStore from '@mm-redux/store';
|
||||
|
||||
|
|
@ -11,29 +11,16 @@ export default async function testConfigureStore(preloadedState) {
|
|||
() => ({}),
|
||||
);
|
||||
|
||||
const offlineConfig = {
|
||||
detectNetwork: (callback) => callback(true),
|
||||
persist: (store, options) => {
|
||||
return persistStore(store, {storage: new AsyncNodeStorage('./.tmp'), ...options});
|
||||
},
|
||||
persistOptions: {
|
||||
debounce: 1000,
|
||||
transforms: [
|
||||
storageTransform,
|
||||
],
|
||||
whitelist: [],
|
||||
},
|
||||
retry: (action, retries) => 200 * (retries + 1),
|
||||
discard: (error, action, retries) => {
|
||||
if (action.meta && action.meta.offline.hasOwnProperty('maxRetry')) {
|
||||
return retries >= action.meta.offline.maxRetry;
|
||||
}
|
||||
|
||||
return retries >= 1;
|
||||
},
|
||||
const persistConfig = {
|
||||
key: 'root',
|
||||
storage: new AsyncNodeStorage('./.tmp'),
|
||||
whitelist: [],
|
||||
transforms: [
|
||||
storageTransform,
|
||||
],
|
||||
};
|
||||
|
||||
const store = configureStore(preloadedState, {}, offlineConfig, () => ({}), {enableBuffer: false});
|
||||
const {store} = configureStore(preloadedState, {}, persistConfig, () => ({}), {enableBuffer: false});
|
||||
|
||||
const wait = () => new Promise((resolve) => setTimeout(resolve), 300); //eslint-disable-line
|
||||
await wait();
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
"@actions/*": ["app/actions/*"],
|
||||
"@constants/*": ["app/constants/*"],
|
||||
"@constants": ["app/constants/index"],
|
||||
"@i18n": ["app/i18n/index"],
|
||||
"@selectors/*": ["app/selectors/*"],
|
||||
"@telemetry/*": ["/app/telemetry/*"],
|
||||
"@utils/*": ["app/utils/*"],
|
||||
|
|
|
|||
Loading…
Reference in a new issue