Automated cherry pick of #3860 (#3868)

* Fix iOS photo/camera denied permissions

* Add unit test and rename files

* Request for permission if returns denied

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Mattermost Build
2020-01-28 20:56:18 +01:00
committed by Elias Nahum
parent 352a103b48
commit 1e0ead398f
13 changed files with 290 additions and 38 deletions

View File

@@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CameraButton should match snapshot 1`] = `
<TouchableWithFeedbackIOS
onPress={[Function]}
style={Object {}}
type="opacity"
>
<Icon
allowFontScaling={false}
color="rgba(61,60,64,0.64)"
name="camera-outline"
size={24}
/>
</TouchableWithFeedbackIOS>
`;

View File

@@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`FileUploadButton should match snapshot 1`] = `
<TouchableWithFeedbackIOS
onPress={[Function]}
style={Object {}}
type="opacity"
>
<Icon
allowFontScaling={false}
color="rgba(61,60,64,0.64)"
name="file-document-outline"
size={24}
/>
</TouchableWithFeedbackIOS>
`;

View File

@@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ImageUploadButton should match snapshot 1`] = `
<TouchableWithFeedbackIOS
onPress={[Function]}
style={Object {}}
type="opacity"
>
<Icon
allowFontScaling={false}
color="rgba(61,60,64,0.64)"
name="image-outline"
size={24}
/>
</TouchableWithFeedbackIOS>
`;

View File

@@ -107,9 +107,10 @@ export default class AttachmentButton extends PureComponent {
const {formatMessage} = this.context.intl;
let permissionRequest;
const targetSource = Permissions.PERMISSIONS.IOS.CAMERA;
const hasPermissionToStorage = await Permissions.check(targetSource);
const hasPermission = await Permissions.check(targetSource);
switch (hasPermissionToStorage) {
switch (hasPermission) {
case Permissions.RESULTS.DENIED:
case Permissions.RESULTS.UNAVAILABLE:
permissionRequest = await Permissions.request(targetSource);
if (permissionRequest !== Permissions.RESULTS.AUTHORIZED) {
@@ -117,17 +118,13 @@ export default class AttachmentButton extends PureComponent {
}
break;
case Permissions.RESULTS.BLOCKED: {
const canOpenSettings = await Permissions.canOpenSettings();
let grantOption = null;
if (canOpenSettings) {
grantOption = {
text: formatMessage({
id: 'mobile.permission_denied_retry',
defaultMessage: 'Settings',
}),
onPress: () => Permissions.openSettings(),
};
}
const grantOption = {
text: formatMessage({
id: 'mobile.permission_denied_retry',
defaultMessage: 'Settings',
}),
onPress: () => Permissions.openSettings(),
};
const {title, text} = this.getPermissionDeniedMessage();

View File

@@ -0,0 +1,66 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {Alert} from 'react-native';
import {shallow} from 'enzyme';
import Permissions from 'react-native-permissions';
import Preferences from 'mattermost-redux/constants/preferences';
import CameraButton from './camera_button';
jest.mock('react-intl');
jest.mock('react-native-image-picker', () => ({
launchCamera: jest.fn(),
}));
describe('CameraButton', () => {
const formatMessage = jest.fn();
const baseProps = {
fileCount: 0,
maxFileCount: 5,
onShowFileMaxWarning: jest.fn(),
theme: Preferences.THEMES.default,
uploadFiles: jest.fn(),
buttonContainerStyle: {},
};
test('should match snapshot', () => {
const wrapper = shallow(<CameraButton {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
test('should return permission false if permission is denied in iOS', async () => {
jest.spyOn(Permissions, 'check').mockReturnValue(Permissions.RESULTS.UNAVAILABLE);
jest.spyOn(Permissions, 'request').mockReturnValue(Permissions.RESULTS.DENIED);
const wrapper = shallow(
<CameraButton {...baseProps}/>,
{context: {intl: {formatMessage}}},
);
const hasPermission = await wrapper.instance().hasCameraPermission();
expect(Permissions.check).toHaveBeenCalled();
expect(Permissions.request).toHaveBeenCalled();
expect(Alert.alert).not.toHaveBeenCalled();
expect(hasPermission).toBe(false);
});
test('should show permission denied alert and return permission false if permission is blocked in iOS', async () => {
jest.spyOn(Permissions, 'check').mockReturnValue(Permissions.RESULTS.BLOCKED);
jest.spyOn(Alert, 'alert').mockReturnValue(true);
const wrapper = shallow(
<CameraButton {...baseProps}/>,
{context: {intl: {formatMessage}}},
);
const hasPermission = await wrapper.instance().hasCameraPermission();
expect(Permissions.check).toHaveBeenCalled();
expect(Permissions.request).not.toHaveBeenCalled();
expect(Alert.alert).toHaveBeenCalled();
expect(hasPermission).toBe(false);
});
});

View File

@@ -100,6 +100,7 @@ export default class FileUploadButton extends PureComponent {
const hasPermissionToStorage = await Permissions.check(storagePermission);
switch (hasPermissionToStorage) {
case Permissions.RESULTS.DENIED:
case Permissions.RESULTS.UNAVAILABLE:
permissionRequest = await Permissions.request(storagePermission);
if (permissionRequest !== Permissions.RESULTS.AUTHORIZED) {

View File

@@ -0,0 +1,76 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {Alert, Platform} from 'react-native';
import {shallow} from 'enzyme';
import Permissions from 'react-native-permissions';
import Preferences from 'mattermost-redux/constants/preferences';
import FileUploadButton from './file_upload_button';
jest.mock('react-intl');
jest.mock('react-native-image-picker', () => ({
launchCamera: jest.fn(),
}));
describe('FileUploadButton', () => {
const formatMessage = jest.fn();
const baseProps = {
blurTextBox: jest.fn(),
browseFileTypes: '*',
fileCount: 0,
maxFileCount: 5,
onShowFileMaxWarning: jest.fn(),
theme: Preferences.THEMES.default,
uploadFiles: jest.fn(),
buttonContainerStyle: {},
};
beforeAll(() => {
Platform.OS = 'android';
});
afterAll(() => {
Platform.OS = 'ios';
});
test('should match snapshot', () => {
const wrapper = shallow(<FileUploadButton {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
test('should return permission false if permission is denied in Android', async () => {
jest.spyOn(Permissions, 'check').mockReturnValue(Permissions.RESULTS.UNAVAILABLE);
jest.spyOn(Permissions, 'request').mockReturnValue(Permissions.RESULTS.DENIED);
const wrapper = shallow(
<FileUploadButton {...baseProps}/>,
{context: {intl: {formatMessage}}},
);
const hasPermission = await wrapper.instance().hasStoragePermission();
expect(Permissions.check).toHaveBeenCalled();
expect(Permissions.request).toHaveBeenCalled();
expect(Alert.alert).not.toHaveBeenCalled();
expect(hasPermission).toBe(false);
});
test('should show permission denied alert and return permission false if permission is blocked in Android', async () => {
jest.spyOn(Permissions, 'check').mockReturnValue(Permissions.RESULTS.BLOCKED);
jest.spyOn(Alert, 'alert').mockReturnValue(true);
const wrapper = shallow(
<FileUploadButton {...baseProps}/>,
{context: {intl: {formatMessage}}},
);
const hasPermission = await wrapper.instance().hasStoragePermission();
expect(Permissions.check).toHaveBeenCalled();
expect(Permissions.request).not.toHaveBeenCalled();
expect(Alert.alert).toHaveBeenCalled();
expect(hasPermission).toBe(false);
});
});

View File

@@ -109,9 +109,10 @@ export default class ImageUploadButton extends PureComponent {
const {formatMessage} = this.context.intl;
let permissionRequest;
const targetSource = Permissions.PERMISSIONS.IOS.PHOTO_LIBRARY;
const hasPermissionToStorage = await Permissions.check(targetSource);
const hasPermission = await Permissions.check(targetSource);
switch (hasPermissionToStorage) {
switch (hasPermission) {
case Permissions.RESULTS.DENIED:
case Permissions.RESULTS.UNAVAILABLE:
permissionRequest = await Permissions.request(targetSource);
if (permissionRequest !== Permissions.RESULTS.AUTHORIZED) {
@@ -119,17 +120,13 @@ export default class ImageUploadButton extends PureComponent {
}
break;
case Permissions.RESULTS.BLOCKED: {
const canOpenSettings = await Permissions.canOpenSettings();
let grantOption = null;
if (canOpenSettings) {
grantOption = {
text: formatMessage({
id: 'mobile.permission_denied_retry',
defaultMessage: 'Settings',
}),
onPress: () => Permissions.openSettings(),
};
}
const grantOption = {
text: formatMessage({
id: 'mobile.permission_denied_retry',
defaultMessage: 'Settings',
}),
onPress: () => Permissions.openSettings(),
};
const {title, text} = this.getPermissionDeniedMessage();

View File

@@ -0,0 +1,67 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {Alert} from 'react-native';
import {shallow} from 'enzyme';
import Permissions from 'react-native-permissions';
import Preferences from 'mattermost-redux/constants/preferences';
import ImageUploadButton from './image_upload_button';
jest.mock('react-intl');
jest.mock('react-native-image-picker', () => ({
launchCamera: jest.fn(),
}));
describe('ImageUploadButton', () => {
const formatMessage = jest.fn();
const baseProps = {
blurTextBox: jest.fn(),
fileCount: 0,
maxFileCount: 5,
onShowFileMaxWarning: jest.fn(),
theme: Preferences.THEMES.default,
uploadFiles: jest.fn(),
buttonContainerStyle: {},
};
test('should match snapshot', () => {
const wrapper = shallow(<ImageUploadButton {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
test('should return permission false if permission is denied in iOS', async () => {
jest.spyOn(Permissions, 'check').mockReturnValue(Permissions.RESULTS.UNAVAILABLE);
jest.spyOn(Permissions, 'request').mockReturnValue(Permissions.RESULTS.DENIED);
const wrapper = shallow(
<ImageUploadButton {...baseProps}/>,
{context: {intl: {formatMessage}}},
);
const hasPermission = await wrapper.instance().hasPhotoPermission();
expect(Permissions.check).toHaveBeenCalled();
expect(Permissions.request).toHaveBeenCalled();
expect(Alert.alert).not.toHaveBeenCalled();
expect(hasPermission).toBe(false);
});
test('should show permission denied alert and return permission false if permission is blocked in iOS', async () => {
jest.spyOn(Permissions, 'check').mockReturnValue(Permissions.RESULTS.BLOCKED);
jest.spyOn(Alert, 'alert').mockReturnValue(true);
const wrapper = shallow(
<ImageUploadButton {...baseProps}/>,
{context: {intl: {formatMessage}}},
);
const hasPermission = await wrapper.instance().hasPhotoPermission();
expect(Permissions.check).toHaveBeenCalled();
expect(Permissions.request).not.toHaveBeenCalled();
expect(Alert.alert).toHaveBeenCalled();
expect(hasPermission).toBe(false);
});
});

View File

@@ -14,9 +14,9 @@ import PasteableTextInput from 'app/components/pasteable_text_input';
import EphemeralStore from 'app/store/ephemeral_store';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import FileUploadButton from './components/fileUploadButton';
import ImageUploadButton from './components/imageUploadButton';
import CameraButton from './components/cameraButton';
import FileUploadButton from './components/file_upload_button';
import ImageUploadButton from './components/image_upload_button';
import CameraButton from './components/camera_button';
import PostTextbox from './post_textbox.ios';

View File

@@ -28,9 +28,9 @@ import {General, RequestStatus} from 'mattermost-redux/constants';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {getFormattedFileSize} from 'mattermost-redux/utils/file_utils';
import FileUploadButton from './components/fileUploadButton';
import ImageUploadButton from './components/imageUploadButton';
import CameraButton from './components/cameraButton';
import FileUploadButton from './components/file_upload_button';
import ImageUploadButton from './components/image_upload_button';
import CameraButton from './components/camera_button';
import FormattedMarkdownText from 'app/components/formatted_markdown_text';
import FormattedText from 'app/components/formatted_text';
import PasteableTextInput from 'app/components/pasteable_text_input';

View File

@@ -119,9 +119,9 @@ module.exports = [
'app/components/post_list/post_list.js',
'app/components/post_profile_picture/index.js',
'app/components/post_profile_picture/post_profile_picture.js',
'app/components/post_textbox/components/cameraButton.js',
'app/components/post_textbox/components/fileUploadButton.js',
'app/components/post_textbox/components/imageUploadButton.js',
'app/components/post_textbox/components/camera_button.js',
'app/components/post_textbox/components/file_upload_button.js',
'app/components/post_textbox/components/image_upload_button.js',
'app/components/post_textbox/components/typing/index.js',
'app/components/post_textbox/components/typing/typing.js',
'app/components/post_textbox/index.js',

View File

@@ -91,9 +91,9 @@ module.exports = [
'./node_modules/app/components/post_list/post_list.js',
'./node_modules/app/components/post_profile_picture/index.js',
'./node_modules/app/components/post_profile_picture/post_profile_picture.js',
'./node_modules/app/components/post_textbox/components/cameraButton.js',
'./node_modules/app/components/post_textbox/components/fileUploadButton.js',
'./node_modules/app/components/post_textbox/components/imageUploadButton.js',
'./node_modules/app/components/post_textbox/components/camera_button.js',
'./node_modules/app/components/post_textbox/components/file_upload_button.js',
'./node_modules/app/components/post_textbox/components/image_upload_button.js',
'./node_modules/app/components/post_textbox/components/typing/index.js',
'./node_modules/app/components/post_textbox/components/typing/typing.js',
'./node_modules/app/components/post_textbox/index.js',