MM-13896 Sync channel as read between clients (including opening Push Notifications) (#2548)
* Update mattermost-redux to use latest WS changes * Properly mark channel as read, keep the new message indicator, and fix push notification flow * unneeded parameter in setCurrentUserStatusOffline Co-Authored-By: enahum <nahumhbl@gmail.com> * Feedback review * Update mm-redux ref
This commit is contained in:
parent
5bb6cd34da
commit
a0a1fa28ce
28 changed files with 115 additions and 196 deletions
|
|
@ -10,8 +10,8 @@ import {
|
|||
fetchMyChannelsAndMembers,
|
||||
getChannelByNameAndTeamName,
|
||||
markChannelAsRead,
|
||||
leaveChannel as serviceLeaveChannel, markChannelAsViewed,
|
||||
selectChannel,
|
||||
leaveChannel as serviceLeaveChannel,
|
||||
} from 'mattermost-redux/actions/channels';
|
||||
import {getPosts, getPostsBefore, getPostsSince, getPostThread} from 'mattermost-redux/actions/posts';
|
||||
import {getFilesForPost} from 'mattermost-redux/actions/files';
|
||||
|
|
@ -19,8 +19,8 @@ import {savePreferences} from 'mattermost-redux/actions/preferences';
|
|||
import {getTeamMembersByIds} from 'mattermost-redux/actions/teams';
|
||||
import {getProfilesInChannel} from 'mattermost-redux/actions/users';
|
||||
import {General, Preferences} from 'mattermost-redux/constants';
|
||||
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getTeamByName} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getCurrentChannelId, getMyChannelMember} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentTeamId, getTeamByName} from 'mattermost-redux/selectors/entities/teams';
|
||||
|
||||
import {
|
||||
getChannelByName,
|
||||
|
|
@ -283,8 +283,7 @@ export function selectInitialChannel(teamId) {
|
|||
lastChannel &&
|
||||
(lastChannel.team_id === teamId || isDMVisible || isGMVisible)
|
||||
) {
|
||||
handleSelectChannel(lastChannelId)(dispatch, getState);
|
||||
markChannelAsRead(lastChannelId)(dispatch, getState);
|
||||
dispatch(handleSelectChannel(lastChannelId));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -317,7 +316,6 @@ export function selectPenultimateChannel(teamId) {
|
|||
dispatch(setChannelLoading(true));
|
||||
dispatch(setChannelDisplayName(lastChannel.display_name));
|
||||
dispatch(handleSelectChannel(lastChannelId));
|
||||
dispatch(markChannelAsRead(lastChannelId));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -327,7 +325,7 @@ export function selectPenultimateChannel(teamId) {
|
|||
|
||||
export function selectDefaultChannel(teamId) {
|
||||
return (dispatch, getState) => {
|
||||
const channels = getState().entities.channels.channels;
|
||||
const {channels} = getState().entities.channels;
|
||||
|
||||
const channel = Object.values(channels).find((c) => c.team_id === teamId && c.name === General.DEFAULT_CHANNEL);
|
||||
let channelId;
|
||||
|
|
@ -345,21 +343,22 @@ export function selectDefaultChannel(teamId) {
|
|||
if (channelId) {
|
||||
dispatch(setChannelDisplayName(''));
|
||||
dispatch(handleSelectChannel(channelId));
|
||||
dispatch(markChannelAsRead(channelId));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function handleSelectChannel(channelId) {
|
||||
return async (dispatch, getState) => {
|
||||
const {currentTeamId} = getState().entities.teams;
|
||||
const state = getState();
|
||||
const currentTeamId = getCurrentTeamId(state);
|
||||
const currentChannelId = getCurrentChannelId(state);
|
||||
const sameChannel = channelId === currentChannelId;
|
||||
const member = getMyChannelMember(state, channelId);
|
||||
|
||||
dispatch(setLoadMorePostsVisible(true));
|
||||
|
||||
loadPostsIfNecessaryWithRetry(channelId)(dispatch, getState);
|
||||
selectChannel(channelId)(dispatch, getState);
|
||||
|
||||
dispatch(loadPostsIfNecessaryWithRetry(channelId));
|
||||
dispatch(batchActions([
|
||||
selectChannel(channelId),
|
||||
{
|
||||
type: ViewTypes.SET_INITIAL_POST_VISIBILITY,
|
||||
data: channelId,
|
||||
|
|
@ -370,7 +369,15 @@ export function handleSelectChannel(channelId) {
|
|||
teamId: currentTeamId,
|
||||
channelId,
|
||||
},
|
||||
{
|
||||
type: ViewTypes.SELECT_CHANNEL_WITH_MEMBER,
|
||||
data: channelId,
|
||||
member,
|
||||
},
|
||||
]));
|
||||
|
||||
dispatch(markChannelAsRead(channelId, sameChannel ? null : currentChannelId));
|
||||
dispatch(markChannelAsViewed(channelId, sameChannel ? null : currentChannelId));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -426,7 +433,7 @@ export function toggleDMChannel(otherUserId, visible, channelId) {
|
|||
value: Date.now().toString(),
|
||||
}];
|
||||
|
||||
savePreferences(currentUserId, dm)(dispatch, getState);
|
||||
dispatch(savePreferences(currentUserId, dm));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -442,7 +449,7 @@ export function toggleGMChannel(channelId, visible) {
|
|||
value: visible,
|
||||
}];
|
||||
|
||||
savePreferences(currentUserId, gm)(dispatch, getState);
|
||||
dispatch(savePreferences(currentUserId, gm));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -451,9 +458,9 @@ export function closeDMChannel(channel) {
|
|||
const state = getState();
|
||||
const currentChannelId = getCurrentChannelId(state);
|
||||
|
||||
toggleDMChannel(channel.teammate_id, 'false')(dispatch, getState);
|
||||
dispatch(toggleDMChannel(channel.teammate_id, 'false'));
|
||||
if (channel.id === currentChannelId) {
|
||||
selectInitialChannel(state.entities.teams.currentTeamId)(dispatch, getState);
|
||||
dispatch(selectInitialChannel(state.entities.teams.currentTeamId));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -497,7 +504,7 @@ export function leaveChannel(channel, reset = false) {
|
|||
await dispatch(selectDefaultChannel(currentTeamId));
|
||||
}
|
||||
|
||||
await serviceLeaveChannel(channel.id)(dispatch, getState);
|
||||
await dispatch(serviceLeaveChannel(channel.id));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@ export function handleCreateChannel(displayName, purpose, header, type) {
|
|||
type,
|
||||
};
|
||||
|
||||
const {data} = await createChannel(channel, currentUserId)(dispatch, getState);
|
||||
const {data} = await dispatch(createChannel(channel, currentUserId));
|
||||
if (data && data.id) {
|
||||
dispatch(setChannelDisplayName(displayName));
|
||||
handleSelectChannel(data.id)(dispatch, getState);
|
||||
dispatch(handleSelectChannel(data.id));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import {GeneralTypes, PostTypes} from 'mattermost-redux/action_types';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import {fetchMyChannelsAndMembers, markChannelAsRead} from 'mattermost-redux/actions/channels';
|
||||
import {fetchMyChannelsAndMembers, markChannelAsRead, markChannelAsViewed} from 'mattermost-redux/actions/channels';
|
||||
import {getClientConfig, getDataRetentionPolicy, getLicenseConfig} from 'mattermost-redux/actions/general';
|
||||
import {getMyTeams, getMyTeamMembers, selectTeam} from 'mattermost-redux/actions/teams';
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ export function loadConfigAndLicense() {
|
|||
};
|
||||
}
|
||||
|
||||
export function loadFromPushNotification(notification) {
|
||||
export function loadFromPushNotification(notification, startAppFromPushNotification) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const {data} = notification;
|
||||
|
|
@ -86,12 +86,11 @@ export function loadFromPushNotification(notification) {
|
|||
dispatch(selectTeam({id: teamId}));
|
||||
}
|
||||
|
||||
// mark channel as read
|
||||
dispatch(markChannelAsRead(channelId, channelId === currentChannelId ? null : currentChannelId, false));
|
||||
|
||||
if (channelId !== currentChannelId) {
|
||||
if (channelId === currentChannelId && !startAppFromPushNotification) {
|
||||
dispatch(markChannelAsRead(channelId, null, true));
|
||||
dispatch(markChannelAsViewed(channelId));
|
||||
} else if (channelId !== currentChannelId) {
|
||||
// when the notification is from a channel other than the current channel
|
||||
dispatch(markChannelAsRead(channelId, currentChannelId, false));
|
||||
dispatch(setChannelDisplayName(''));
|
||||
dispatch(handleSelectChannel(channelId));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,16 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
|
||||
import {ChannelTypes, TeamTypes} from 'mattermost-redux/action_types';
|
||||
import {markChannelAsRead, markChannelAsViewed} from 'mattermost-redux/actions/channels';
|
||||
import {TeamTypes} from 'mattermost-redux/action_types';
|
||||
import {getMyTeams} from 'mattermost-redux/actions/teams';
|
||||
import {RequestStatus} from 'mattermost-redux/constants';
|
||||
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import {NavigationTypes} from 'app/constants';
|
||||
import {selectFirstAvailableTeam} from 'app/utils/teams';
|
||||
|
||||
import {setChannelDisplayName} from './channel';
|
||||
|
||||
export function handleTeamChange(teamId, selectChannel = true) {
|
||||
export function handleTeamChange(teamId) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const {currentTeamId} = state.entities.teams;
|
||||
|
|
@ -24,19 +18,7 @@ export function handleTeamChange(teamId, selectChannel = true) {
|
|||
return;
|
||||
}
|
||||
|
||||
const actions = [setChannelDisplayName(''), {type: TeamTypes.SELECT_TEAM, data: teamId}];
|
||||
|
||||
if (selectChannel) {
|
||||
actions.push({type: ChannelTypes.SELECT_CHANNEL, data: ''});
|
||||
|
||||
const lastChannels = state.views.team.lastChannelForTeam[teamId] || [];
|
||||
const lastChannelId = lastChannels[0] || '';
|
||||
const currentChannelId = getCurrentChannelId(state);
|
||||
markChannelAsViewed(currentChannelId)(dispatch, getState);
|
||||
markChannelAsRead(lastChannelId, currentChannelId)(dispatch, getState);
|
||||
}
|
||||
|
||||
dispatch(batchActions(actions, 'BATCH_SELECT_TEAM'), getState);
|
||||
dispatch({type: TeamTypes.SELECT_TEAM, data: teamId});
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,18 +2,13 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {UserTypes} from 'mattermost-redux/action_types';
|
||||
import {getStatus, getStatusesByIds, startPeriodicStatusUpdates} from 'mattermost-redux/actions/users';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
||||
|
||||
export function setCurrentUserStatus(isOnline) {
|
||||
export function setCurrentUserStatusOffline() {
|
||||
return (dispatch, getState) => {
|
||||
const currentUserId = getCurrentUserId(getState());
|
||||
|
||||
if (isOnline) {
|
||||
return dispatch(getStatus(currentUserId));
|
||||
}
|
||||
|
||||
return dispatch({
|
||||
type: UserTypes.RECEIVED_STATUS,
|
||||
data: {
|
||||
|
|
@ -23,16 +18,3 @@ export function setCurrentUserStatus(isOnline) {
|
|||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function initUserStatuses() {
|
||||
return (dispatch, getState) => {
|
||||
const {statuses} = getState().entities.users || {};
|
||||
const userIds = Object.keys(statuses);
|
||||
|
||||
if (userIds.length) {
|
||||
dispatch(getStatusesByIds(userIds));
|
||||
}
|
||||
|
||||
dispatch(startPeriodicStatusUpdates());
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import thunk from 'redux-thunk';
|
|||
import {UserTypes} from 'mattermost-redux/action_types';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
||||
import {initUserStatuses, setCurrentUserStatus} from 'app/actions/views/user';
|
||||
import {setCurrentUserStatusOffline} from 'app/actions/views/user';
|
||||
|
||||
const mockStore = configureStore([thunk]);
|
||||
|
||||
|
|
@ -44,28 +44,7 @@ describe('Actions.Views.User', () => {
|
|||
},
|
||||
};
|
||||
|
||||
await store.dispatch(setCurrentUserStatus(false));
|
||||
await store.dispatch(setCurrentUserStatusOffline());
|
||||
expect(store.getActions()).toEqual([action]);
|
||||
});
|
||||
|
||||
test('should fetch the current user status from the server', async () => {
|
||||
const action = {
|
||||
type: 'MOCK_GET_STATUS',
|
||||
args: ['current-user-id'],
|
||||
};
|
||||
|
||||
await store.dispatch(setCurrentUserStatus(true));
|
||||
expect(store.getActions()).toEqual([action]);
|
||||
});
|
||||
|
||||
test('should initialize the periodic status updates and get the current user statuses', () => {
|
||||
const actionStatusByIds = {
|
||||
type: 'MOCK_GET_STATUS_BY_IDS',
|
||||
args: [['current-user-id', 'another-user-id1', 'another-user-id2']],
|
||||
};
|
||||
const actionPeriodicUpdates = {type: 'MOCK_PERIODIC_STATUS_UPDATES'};
|
||||
|
||||
store.dispatch(initUserStatuses());
|
||||
expect(store.getActions()).toEqual([actionStatusByIds, actionPeriodicUpdates]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -314,7 +314,6 @@ export default class App {
|
|||
break;
|
||||
}
|
||||
|
||||
this.setStartAppFromPushNotification(false);
|
||||
this.setAppStarted(true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ export default class ChannelLoader extends PureComponent {
|
|||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
handleSelectChannel: PropTypes.func.isRequired,
|
||||
markChannelAsViewed: PropTypes.func.isRequired,
|
||||
markChannelAsRead: PropTypes.func.isRequired,
|
||||
setChannelLoading: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
backgroundColor: PropTypes.string,
|
||||
|
|
@ -42,7 +40,6 @@ export default class ChannelLoader extends PureComponent {
|
|||
return {
|
||||
switch: false,
|
||||
channel: null,
|
||||
currentChannelId: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -61,22 +58,13 @@ export default class ChannelLoader extends PureComponent {
|
|||
if (this.state.switch) {
|
||||
const {
|
||||
handleSelectChannel,
|
||||
markChannelAsRead,
|
||||
markChannelAsViewed,
|
||||
setChannelLoading,
|
||||
} = this.props.actions;
|
||||
|
||||
const {channel, currentChannelId} = this.state;
|
||||
const {channel} = this.state;
|
||||
|
||||
setTimeout(() => {
|
||||
handleSelectChannel(channel.id);
|
||||
|
||||
// mark the channel as viewed after all the frame has flushed
|
||||
markChannelAsRead(channel.id, currentChannelId);
|
||||
if (channel.id !== currentChannelId) {
|
||||
markChannelAsViewed(currentChannelId);
|
||||
}
|
||||
|
||||
setChannelLoading(false);
|
||||
}, 250);
|
||||
}
|
||||
|
|
@ -106,7 +94,7 @@ export default class ChannelLoader extends PureComponent {
|
|||
if (channel.id === currentChannelId) {
|
||||
this.props.actions.setChannelLoading(false);
|
||||
} else {
|
||||
this.setState({switch: true, channel, currentChannelId});
|
||||
this.setState({switch: true, channel});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {markChannelAsRead, markChannelAsViewed} from 'mattermost-redux/actions/channels';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {handleSelectChannel, setChannelLoading} from 'app/actions/views/channel';
|
||||
|
|
@ -22,9 +21,7 @@ function mapDispatchToProps(dispatch) {
|
|||
return {
|
||||
actions: bindActionCreators({
|
||||
handleSelectChannel,
|
||||
markChannelAsRead,
|
||||
setChannelLoading,
|
||||
markChannelAsViewed,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,12 @@
|
|||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {stopPeriodicStatusUpdates, logout} from 'mattermost-redux/actions/users';
|
||||
import {stopPeriodicStatusUpdates, startPeriodicStatusUpdates, logout} from 'mattermost-redux/actions/users';
|
||||
import {init as initWebSocket, close as closeWebSocket} from 'mattermost-redux/actions/websocket';
|
||||
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
|
||||
|
||||
import {connection} from 'app/actions/device';
|
||||
import {
|
||||
initUserStatuses as startPeriodicStatusUpdates,
|
||||
setCurrentUserStatus,
|
||||
} from 'app/actions/views/user';
|
||||
import {setCurrentUserStatusOffline} from 'app/actions/views/user';
|
||||
import {getConnection, isLandscape} from 'app/selectors/device';
|
||||
|
||||
import NetworkIndicator from './network_indicator';
|
||||
|
|
@ -37,7 +34,7 @@ function mapDispatchToProps(dispatch) {
|
|||
connection,
|
||||
initWebSocket,
|
||||
logout,
|
||||
setCurrentUserStatus,
|
||||
setCurrentUserStatusOffline,
|
||||
startPeriodicStatusUpdates,
|
||||
stopPeriodicStatusUpdates,
|
||||
}, dispatch),
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export default class NetworkIndicator extends PureComponent {
|
|||
connection: PropTypes.func.isRequired,
|
||||
initWebSocket: PropTypes.func.isRequired,
|
||||
logout: PropTypes.func.isRequired,
|
||||
setCurrentUserStatus: PropTypes.func.isRequired,
|
||||
setCurrentUserStatusOffline: PropTypes.func.isRequired,
|
||||
startPeriodicStatusUpdates: PropTypes.func.isRequired,
|
||||
stopPeriodicStatusUpdates: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
|
|
@ -165,7 +165,6 @@ export default class NetworkIndicator extends PureComponent {
|
|||
};
|
||||
|
||||
connected = () => {
|
||||
this.props.actions.setCurrentUserStatus(true);
|
||||
Animated.sequence([
|
||||
Animated.timing(
|
||||
this.backgroundColor, {
|
||||
|
|
@ -337,7 +336,7 @@ export default class NetworkIndicator extends PureComponent {
|
|||
duration: 300,
|
||||
}
|
||||
).start(() => {
|
||||
this.props.actions.setCurrentUserStatus(false);
|
||||
this.props.actions.setCurrentUserStatusOffline();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ const ViewTypes = keyMirror({
|
|||
|
||||
SELECTED_ACTION_MENU: null,
|
||||
SUBMIT_ATTACHMENT_MENU_ACTION: null,
|
||||
SELECT_CHANNEL_WITH_MEMBER: null,
|
||||
});
|
||||
|
||||
export default {
|
||||
|
|
|
|||
|
|
@ -24,12 +24,15 @@ import {setAppState, setServerVersion} from 'mattermost-redux/actions/general';
|
|||
import {loadMe, logout} from 'mattermost-redux/actions/users';
|
||||
import {close as closeWebSocket} from 'mattermost-redux/actions/websocket';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
||||
import {handleLoginIdChanged} from 'app/actions/views/login';
|
||||
import {handleServerUrlChanged} from 'app/actions/views/select_server';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {selectDefaultChannel} from 'app/actions/views/channel';
|
||||
import {setDeviceDimensions, setDeviceOrientation, setDeviceAsTablet, setStatusBarHeight} from 'app/actions/device';
|
||||
import {handleLoginIdChanged} from 'app/actions/views/login';
|
||||
import {handleServerUrlChanged} from 'app/actions/views/select_server';
|
||||
import {loadConfigAndLicense, startDataCleanup} from 'app/actions/views/root';
|
||||
|
||||
import initialState from 'app/initial_state';
|
||||
import configureStore from 'app/store';
|
||||
import {NavigationTypes} from 'app/constants';
|
||||
|
|
@ -38,14 +41,6 @@ import mattermostManaged from 'app/mattermost_managed';
|
|||
import {configurePushNotifications} from 'app/utils/push_notifications';
|
||||
import PushNotifications from 'app/push_notifications';
|
||||
import {registerScreens} from 'app/screens';
|
||||
import {
|
||||
setDeviceDimensions,
|
||||
setDeviceOrientation,
|
||||
setDeviceAsTablet,
|
||||
setStatusBarHeight,
|
||||
} from 'app/actions/device';
|
||||
import {loadConfigAndLicense, startDataCleanup} from 'app/actions/views/root';
|
||||
import {setChannelDisplayName} from 'app/actions/views/channel';
|
||||
import {deleteFileCache} from 'app/utils/file';
|
||||
import avoidNativeBridge from 'app/utils/avoid_native_bridge';
|
||||
import {t} from 'app/utils/i18n';
|
||||
|
|
@ -96,7 +91,7 @@ const initializeModules = () => {
|
|||
EventEmitter.on(NavigationTypes.RESTART_APP, restartApp);
|
||||
EventEmitter.on(General.SERVER_VERSION_CHANGED, handleServerVersionChanged);
|
||||
EventEmitter.on(General.CONFIG_CHANGED, handleConfigChanged);
|
||||
EventEmitter.on(General.DEFAULT_CHANNEL, handleResetChannelDisplayName);
|
||||
EventEmitter.on(General.SWITCH_TO_DEFAULT_CHANNEL, handleSwithToDefaultChannel);
|
||||
Dimensions.addEventListener('change', handleOrientationChange);
|
||||
mattermostManaged.addEventListener('managedConfigDidChange', () => {
|
||||
handleManagedConfig(true);
|
||||
|
|
@ -342,8 +337,8 @@ const handleAuthentication = async (vendor) => {
|
|||
return true;
|
||||
};
|
||||
|
||||
const handleResetChannelDisplayName = (displayName) => {
|
||||
store.dispatch(setChannelDisplayName(displayName));
|
||||
const handleSwithToDefaultChannel = (teamId) => {
|
||||
store.dispatch(selectDefaultChannel(teamId));
|
||||
};
|
||||
|
||||
const launchSelectServer = () => {
|
||||
|
|
|
|||
|
|
@ -332,6 +332,22 @@ function loadMorePostsVisible(state = true, action) {
|
|||
}
|
||||
}
|
||||
|
||||
function lastChannelViewTime(state = {}, action) {
|
||||
switch (action.type) {
|
||||
case ViewTypes.SELECT_CHANNEL_WITH_MEMBER: {
|
||||
if (action.member) {
|
||||
const nextState = {...state};
|
||||
nextState[action.data] = action.member.last_viewed_at;
|
||||
return nextState;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default combineReducers({
|
||||
displayName,
|
||||
drafts,
|
||||
|
|
@ -343,4 +359,5 @@ export default combineReducers({
|
|||
lastGetPosts,
|
||||
retryFailed,
|
||||
loadMorePostsVisible,
|
||||
lastChannelViewTime,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import {
|
|||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import {app} from 'app/mattermost';
|
||||
|
||||
import InteractiveDialogController from 'app/components/interactive_dialog_controller';
|
||||
import EmptyToolbar from 'app/components/start/empty_toolbar';
|
||||
import ChannelLoader from 'app/components/channel_loader';
|
||||
|
|
@ -247,9 +249,12 @@ export default class Channel extends PureComponent {
|
|||
|
||||
loadChannelsIfNecessary(teamId).then(() => {
|
||||
loadProfilesAndTeamMembersForDMSidebar(teamId);
|
||||
selectInitialChannel(teamId);
|
||||
}).catch(() => {
|
||||
selectInitialChannel(teamId);
|
||||
|
||||
if (app.startAppFromPushNotification) {
|
||||
app.setStartAppFromPushNotification(false);
|
||||
} else {
|
||||
selectInitialChannel(teamId);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {connect} from 'react-redux';
|
|||
|
||||
import {selectPost} from 'mattermost-redux/actions/posts';
|
||||
import {getPostIdsInCurrentChannel} from 'mattermost-redux/selectors/entities/posts';
|
||||
import {getCurrentChannelId, getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
|
|
@ -18,7 +18,6 @@ import ChannelPostList from './channel_post_list';
|
|||
function mapStateToProps(state) {
|
||||
const channelId = getCurrentChannelId(state);
|
||||
const channelRefreshingFailed = state.views.channel.retryFailed;
|
||||
const currentChannelMember = getMyCurrentChannelMembership(state);
|
||||
|
||||
return {
|
||||
channelId,
|
||||
|
|
@ -27,7 +26,7 @@ function mapStateToProps(state) {
|
|||
deviceHeight: state.device.dimension.deviceHeight,
|
||||
postIds: getPostIdsInCurrentChannel(state),
|
||||
postVisibility: state.views.channel.postVisibility[channelId],
|
||||
lastViewedAt: currentChannelMember && currentChannelMember.last_viewed_at,
|
||||
lastViewedAt: state.views.channel.lastChannelViewTime[channelId],
|
||||
loadMorePostsVisible: state.views.channel.loadMorePostsVisible,
|
||||
refreshing: state.views.channel.refreshing,
|
||||
theme: getTheme(state),
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import {
|
|||
} from 'react-native';
|
||||
|
||||
import {General, Users} from 'mattermost-redux/constants';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
|
|
@ -42,6 +41,7 @@ export default class ChannelInfo extends PureComponent {
|
|||
updateChannelNotifyProps: PropTypes.func.isRequired,
|
||||
selectPenultimateChannel: PropTypes.func.isRequired,
|
||||
handleSelectChannel: PropTypes.func.isRequired,
|
||||
setChannelDisplayName: PropTypes.func.isRequired,
|
||||
}),
|
||||
viewArchivedChannels: PropTypes.bool.isRequired,
|
||||
canDeleteChannel: PropTypes.bool.isRequired,
|
||||
|
|
@ -104,7 +104,7 @@ export default class ChannelInfo extends PureComponent {
|
|||
|
||||
close = (redirect = true) => {
|
||||
if (redirect) {
|
||||
EventEmitter.emit(General.DEFAULT_CHANNEL, '');
|
||||
this.props.actions.setChannelDisplayName('');
|
||||
}
|
||||
if (Platform.OS === 'android') {
|
||||
this.props.navigator.dismissModal({animated: true});
|
||||
|
|
|
|||
|
|
@ -33,10 +33,11 @@ import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general
|
|||
import {
|
||||
closeDMChannel,
|
||||
closeGMChannel,
|
||||
handleSelectChannel,
|
||||
leaveChannel,
|
||||
loadChannelsByTeamName,
|
||||
selectPenultimateChannel,
|
||||
handleSelectChannel,
|
||||
setChannelDisplayName,
|
||||
} from 'app/actions/views/channel';
|
||||
|
||||
import ChannelInfo from './channel_info';
|
||||
|
|
@ -107,6 +108,7 @@ function mapDispatchToProps(dispatch) {
|
|||
selectFocusedPostId,
|
||||
updateChannelNotifyProps,
|
||||
selectPenultimateChannel,
|
||||
setChannelDisplayName,
|
||||
handleSelectChannel,
|
||||
}, dispatch),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import {
|
|||
app,
|
||||
store,
|
||||
} from 'app/mattermost';
|
||||
import {loadFromPushNotification} from 'app/actions/views/root';
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import PushNotifications from 'app/push_notifications';
|
||||
import {stripTrailingSlashes} from 'app/utils/url';
|
||||
|
|
@ -202,11 +201,8 @@ export default class Entry extends PureComponent {
|
|||
const {data, text, badge, completed} = notificationData;
|
||||
|
||||
// if the notification has a completed property it means that we are replying to a notification
|
||||
// and in case it doesn't it means we just opened the notification
|
||||
if (completed) {
|
||||
onPushNotificationReply(data, text, badge, completed);
|
||||
} else {
|
||||
await store.dispatch(loadFromPushNotification(notification));
|
||||
}
|
||||
PushNotifications.resetNotification();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getChannel as getChannelAction, joinChannel, markChannelAsRead, markChannelAsViewed} from 'mattermost-redux/actions/channels';
|
||||
import {getChannel as getChannelAction, joinChannel} from 'mattermost-redux/actions/channels';
|
||||
import {getPostsAfter, getPostsBefore, getPostThread, selectPost} from 'mattermost-redux/actions/posts';
|
||||
import {makeGetChannel, getMyChannelMemberships} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {makeGetPostIdsAroundPost, getPost} from 'mattermost-redux/selectors/entities/posts';
|
||||
|
|
@ -68,8 +68,6 @@ function mapDispatchToProps(dispatch) {
|
|||
handleTeamChange,
|
||||
joinChannel,
|
||||
loadThreadIfNecessary,
|
||||
markChannelAsRead,
|
||||
markChannelAsViewed,
|
||||
selectPost,
|
||||
setChannelDisplayName,
|
||||
setChannelLoading,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
InteractionManager,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
|
|
@ -53,8 +52,6 @@ export default class Permalink extends PureComponent {
|
|||
handleTeamChange: PropTypes.func.isRequired,
|
||||
joinChannel: PropTypes.func.isRequired,
|
||||
loadThreadIfNecessary: PropTypes.func.isRequired,
|
||||
markChannelAsRead: PropTypes.func.isRequired,
|
||||
markChannelAsViewed: PropTypes.func.isRequired,
|
||||
selectPost: PropTypes.func.isRequired,
|
||||
setChannelDisplayName: PropTypes.func.isRequired,
|
||||
setChannelLoading: PropTypes.func.isRequired,
|
||||
|
|
@ -232,10 +229,8 @@ export default class Permalink extends PureComponent {
|
|||
const {
|
||||
handleSelectChannel,
|
||||
handleTeamChange,
|
||||
markChannelAsRead,
|
||||
setChannelLoading,
|
||||
setChannelDisplayName,
|
||||
markChannelAsViewed,
|
||||
} = actions;
|
||||
|
||||
actions.selectPost('');
|
||||
|
|
@ -266,19 +261,12 @@ export default class Permalink extends PureComponent {
|
|||
}
|
||||
|
||||
if (channelTeamId && currentTeamId !== channelTeamId) {
|
||||
handleTeamChange(channelTeamId, false);
|
||||
handleTeamChange(channelTeamId);
|
||||
}
|
||||
|
||||
setChannelLoading(channelId !== currentChannelId);
|
||||
setChannelDisplayName(channelDisplayName);
|
||||
handleSelectChannel(channelId);
|
||||
|
||||
InteractionManager.runAfterInteractions(async () => {
|
||||
markChannelAsRead(channelId, currentChannelId);
|
||||
if (channelId !== currentChannelId) {
|
||||
markChannelAsViewed(currentChannelId);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ describe('Permalink', () => {
|
|||
handleTeamChange: jest.fn(),
|
||||
joinChannel: jest.fn(),
|
||||
loadThreadIfNecessary: jest.fn(),
|
||||
markChannelAsRead: jest.fn(),
|
||||
markChannelAsViewed: jest.fn(),
|
||||
selectPost: jest.fn(),
|
||||
setChannelDisplayName: jest.fn(),
|
||||
setChannelLoading: jest.fn(),
|
||||
|
|
|
|||
|
|
@ -6,10 +6,8 @@ import {connect} from 'react-redux';
|
|||
|
||||
import {handleTeamChange} from 'app/actions/views/select_team';
|
||||
|
||||
import {markChannelAsRead} from 'mattermost-redux/actions/channels';
|
||||
import {getTeams, joinTeam} from 'mattermost-redux/actions/teams';
|
||||
import {logout} from 'mattermost-redux/actions/users';
|
||||
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getJoinableTeams} from 'mattermost-redux/selectors/entities/teams';
|
||||
|
||||
import {getCurrentLocale} from 'app/selectors/i18n';
|
||||
|
|
@ -30,7 +28,6 @@ function mapStateToProps(state) {
|
|||
return {
|
||||
teamsRequest: state.requests.teams.getTeams,
|
||||
teams: Object.values(getJoinableTeams(state)).sort(sortTeams),
|
||||
currentChannelId: getCurrentChannelId(state),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -41,7 +38,6 @@ function mapDispatchToProps(dispatch) {
|
|||
handleTeamChange,
|
||||
joinTeam,
|
||||
logout,
|
||||
markChannelAsRead,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,9 +47,7 @@ export default class SelectTeam extends PureComponent {
|
|||
handleTeamChange: PropTypes.func.isRequired,
|
||||
joinTeam: PropTypes.func.isRequired,
|
||||
logout: PropTypes.func.isRequired,
|
||||
markChannelAsRead: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
currentChannelId: PropTypes.string,
|
||||
currentUrl: PropTypes.string.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
userWithoutTeams: PropTypes.bool,
|
||||
|
|
@ -143,17 +141,12 @@ export default class SelectTeam extends PureComponent {
|
|||
|
||||
onSelectTeam = async (team) => {
|
||||
this.setState({joining: true});
|
||||
const {currentChannelId, userWithoutTeams} = this.props;
|
||||
const {userWithoutTeams} = this.props;
|
||||
const {
|
||||
joinTeam,
|
||||
handleTeamChange,
|
||||
markChannelAsRead,
|
||||
} = this.props.actions;
|
||||
|
||||
if (currentChannelId) {
|
||||
markChannelAsRead(currentChannelId);
|
||||
}
|
||||
|
||||
const {error} = await joinTeam(team.invite_id, team.id);
|
||||
if (error) {
|
||||
Alert.alert(error.message);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ describe('SelectTeam', () => {
|
|||
handleTeamChange: jest.fn(),
|
||||
joinTeam: jest.fn(),
|
||||
logout: jest.fn(),
|
||||
markChannelAsRead: jest.fn(),
|
||||
};
|
||||
|
||||
const baseProps = {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import {Platform} from 'react-native';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
|
||||
import {markChannelAsRead} from 'mattermost-redux/actions/channels';
|
||||
import {markChannelAsRead, markChannelAsViewed} from 'mattermost-redux/actions/channels';
|
||||
import {setDeviceToken} from 'mattermost-redux/actions/general';
|
||||
import {getPosts} from 'mattermost-redux/actions/posts';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
|
@ -50,7 +50,7 @@ const onRegisterDevice = (data) => {
|
|||
};
|
||||
|
||||
const loadFromNotification = async (notification) => {
|
||||
await store.dispatch(loadFromPushNotification(notification));
|
||||
await store.dispatch(loadFromPushNotification(notification, app.startAppFromPushNotification));
|
||||
if (!app.startAppFromPushNotification) {
|
||||
EventEmitter.emit(ViewTypes.NOTIFICATION_TAPPED);
|
||||
PushNotifications.resetNotification();
|
||||
|
|
@ -63,7 +63,7 @@ const onPushNotification = async (deviceNotification) => {
|
|||
let stopLoadingNotification = false;
|
||||
|
||||
// mark the app as started as soon as possible
|
||||
if (Platform.OS === 'android' && !app.appStarted) {
|
||||
if (!app.appStarted) {
|
||||
app.setStartAppFromPushNotification(true);
|
||||
}
|
||||
|
||||
|
|
@ -79,6 +79,7 @@ const onPushNotification = async (deviceNotification) => {
|
|||
|
||||
if (data.type === 'clear') {
|
||||
dispatch(markChannelAsRead(data.channel_id, null, false));
|
||||
dispatch(markChannelAsViewed(data.channel_id, null));
|
||||
} else {
|
||||
// get the posts for the channel as soon as possible
|
||||
retryGetPostsAction(getPosts(data.channel_id), dispatch, getState);
|
||||
|
|
|
|||
36
package-lock.json
generated
36
package-lock.json
generated
|
|
@ -1949,7 +1949,7 @@
|
|||
},
|
||||
"babel-plugin-istanbul": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "http://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz",
|
||||
"integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
@ -1988,7 +1988,7 @@
|
|||
},
|
||||
"babel-plugin-syntax-object-rest-spread": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "http://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
|
||||
"integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=",
|
||||
"dev": true
|
||||
},
|
||||
|
|
@ -2566,6 +2566,7 @@
|
|||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
|
||||
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"is-extendable": "^0.1.0"
|
||||
}
|
||||
|
|
@ -2610,7 +2611,8 @@
|
|||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "4.0.0",
|
||||
|
|
@ -2806,7 +2808,7 @@
|
|||
},
|
||||
"colors": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "http://registry.npmjs.org/colors/-/colors-0.5.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz",
|
||||
"integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=",
|
||||
"dev": true
|
||||
},
|
||||
|
|
@ -3258,7 +3260,7 @@
|
|||
"dependencies": {
|
||||
"domelementtype": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
|
||||
"integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=",
|
||||
"dev": true
|
||||
}
|
||||
|
|
@ -4146,7 +4148,7 @@
|
|||
},
|
||||
"form-data": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "http://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
|
||||
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
@ -6420,7 +6422,7 @@
|
|||
},
|
||||
"jest-get-type": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "http://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz",
|
||||
"integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==",
|
||||
"dev": true
|
||||
},
|
||||
|
|
@ -8381,8 +8383,8 @@
|
|||
"integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w="
|
||||
},
|
||||
"mattermost-redux": {
|
||||
"version": "github:mattermost/mattermost-redux#656b40147b82e5914aea9c0e7818ef9f042c298a",
|
||||
"from": "github:mattermost/mattermost-redux#656b40147b82e5914aea9c0e7818ef9f042c298a",
|
||||
"version": "github:mattermost/mattermost-redux#457943d7154a4a5c62ece9e28e720136d25c8bb4",
|
||||
"from": "github:mattermost/mattermost-redux#457943d7154a4a5c62ece9e28e720136d25c8bb4",
|
||||
"requires": {
|
||||
"deep-equal": "1.0.1",
|
||||
"eslint-plugin-header": "2.0.0",
|
||||
|
|
@ -10496,7 +10498,7 @@
|
|||
},
|
||||
"os-homedir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
|
||||
"dev": true
|
||||
},
|
||||
|
|
@ -10680,7 +10682,7 @@
|
|||
},
|
||||
"pegjs": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "http://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz",
|
||||
"integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0="
|
||||
},
|
||||
"performance-now": {
|
||||
|
|
@ -10786,7 +10788,7 @@
|
|||
},
|
||||
"pretty-format": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "http://registry.npmjs.org/pretty-format/-/pretty-format-4.3.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-4.3.1.tgz",
|
||||
"integrity": "sha1-UwvlxCs8BbNkFKeipDN6qArNDo0="
|
||||
},
|
||||
"private": {
|
||||
|
|
@ -11360,7 +11362,7 @@
|
|||
},
|
||||
"react-native-tab-view": {
|
||||
"version": "0.0.77",
|
||||
"resolved": "http://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-0.0.77.tgz",
|
||||
"resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-0.0.77.tgz",
|
||||
"integrity": "sha512-9vjD4Ly1Zlum1Y4g23ODpi/F3gYIUIsKWrsZO/Oh5cuX1eiB1DRVn11nY1z+j/hsQfhfyW6nDlmySyDvYQvYCA==",
|
||||
"requires": {
|
||||
"prop-types": "^15.6.0"
|
||||
|
|
@ -12134,7 +12136,7 @@
|
|||
"dependencies": {
|
||||
"jsesc": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
|
||||
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0="
|
||||
}
|
||||
}
|
||||
|
|
@ -12267,7 +12269,7 @@
|
|||
},
|
||||
"require-uncached": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
|
||||
"integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
@ -13330,7 +13332,7 @@
|
|||
},
|
||||
"ws": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "http://registry.npmjs.org/ws/-/ws-5.1.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-5.1.1.tgz",
|
||||
"integrity": "sha512-bOusvpCb09TOBLbpMKszd45WKC2KPtxiyiHanv+H2DE3Az+1db5a/L7sVJZVDPUC1Br8f0SKRr1KjLpD1U/IAw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
@ -13718,7 +13720,7 @@
|
|||
},
|
||||
"load-json-file": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
||||
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
"intl": "1.2.5",
|
||||
"jail-monkey": "1.0.0",
|
||||
"jsc-android": "236355.1.0",
|
||||
"mattermost-redux": "github:mattermost/mattermost-redux#656b40147b82e5914aea9c0e7818ef9f042c298a",
|
||||
"mattermost-redux": "github:mattermost/mattermost-redux#457943d7154a4a5c62ece9e28e720136d25c8bb4",
|
||||
"mime-db": "1.37.0",
|
||||
"moment-timezone": "0.5.23",
|
||||
"prop-types": "15.6.2",
|
||||
|
|
|
|||
Loading…
Reference in a new issue