From 08df256c1d2a9895c3e513a95f274554d3f66df0 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Fri, 3 May 2019 17:42:32 -0400 Subject: [PATCH] MM-14921 Fix iPad layout including safeAreaInsets (#2748) * MM-14780 and MM-14921 Fix iPad layout including safeAreaInsets * Fix unit tests --- .../network_indicator/network_indicator.js | 4 ++ app/components/post_list/post_list_base.js | 2 +- .../safe_area_view/safe_area_view.ios.js | 9 ++-- .../slide_up_panel/slide_up_panel.js | 3 +- app/constants/device.js | 1 + .../mattermost-managed.ios.js | 1 + .../__snapshots__/post_options.test.js.snap | 12 ++--- ios/Mattermost/MattermostManaged.h | 1 + ios/Mattermost/MattermostManaged.m | 45 ++++++++++++++++++- test/setup.js | 1 + 10 files changed, 66 insertions(+), 13 deletions(-) diff --git a/app/components/network_indicator/network_indicator.js b/app/components/network_indicator/network_indicator.js index 56e4b9f65..a64591e54 100644 --- a/app/components/network_indicator/network_indicator.js +++ b/app/components/network_indicator/network_indicator.js @@ -19,6 +19,7 @@ import IonIcon from 'react-native-vector-icons/Ionicons'; import FormattedText from 'app/components/formatted_text'; import {DeviceTypes, ViewTypes} from 'app/constants'; import mattermostBucket from 'app/mattermost_bucket'; +import mattermostManaged from 'app/mattermost_managed'; import PushNotifications from 'app/push_notifications'; import networkConnectionListener, {checkConnection} from 'app/utils/network'; import {t} from 'app/utils/i18n'; @@ -35,6 +36,7 @@ const { IOS_TOP_LANDSCAPE, IOS_TOP_PORTRAIT, IOSX_TOP_PORTRAIT, + STATUS_BAR_HEIGHT, } = ViewTypes; export default class NetworkIndicator extends PureComponent { @@ -202,6 +204,8 @@ export default class NetworkIndicator extends PureComponent { return IOS_TOP_LANDSCAPE; } else if (isX) { return IOSX_TOP_PORTRAIT; + } else if (isLandscape && DeviceTypes.IS_TABLET && mattermostManaged.hasSafeAreaInsets) { + return IOS_TOP_LANDSCAPE + STATUS_BAR_HEIGHT; } else if (isLandscape) { return IOS_TOP_LANDSCAPE; } diff --git a/app/components/post_list/post_list_base.js b/app/components/post_list/post_list_base.js index 0643437f9..d24404485 100644 --- a/app/components/post_list/post_list_base.js +++ b/app/components/post_list/post_list_base.js @@ -153,7 +153,7 @@ export default class PostListBase extends PureComponent { navigator: this.props.navigator, onHashtagPress: this.props.onHashtagPress, onPermalinkPress: this.handlePermalinkPress, - onPostPress: this.props.onPostPress, + onPress: this.props.onPostPress, renderReplies: this.props.renderReplies, shouldRenderReplyButton: this.props.shouldRenderReplyButton, }; diff --git a/app/components/safe_area_view/safe_area_view.ios.js b/app/components/safe_area_view/safe_area_view.ios.js index 05a4ccd1a..8477a78b7 100644 --- a/app/components/safe_area_view/safe_area_view.ios.js +++ b/app/components/safe_area_view/safe_area_view.ios.js @@ -7,6 +7,7 @@ import {Dimensions, Keyboard, NativeModules, View} from 'react-native'; import SafeArea from 'react-native-safe-area'; import {DeviceTypes} from 'app/constants'; +import mattermostManaged from 'app/mattermost_managed'; const {StatusBarManager} = NativeModules; @@ -41,7 +42,7 @@ export default class SafeAreaIos extends PureComponent { safeAreaInsets: { top: DeviceTypes.IS_IPHONE_X ? 44 : 20, left: 0, - bottom: DeviceTypes.IS_IPHONE_X ? 34 : 15, + bottom: DeviceTypes.IS_IPHONE_X || mattermostManaged.hasSafeAreaInsets ? 20 : 0, right: 0, }, statusBarHeight: 20, @@ -86,7 +87,7 @@ export default class SafeAreaIos extends PureComponent { getSafeAreaInsets = () => { this.getStatusBarHeight(); - if (DeviceTypes.IS_IPHONE_X) { + if (DeviceTypes.IS_IPHONE_X || mattermostManaged.hasSafeAreaInsets) { SafeArea.getSafeAreaInsetsForRootView().then((result) => { const {safeAreaInsets} = result; @@ -173,7 +174,7 @@ export default class SafeAreaIos extends PureComponent { } let offset = 0; - if (keyboardOffset && DeviceTypes.IS_IPHONE_X) { + if (keyboardOffset && mattermostManaged.hasSafeAreaInsets) { offset = keyboardOffset; } @@ -186,7 +187,7 @@ export default class SafeAreaIos extends PureComponent { > {this.renderTopBar()} {children} - + {footerComponent} diff --git a/app/components/slide_up_panel/slide_up_panel.js b/app/components/slide_up_panel/slide_up_panel.js index 1bc04bfb6..02473d2e9 100644 --- a/app/components/slide_up_panel/slide_up_panel.js +++ b/app/components/slide_up_panel/slide_up_panel.js @@ -12,10 +12,11 @@ import { } from 'react-native-gesture-handler'; import {DeviceTypes} from 'app/constants'; +import mattermostManaged from 'app/mattermost_managed'; import SlideUpPanelIndicator from './slide_up_panel_indicator'; -export const BOTTOM_MARGIN = DeviceTypes.IS_IPHONE_X ? 24 : 0; +export const BOTTOM_MARGIN = mattermostManaged.hasSafeAreaInsets ? 24 : 0; const TOP_IOS_MARGIN = DeviceTypes.IS_IPHONE_X ? 84 : 64; const TOP_ANDROID_MARGIN = 44; const TOP_MARGIN = Platform.OS === 'ios' ? TOP_IOS_MARGIN : TOP_ANDROID_MARGIN; diff --git a/app/constants/device.js b/app/constants/device.js index 43d1986b6..a7b66ad20 100644 --- a/app/constants/device.js +++ b/app/constants/device.js @@ -18,5 +18,6 @@ export default { DOCUMENTS_PATH: `${RNFetchBlobFS.dirs.CacheDir}/Documents`, IMAGES_PATH: `${RNFetchBlobFS.dirs.CacheDir}/Images`, IS_IPHONE_X: DeviceInfo.getModel().includes('iPhone X'), + IS_TABLET: DeviceInfo.isTablet(), VIDEOS_PATH: `${RNFetchBlobFS.dirs.CacheDir}/Videos`, }; diff --git a/app/mattermost_managed/mattermost-managed.ios.js b/app/mattermost_managed/mattermost-managed.ios.js index 4c4c64d8a..b81d226a9 100644 --- a/app/mattermost_managed/mattermost-managed.ios.js +++ b/app/mattermost_managed/mattermost-managed.ios.js @@ -48,6 +48,7 @@ export default { getCachedConfig: () => { return cachedConfig; }, + hasSafeAreaInsets: MattermostManaged.hasSafeAreaInsets, isDeviceSecure: async () => { try { return await LocalAuth.isDeviceSecure(); diff --git a/app/screens/post_options/__snapshots__/post_options.test.js.snap b/app/screens/post_options/__snapshots__/post_options.test.js.snap index 569b96074..6a81bfc74 100644 --- a/app/screens/post_options/__snapshots__/post_options.test.js.snap +++ b/app/screens/post_options/__snapshots__/post_options.test.js.snap @@ -10,8 +10,8 @@ exports[`PostOptions should match snapshot, no option for system message to user > #import +#import @interface MattermostManaged : RCTEventEmitter - (NSUserDefaults *)bucketByName:(NSString*)name; diff --git a/ios/Mattermost/MattermostManaged.m b/ios/Mattermost/MattermostManaged.m index 985b617ad..b3ad2a6cc 100644 --- a/ios/Mattermost/MattermostManaged.m +++ b/ios/Mattermost/MattermostManaged.m @@ -28,6 +28,49 @@ RCT_EXPORT_MODULE(); hasListeners = NO; } +- (BOOL)hasSafeAreaInsets { + UIView *rootView = nil; + + UIApplication *sharedApplication = RCTSharedApplication(); + if (sharedApplication) { + UIWindow *window = RCTSharedApplication().keyWindow; + if (window) { + UIViewController *rootViewController = window.rootViewController; + if (rootViewController) { + rootView = rootViewController.view; + } + } + } + + UIEdgeInsets safeAreaInsets = [self safeAreaInsetsForView:rootView]; + return safeAreaInsets.bottom > 0; +} + +- (UIEdgeInsets)safeAreaInsetsForView:(UIView *)view { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 + if (@available(iOS 11.0, *)) { + if (view) { + return view.safeAreaInsets; + } + } +#endif + + UIEdgeInsets safeAreaInsets = UIEdgeInsetsMake(0, 0, 0, 0); + + if (view) { + return safeAreaInsets; + } + + return safeAreaInsets; +} + +- (NSDictionary *)constantsToExport { + + return @{ + @"hasSafeAreaInsets": @([self hasSafeAreaInsets]), + }; +} + - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; @@ -115,7 +158,7 @@ RCT_EXPORT_METHOD(quitApp) return NO; } } - + return [super canPerformAction:action withSender:sender]; } diff --git a/test/setup.js b/test/setup.js index 5682cade5..2b5d84bfc 100644 --- a/test/setup.js +++ b/test/setup.js @@ -38,6 +38,7 @@ jest.mock('react-native-device-info', () => { getVersion: () => '0.0.0', getBuildNumber: () => '0', getModel: () => 'iPhone X', + isTablet: () => false, }; });