Merge branch 'master' into mark-as-unread
This commit is contained in:
commit
ef162ce403
15 changed files with 175 additions and 55 deletions
|
|
@ -140,7 +140,7 @@ android {
|
|||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
missingDimensionStrategy "RNN.reactNativeVersion", "reactNative60"
|
||||
versionCode 243
|
||||
versionCode 245
|
||||
versionName "1.25.0"
|
||||
multiDexEnabled = true
|
||||
ndk {
|
||||
|
|
|
|||
|
|
@ -379,45 +379,23 @@ export function handleSelectChannel(channelId, fromPushNotification = false) {
|
|||
dispatch(loadPostsIfNecessaryWithRetry(channelId));
|
||||
}
|
||||
|
||||
let previousChannelId;
|
||||
if (!fromPushNotification && !sameChannel) {
|
||||
previousChannelId = currentChannelId;
|
||||
}
|
||||
|
||||
const actions = [
|
||||
selectChannel(channelId),
|
||||
getChannelStats(channelId),
|
||||
setChannelDisplayName(channel.display_name),
|
||||
{
|
||||
type: ViewTypes.SET_INITIAL_POST_VISIBILITY,
|
||||
data: channelId,
|
||||
},
|
||||
setInitialPostVisibility(channelId),
|
||||
setChannelLoading(false),
|
||||
{
|
||||
type: ViewTypes.SET_LAST_CHANNEL_FOR_TEAM,
|
||||
teamId: currentTeamId,
|
||||
channelId,
|
||||
},
|
||||
setLastChannelForTeam(currentTeamId, channelId),
|
||||
selectChannelWithMember(channelId, channel, member),
|
||||
];
|
||||
|
||||
let markPreviousChannelId;
|
||||
if (!fromPushNotification && !sameChannel) {
|
||||
markPreviousChannelId = currentChannelId;
|
||||
actions.push({
|
||||
type: ViewTypes.SELECT_CHANNEL_WITH_MEMBER,
|
||||
data: currentChannelId,
|
||||
channel: getChannel(state, currentChannelId),
|
||||
member: getMyChannelMember(state, currentChannelId),
|
||||
});
|
||||
}
|
||||
|
||||
if (!fromPushNotification) {
|
||||
actions.push({
|
||||
type: ViewTypes.SELECT_CHANNEL_WITH_MEMBER,
|
||||
data: channelId,
|
||||
channel,
|
||||
member,
|
||||
});
|
||||
}
|
||||
|
||||
dispatch(batchActions(actions));
|
||||
|
||||
dispatch(markChannelViewedAndRead(channelId, markPreviousChannelId));
|
||||
dispatch(markChannelViewedAndRead(channelId, previousChannelId));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -686,3 +664,27 @@ function setLoadMorePostsVisible(visible) {
|
|||
data: visible,
|
||||
};
|
||||
}
|
||||
|
||||
function setInitialPostVisibility(channelId) {
|
||||
return {
|
||||
type: ViewTypes.SET_INITIAL_POST_VISIBILITY,
|
||||
data: channelId,
|
||||
};
|
||||
}
|
||||
|
||||
function setLastChannelForTeam(teamId, channelId) {
|
||||
return {
|
||||
type: ViewTypes.SET_LAST_CHANNEL_FOR_TEAM,
|
||||
teamId,
|
||||
channelId,
|
||||
};
|
||||
}
|
||||
|
||||
function selectChannelWithMember(channelId, channel, member) {
|
||||
return {
|
||||
type: ViewTypes.SELECT_CHANNEL_WITH_MEMBER,
|
||||
data: channelId,
|
||||
channel,
|
||||
member,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,27 +5,27 @@ import configureStore from 'redux-mock-store';
|
|||
import thunk from 'redux-thunk';
|
||||
|
||||
import initialState from 'app/initial_state';
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import testHelper from 'test/test_helper';
|
||||
|
||||
import {
|
||||
import * as ChannelActions from 'app/actions/views/channel';
|
||||
const {
|
||||
handleSelectChannel,
|
||||
handleSelectChannelByName,
|
||||
loadPostsIfNecessaryWithRetry,
|
||||
} from 'app/actions/views/channel';
|
||||
} = ChannelActions;
|
||||
|
||||
import postReducer from 'mattermost-redux/reducers/entities/posts';
|
||||
|
||||
jest.mock('mattermost-redux/selectors/entities/channels', () => ({
|
||||
getChannel: () => ({data: 'received-channel-id'}),
|
||||
getCurrentChannelId: () => 'current-channel-id',
|
||||
getMyChannelMember: () => ({data: {member: {}}}),
|
||||
}));
|
||||
const MOCK_CHANNEL_MARK_AS_READ = 'MOCK_CHANNEL_MARK_AS_READ';
|
||||
const MOCK_CHANNEL_MARK_AS_VIEWED = 'MOCK_CHANNEL_MARK_AS_VIEWED';
|
||||
|
||||
jest.mock('mattermost-redux/actions/channels', () => {
|
||||
const channelActions = require.requireActual('mattermost-redux/actions/channels');
|
||||
return {
|
||||
...channelActions,
|
||||
markChannelAsRead: jest.fn(),
|
||||
markChannelAsViewed: jest.fn(),
|
||||
markChannelAsRead: jest.fn().mockReturnValue({type: 'MOCK_CHANNEL_MARK_AS_READ'}),
|
||||
markChannelAsViewed: jest.fn().mockReturnValue({type: 'MOCK_CHANNEL_MARK_AS_VIEWED'}),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -130,6 +130,11 @@ describe('Actions.Views.Channel', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const channelSelectors = require('mattermost-redux/selectors/entities/channels');
|
||||
channelSelectors.getChannel = jest.fn((state, channelId) => ({data: channelId}));
|
||||
channelSelectors.getCurrentChannelId = jest.fn(() => currentChannelId);
|
||||
channelSelectors.getMyChannelMember = jest.fn(() => ({data: {member: {}}}));
|
||||
|
||||
test('handleSelectChannelByName success', async () => {
|
||||
store = mockStore(storeObj);
|
||||
|
||||
|
|
@ -238,4 +243,38 @@ describe('Actions.Views.Channel', () => {
|
|||
expect(postActions.getPostsSince).toHaveBeenCalledWith(currentChannelId, store.getState().views.channel.lastGetPosts[currentChannelId]);
|
||||
expect(receivedPostsSince).not.toBe(null);
|
||||
});
|
||||
|
||||
const handleSelectChannelCases = [
|
||||
[currentChannelId, true],
|
||||
[currentChannelId, false],
|
||||
[`not-${currentChannelId}`, true],
|
||||
[`not-${currentChannelId}`, false],
|
||||
];
|
||||
test.each(handleSelectChannelCases)('handleSelectChannel dispatches selectChannelWithMember', async (channelId, fromPushNotification) => {
|
||||
store = mockStore({...storeObj});
|
||||
|
||||
await store.dispatch(handleSelectChannel(channelId, fromPushNotification));
|
||||
const storeActions = store.getActions();
|
||||
const storeBatchActions = storeActions.find(({type}) => type === 'BATCHING_REDUCER.BATCH');
|
||||
const selectChannelWithMember = storeBatchActions.payload.find(({type}) => type === ViewTypes.SELECT_CHANNEL_WITH_MEMBER);
|
||||
const viewedAction = storeActions.find(({type}) => type === MOCK_CHANNEL_MARK_AS_VIEWED);
|
||||
const readAction = storeActions.find(({type}) => type === MOCK_CHANNEL_MARK_AS_READ);
|
||||
|
||||
const expectedSelectChannelWithMember = {
|
||||
type: ViewTypes.SELECT_CHANNEL_WITH_MEMBER,
|
||||
data: channelId,
|
||||
channel: {
|
||||
data: channelId,
|
||||
},
|
||||
member: {
|
||||
data: {
|
||||
member: {},
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
expect(selectChannelWithMember).toStrictEqual(expectedSelectChannelWithMember);
|
||||
expect(viewedAction).not.toBe(null);
|
||||
expect(readAction).not.toBe(null);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export function loadConfigAndLicense() {
|
|||
};
|
||||
}
|
||||
|
||||
export function loadFromPushNotification(notification, startAppFromPushNotification) {
|
||||
export function loadFromPushNotification(notification) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const {data} = notification;
|
||||
|
|
@ -84,7 +84,7 @@ export function loadFromPushNotification(notification, startAppFromPushNotificat
|
|||
dispatch(selectTeam({id: teamId}));
|
||||
}
|
||||
|
||||
dispatch(handleSelectChannel(channelId, startAppFromPushNotification));
|
||||
dispatch(handleSelectChannel(channelId, true));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Notification should match snapshot 1`] = `null`;
|
||||
60
app/screens/notification/notification.test.js
Normal file
60
app/screens/notification/notification.test.js
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
|
||||
import Preferences from 'mattermost-redux/constants/preferences';
|
||||
|
||||
import Notification from './notification.js';
|
||||
|
||||
jest.mock('react-native-navigation', () => ({
|
||||
Navigation: {
|
||||
events: jest.fn(() => ({
|
||||
registerComponentDidDisappearListener: jest.fn(),
|
||||
})),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('Notification', () => {
|
||||
const baseProps = {
|
||||
actions: {
|
||||
loadFromPushNotification: jest.fn(),
|
||||
},
|
||||
componentId: 'component-id',
|
||||
deviceWidth: 100,
|
||||
notification: {},
|
||||
theme: Preferences.THEMES.default,
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
<Notification {...baseProps}/>,
|
||||
);
|
||||
|
||||
expect(wrapper.getElement()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should call loadFromPushNotification on notification tap for non-local notification', () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
notification: {
|
||||
localNotification: true,
|
||||
},
|
||||
};
|
||||
const wrapper = shallow(
|
||||
<Notification {...props}/>,
|
||||
);
|
||||
const instance = wrapper.instance();
|
||||
|
||||
instance.notificationTapped();
|
||||
expect(baseProps.actions.loadFromPushNotification).not.toHaveBeenCalled();
|
||||
|
||||
const notification = {
|
||||
localNotification: false,
|
||||
};
|
||||
wrapper.setProps({notification});
|
||||
instance.notificationTapped();
|
||||
expect(baseProps.actions.loadFromPushNotification).toHaveBeenCalledWith(notification);
|
||||
});
|
||||
});
|
||||
|
|
@ -134,12 +134,13 @@ export default class NotificationSettings extends PureComponent {
|
|||
|
||||
saveNotificationProps = (notifyProps) => {
|
||||
const {currentUser} = this.props;
|
||||
const prevProps = getNotificationProps(currentUser);
|
||||
const updatedProps = {
|
||||
...getNotificationProps(currentUser),
|
||||
...prevProps,
|
||||
...notifyProps,
|
||||
};
|
||||
|
||||
if (!deepEqual(updatedProps, notifyProps)) {
|
||||
if (!deepEqual(prevProps, notifyProps)) {
|
||||
this.props.actions.updateMe({notify_props: updatedProps});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,13 +9,16 @@ import {shallowWithIntl} from 'test/intl-test-helper';
|
|||
|
||||
import NotificationSettings from './notification_settings.js';
|
||||
|
||||
import {getNotificationProps} from 'app/utils/notify_props';
|
||||
|
||||
describe('NotificationSettings', () => {
|
||||
const currentUser = {id: 'current_user_id'};
|
||||
const baseProps = {
|
||||
actions: {
|
||||
updateMe: jest.fn(),
|
||||
},
|
||||
componentId: 'component-id',
|
||||
currentUser: {id: 'current_user_id'},
|
||||
currentUser,
|
||||
theme: Preferences.THEMES.default,
|
||||
updateMeRequest: {},
|
||||
currentUserStatus: 'status',
|
||||
|
|
@ -32,17 +35,22 @@ describe('NotificationSettings', () => {
|
|||
});
|
||||
|
||||
test('should include previous notification props when saving new ones', () => {
|
||||
baseProps.currentUser.notify_props = {previous: 'previous'};
|
||||
const wrapper = shallowWithIntl(
|
||||
<NotificationSettings {...baseProps}/>
|
||||
);
|
||||
|
||||
const instance = wrapper.instance();
|
||||
|
||||
const defaultNotifyProps = getNotificationProps(currentUser);
|
||||
instance.saveNotificationProps(defaultNotifyProps);
|
||||
expect(baseProps.actions.updateMe).toHaveBeenCalledTimes(0);
|
||||
|
||||
const newProps = {new: 'new'};
|
||||
instance.saveNotificationProps(newProps);
|
||||
expect(baseProps.actions.updateMe).toHaveBeenCalledTimes(1);
|
||||
expect(baseProps.actions.updateMe).toHaveBeenCalledWith({
|
||||
notify_props: {
|
||||
...baseProps.currentUser.notify_props,
|
||||
...defaultNotifyProps,
|
||||
...newProps,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class PushNotificationUtils {
|
|||
loadFromNotification = async (notification) => {
|
||||
// Set appStartedFromPushNotification to avoid channel screen to call selectInitialChannel
|
||||
EphemeralStore.appStartedFromPushNotification = true;
|
||||
await this.store.dispatch(loadFromPushNotification(notification, true));
|
||||
await this.store.dispatch(loadFromPushNotification(notification));
|
||||
|
||||
// if we have a componentId means that the app is already initialized
|
||||
const componentId = EphemeralStore.getNavigationTopComponentId();
|
||||
|
|
|
|||
|
|
@ -1060,7 +1060,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 243;
|
||||
CURRENT_PROJECT_VERSION = 245;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
ENABLE_BITCODE = NO;
|
||||
|
|
@ -1098,7 +1098,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 243;
|
||||
CURRENT_PROJECT_VERSION = 245;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
ENABLE_BITCODE = NO;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>243</string>
|
||||
<string>245</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.25.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>243</string>
|
||||
<string>245</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@
|
|||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>243</string>
|
||||
<string>245</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.25.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>243</string>
|
||||
<string>245</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ jest.doMock('react-native', () => {
|
|||
ImagePickerManager,
|
||||
requireNativeComponent,
|
||||
Alert: RNAlert,
|
||||
InteractionManager: RNInteractionManager,
|
||||
NativeModules: RNNativeModules,
|
||||
} = ReactNative;
|
||||
|
||||
|
|
@ -31,6 +32,11 @@ jest.doMock('react-native', () => {
|
|||
alert: jest.fn(),
|
||||
};
|
||||
|
||||
const InteractionManager = {
|
||||
...RNInteractionManager,
|
||||
runAfterInteractions: jest.fn((cb) => cb()),
|
||||
};
|
||||
|
||||
const NativeModules = {
|
||||
...RNNativeModules,
|
||||
UIManager: {
|
||||
|
|
@ -87,6 +93,7 @@ jest.doMock('react-native', () => {
|
|||
ImagePickerManager,
|
||||
requireNativeComponent,
|
||||
Alert,
|
||||
InteractionManager,
|
||||
NativeModules,
|
||||
}, ReactNative);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue