Automated cherry pick of #3618 (#3627)

* Use bottom sheet over modal

* check-style fixes
This commit is contained in:
Mattermost Build 2019-11-28 12:12:45 +01:00 committed by Elias Nahum
parent 4b58f01597
commit dfc50b7dff
2 changed files with 147 additions and 26 deletions

View file

@ -13,6 +13,7 @@ import {
Text,
TouchableOpacity,
View,
findNodeHandle,
} from 'react-native';
import RNFetchBlob from 'rn-fetch-blob';
import Icon from 'react-native-vector-icons/Ionicons';
@ -32,8 +33,8 @@ import {getLocalFilePathFromFile, isDocument, isVideo} from 'app/utils/file';
import {emptyFunction} from 'app/utils/general';
import {calculateDimensions} from 'app/utils/images';
import {t} from 'app/utils/i18n';
import BottomSheet from 'app/utils/bottom_sheet';
import {
showModalOverCurrentContext,
dismissModal,
mergeNavigationOptions,
} from 'app/actions/navigation';
@ -89,6 +90,8 @@ export default class ImagePreview extends PureComponent {
showDownloader: false,
target: props.target,
};
this.headerRef = React.createRef();
}
componentDidMount() {
@ -267,6 +270,7 @@ export default class ImagePreview extends PureComponent {
return (
<TouchableOpacity
ref={this.headerRef}
onPress={action}
style={style.headerIcon}
>
@ -434,7 +438,9 @@ export default class ImagePreview extends PureComponent {
}
this.setState({showHeaderFooter: show});
StatusBar.setHidden(!show, 'slide');
if (Platform.OS === 'ios') {
StatusBar.setHidden(!show, 'slide');
}
Animated.timing(this.headerFooterAnim, {
...ANIM_CONFIG,
@ -465,7 +471,8 @@ export default class ImagePreview extends PureComponent {
showDownloadOptionsIOS = async () => {
const {formatMessage} = this.context.intl;
const file = this.getCurrentFile();
const items = [];
const options = [];
const actions = [];
let permissionRequest;
const hasPermissionToStorage = await Permissions.check('photo');
@ -510,37 +517,37 @@ export default class ImagePreview extends PureComponent {
const path = getLocalFilePathFromFile(VIDEOS_PATH, file);
const exist = await RNFetchBlob.fs.exists(path);
if (exist) {
items.push({
action: this.saveVideoIOS,
text: {
id: t('mobile.image_preview.save_video'),
defaultMessage: 'Save Video',
},
});
options.push(formatMessage({
id: t('mobile.image_preview.save_video'),
defaultMessage: 'Save Video',
}));
actions.push(this.saveVideoIOS);
} else {
this.showVideoDownloadRequiredAlertIOS();
}
} else {
items.push({
action: this.showDownloader,
text: {
id: t('mobile.image_preview.save'),
defaultMessage: 'Save Image',
},
});
options.push(formatMessage({
id: t('mobile.image_preview.save'),
defaultMessage: 'Save Image',
}));
actions.push(this.showDownloader);
}
if (items.length) {
this.setHeaderAndFooterVisible(false);
if (options.length) {
options.push(formatMessage({
id: 'mobile.post.cancel',
defaultMessage: 'Cancel',
}));
actions.push(emptyFunction);
const screen = 'OptionsModal';
const passProps = {
BottomSheet.showBottomSheetWithOptions({
options,
cancelButtonIndex: options.length - 1,
title: file.caption,
items,
onCancelPress: () => this.setHeaderAndFooterVisible(true),
};
showModalOverCurrentContext(screen, passProps);
anchor: this.headerRef.current ? findNodeHandle(this.headerRef.current) : null,
}, (buttonIndex) => {
actions[buttonIndex]();
});
}
};

View file

@ -6,10 +6,12 @@ import {shallow} from 'enzyme';
import {
TouchableOpacity,
} from 'react-native';
import RNFetchBlob from 'rn-fetch-blob';
import Preferences from 'mattermost-redux/constants/preferences';
import * as NavigationActions from 'app/actions/navigation';
import BottomSheet from 'app/utils/bottom_sheet';
import ImagePreview from './image_preview';
@ -20,6 +22,11 @@ jest.mock('react-native-doc-viewer', () => {
OpenFile: jest.fn(),
};
});
jest.mock('react-native-permissions', () => {
return {
check: jest.fn(),
};
});
describe('ImagePreview', () => {
const baseProps = {
@ -108,4 +115,111 @@ describe('ImagePreview', () => {
},
);
});
test('should show bottom sheet when showDownloadOptionsIOS is called for existing video', async () => {
BottomSheet.showBottomSheetWithOptions = jest.fn();
RNFetchBlob.fs.exists = jest.fn().mockReturnValue(true);
const formatMessage = jest.fn(({defaultMessage}) => {
return defaultMessage;
});
const index = 0;
const files = [{
caption: 'caption',
data: {
mime_type: 'video/mp4',
},
}];
const props = {
...baseProps,
index,
files,
};
const wrapper = shallow(
<ImagePreview
{...props}
/>,
{context: {intl: {formatMessage}}},
);
const instance = wrapper.instance();
await instance.showDownloadOptionsIOS();
const expectedOptions = {
options: ['Save Video', 'Cancel'],
cancelButtonIndex: 1,
anchor: null,
title: files[index].caption,
};
expect(BottomSheet.showBottomSheetWithOptions).
toHaveBeenCalledWith(expectedOptions, expect.any(Function));
});
test('should not show bottom sheet when showDownloadOptionsIOS is called for non-existing video', async () => {
BottomSheet.showBottomSheetWithOptions = jest.fn();
RNFetchBlob.fs.exists = jest.fn().mockReturnValue(false);
const index = 0;
const files = [{
caption: 'caption',
data: {
mime_type: 'video/mp4',
},
}];
const props = {
...baseProps,
index,
files,
};
const wrapper = shallow(
<ImagePreview
{...props}
/>,
{context: {intl: {formatMessage: jest.fn()}}},
);
const instance = wrapper.instance();
await instance.showDownloadOptionsIOS();
expect(BottomSheet.showBottomSheetWithOptions).not.toHaveBeenCalled();
});
test('should show bottom sheet when showDownloadOptionsIOS is called for image', async () => {
BottomSheet.showBottomSheetWithOptions = jest.fn();
RNFetchBlob.fs.exists = jest.fn().mockReturnValue(true);
const formatMessage = jest.fn(({defaultMessage}) => {
return defaultMessage;
});
const index = 0;
const files = [{
caption: 'caption',
data: {
mime_type: 'image/jpeg',
},
}];
const props = {
...baseProps,
index,
files,
};
const wrapper = shallow(
<ImagePreview
{...props}
/>,
{context: {intl: {formatMessage}}},
);
const instance = wrapper.instance();
await instance.showDownloadOptionsIOS();
const expectedOptions = {
options: ['Save Image', 'Cancel'],
cancelButtonIndex: 1,
anchor: null,
title: files[index].caption,
};
expect(BottomSheet.showBottomSheetWithOptions).
toHaveBeenCalledWith(expectedOptions, expect.any(Function));
});
});