diff --git a/Makefile b/Makefile index 3b1589787..db3d6cf52 100644 --- a/Makefile +++ b/Makefile @@ -77,9 +77,12 @@ post-install: @# Need to copy custom RNDocumentPicker.m that implements direct access to the document picker in iOS @cp ./native_modules/RNDocumentPicker.m node_modules/react-native-document-picker/ios/RNDocumentPicker/RNDocumentPicker.m - # Need to copy custom RNCookieManagerIOS.m that fixes a crash when cookies does not have expiration date set + @# Need to copy custom RNCookieManagerIOS.m that fixes a crash when cookies does not have expiration date set @cp ./native_modules/RNCookieManagerIOS.m node_modules/react-native-cookies/ios/RNCookieManagerIOS/RNCookieManagerIOS.m + @# Need to copy custom RNCNetInfo.m that checks for internet connectivity instead of reaching a host by default + @cp ./native_modules/RNCNetInfo.m node_modules/@react-native-community/netinfo/ios/RNCNetInfo.m + @rm -f node_modules/intl/.babelrc @# Hack to get react-intl and its dependencies to work with react-native @# Based off of https://github.com/este/este/blob/master/gulp/native-fix.js diff --git a/android/app/build.gradle b/android/app/build.gradle index 9ab282910..148f8a12a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -122,7 +122,7 @@ android { applicationId "com.mattermost.rnbeta" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 195 + versionCode 196 versionName "1.20.0" multiDexEnabled = true ndk { diff --git a/android/app/google-services.json b/android/app/google-services.json index 9f083952d..74d308939 100644 --- a/android/app/google-services.json +++ b/android/app/google-services.json @@ -26,7 +26,7 @@ ], "services": { "analytics_service": { - "status": 1 + "status": 0 }, "appinvite_service": { "status": 1, @@ -57,7 +57,7 @@ ], "services": { "analytics_service": { - "status": 1 + "status": 0 }, "appinvite_service": { "status": 1, @@ -88,7 +88,7 @@ ], "services": { "analytics_service": { - "status": 1 + "status": 0 }, "appinvite_service": { "status": 1, @@ -101,4 +101,4 @@ } ], "configuration_version": "1" -} \ No newline at end of file +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 54cfc1fcf..e3d966fce 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -19,6 +19,7 @@ android:installLocation="auto" android:networkSecurityConfig="@xml/network_security_config" > + diff --git a/app/components/network_indicator/network_indicator.js b/app/components/network_indicator/network_indicator.js index 62b340117..71a40b28a 100644 --- a/app/components/network_indicator/network_indicator.js +++ b/app/components/network_indicator/network_indicator.js @@ -19,7 +19,6 @@ 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'; @@ -36,7 +35,6 @@ const { IOS_TOP_LANDSCAPE, IOS_TOP_PORTRAIT, IOSX_TOP_PORTRAIT, - STATUS_BAR_HEIGHT, } = ViewTypes; export default class NetworkIndicator extends PureComponent { @@ -142,7 +140,7 @@ export default class NetworkIndicator extends PureComponent { const {connection} = this.props.actions; clearTimeout(this.connectionRetryTimeout); - NetInfo.isConnected.fetch().then(async (isConnected) => { + NetInfo.fetch().then(async ({isConnected}) => { const {hasInternet, serverReachable} = await checkConnection(isConnected); connection(hasInternet); @@ -204,9 +202,7 @@ 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) { + } else if (isLandscape && !DeviceTypes.IS_TABLET) { return IOS_TOP_LANDSCAPE; } @@ -233,7 +229,6 @@ export default class NetworkIndicator extends PureComponent { handleAppStateChange = async (appState) => { const {actions, currentChannelId} = this.props; const active = appState === 'active'; - if (active) { this.connect(true); diff --git a/app/components/sidebars/settings/settings_sidebar.js b/app/components/sidebars/settings/settings_sidebar.js index ffade1249..9d552978b 100644 --- a/app/components/sidebars/settings/settings_sidebar.js +++ b/app/components/sidebars/settings/settings_sidebar.js @@ -153,11 +153,12 @@ export default class SettingsDrawer extends PureComponent { goToEditProfile = preventDoubleTap(() => { const {currentUser} = this.props; const {formatMessage} = this.context.intl; + const commandType = 'ShowModal'; this.openModal( 'EditProfile', formatMessage({id: 'mobile.routes.edit_profile', defaultMessage: 'Edit Profile'}), - {currentUser} + {currentUser, commandType} ); }); @@ -179,6 +180,17 @@ export default class SettingsDrawer extends PureComponent { ); }); + goToUserProfile = preventDoubleTap(() => { + const userId = this.props.currentUser.id; + const {formatMessage} = this.context.intl; + + this.openModal( + 'UserProfile', + formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}), + {userId, fromSettings: true} + ); + }); + goToSettings = preventDoubleTap(() => { const {intl} = this.context; @@ -258,7 +270,7 @@ export default class SettingsDrawer extends PureComponent { contentContainerStyle={style.wrapper} > @@ -293,6 +305,15 @@ export default class SettingsDrawer extends PureComponent { + { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); + if (this.props.commandType === 'Push') { + this.props.navigator.pop(); + } else { + this.props.navigator.dismissModal({ + animationType: 'slide-down', + }); + } }; emitCanUpdateAccount = (enabled) => { diff --git a/app/screens/edit_profile/edit_profile.test.js b/app/screens/edit_profile/edit_profile.test.js index 249fedf10..69682f745 100644 --- a/app/screens/edit_profile/edit_profile.test.js +++ b/app/screens/edit_profile/edit_profile.test.js @@ -45,6 +45,7 @@ describe('edit_profile', () => { nickname: 'Dragon', position: 'position', }, + commandType: 'ShowModal', }; test('should match snapshot', async () => { diff --git a/app/screens/user_profile/index.js b/app/screens/user_profile/index.js index a9d6ba836..27b466ed1 100644 --- a/app/screens/user_profile/index.js +++ b/app/screens/user_profile/index.js @@ -12,6 +12,7 @@ import {getConfig} from 'mattermost-redux/selectors/entities/general'; import Preferences from 'mattermost-redux/constants/preferences'; import {loadBot} from 'mattermost-redux/actions/bots'; import {getBotAccounts} from 'mattermost-redux/selectors/entities/bots'; +import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; import {isTimezoneEnabled} from 'app/utils/timezone'; @@ -33,6 +34,7 @@ function mapStateToProps(state, ownProps) { enableTimezone, militaryTime, theme: getTheme(state), + isMyUser: getCurrentUserId(state) === ownProps.userId, }; } diff --git a/app/screens/user_profile/user_profile.js b/app/screens/user_profile/user_profile.js index 9a6ea0e66..f1bf1ba34 100644 --- a/app/screens/user_profile/user_profile.js +++ b/app/screens/user_profile/user_profile.js @@ -42,12 +42,34 @@ export default class UserProfile extends PureComponent { bot: PropTypes.object, militaryTime: PropTypes.bool.isRequired, enableTimezone: PropTypes.bool.isRequired, + isMyUser: PropTypes.bool.isRequired, + fromSettings: PropTypes.bool, }; static contextTypes = { intl: intlShape.isRequired, }; + rightButton = { + id: 'edit-profile', + showAsAction: 'always', + }; + + constructor(props, context) { + super(props); + + if (props.isMyUser) { + this.rightButton.title = context.intl.formatMessage({id: 'mobile.routes.user_profile.edit', defaultMessage: 'Edit'}); + + const buttons = { + rightButtons: [this.rightButton], + }; + + props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); + props.navigator.setButtons(buttons); + } + } + componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { setNavigatorStyles(this.props.navigator, nextProps.theme); @@ -63,6 +85,13 @@ export default class UserProfile extends PureComponent { close = () => { const {navigator, theme} = this.props; + if (this.props.fromSettings) { + navigator.dismissModal({ + animationType: 'slide-down', + }); + return; + } + navigator.resetTo({ screen: 'Channel', animated: true, @@ -187,6 +216,44 @@ export default class UserProfile extends PureComponent { }; }; + goToEditProfile = () => { + const {user: currentUser} = this.props; + const {formatMessage} = this.context.intl; + const commandType = 'Push'; + + const {navigator, theme} = this.props; + const options = { + screen: 'EditProfile', + title: formatMessage({id: 'mobile.routes.edit_profile', defaultMessage: 'Edit Profile'}), + animated: true, + backButtonTitle: '', + passProps: {currentUser, commandType}, + navigatorStyle: { + navBarTextColor: theme.sidebarHeaderTextColor, + navBarBackgroundColor: theme.sidebarHeaderBg, + navBarButtonColor: theme.sidebarHeaderTextColor, + screenBackgroundColor: theme.centerChannelBg, + }, + }; + + requestAnimationFrame(() => { + navigator.push(options); + }); + }; + + onNavigatorEvent = (event) => { + if (event.type === 'NavBarButtonPress') { + switch (event.id) { + case this.rightButton.id: + this.goToEditProfile(); + break; + case 'close-settings': + this.close(); + break; + } + } + }; + renderAdditionalOptions = () => { if (!Config.ExperimentalProfileLinks) { return null; diff --git a/app/screens/user_profile/user_profile.test.js b/app/screens/user_profile/user_profile.test.js index 6b61fe051..e1dac798e 100644 --- a/app/screens/user_profile/user_profile.test.js +++ b/app/screens/user_profile/user_profile.test.js @@ -31,11 +31,14 @@ describe('user_profile', () => { teammateNameDisplay: 'username', navigator: { resetTo: jest.fn(), + push: jest.fn(), + dismissModal: jest.fn(), }, teams: [], theme: Preferences.THEMES.default, enableTimezone: false, militaryTime: false, + isMyUser: false, }; const user = { @@ -84,4 +87,70 @@ describe('user_profile', () => { /> )).toEqual(true); }); + + test('should push EditProfile', async () => { + const props = { + ...baseProps, + navigator: { + push: jest.fn(), + }, + }; + + const wrapper = shallow( + , + {context: {intl: {formatMessage: jest.fn()}}}, + ); + + wrapper.instance().goToEditProfile(); + setTimeout(() => { + expect(props.navigator.push).toHaveBeenCalledTimes(1); + }, 16); + }); + + test('should call goToEditProfile', () => { + const props = { + ...baseProps, + navigator: { + push: jest.fn(), + }, + }; + + const wrapper = shallow( + , + {context: {intl: {formatMessage: jest.fn()}}}, + ); + + const event = {type: 'NavBarButtonPress', id: wrapper.instance().rightButton.id}; + wrapper.instance().onNavigatorEvent(event); + setTimeout(() => { + expect(props.navigator.push).toHaveBeenCalledTimes(1); + }, 0); + }); + + test('should close', async () => { + const props = {...baseProps, fromSettings: true}; + + const wrapper = shallow( + , + {context: {intl: {formatMessage: jest.fn()}}}, + ); + + const event = {type: 'NavBarButtonPress', id: 'close-settings'}; + wrapper.instance().onNavigatorEvent(event); + expect(props.navigator.dismissModal).toHaveBeenCalledTimes(1); + + props.fromSettings = false; + wrapper.setProps({...props}); + wrapper.instance().onNavigatorEvent(event); + expect(props.navigator.resetTo).toHaveBeenCalledTimes(1); + }); }); diff --git a/app/telemetry/telemetry.android.js b/app/telemetry/telemetry.android.js index 40eb56c58..cd6dd4fc4 100644 --- a/app/telemetry/telemetry.android.js +++ b/app/telemetry/telemetry.android.js @@ -146,7 +146,7 @@ class Telemetry { const {config} = store.getState().entities.general; const deviceInfo = getDeviceInfo(); - deviceInfo.serverVersion = config.Version; + deviceInfo.server_version = config.Version; saveToTelemetryServer({trace_events: metrics, device_info: deviceInfo}); diff --git a/app/utils/network.js b/app/utils/network.js index a441f12d9..0502c3087 100644 --- a/app/utils/network.js +++ b/app/utils/network.js @@ -13,6 +13,10 @@ import mattermostManaged from 'app/mattermost_managed'; let certificate = ''; let previousState; export async function checkConnection(isConnected) { + if (!isConnected) { + return {hasInternet: false, serverReachable: false}; + } + if (!Client4.getBaseRoute().startsWith('http')) { // If we don't have a connection or have a server yet, return the default implementation return {hasInternet: isConnected, serverReachable: false}; @@ -43,6 +47,7 @@ export async function checkConnection(isConnected) { auto: true, waitsForConnectivity, timeoutIntervalForResource, + timeout: 3000, }; if (Platform.OS === 'ios' && certificate === '') { @@ -59,7 +64,7 @@ export async function checkConnection(isConnected) { } function handleConnectionChange(onChange) { - return async (isConnected) => { + return async ({isConnected}) => { if (isConnected !== previousState) { previousState = isConnected; @@ -72,13 +77,7 @@ function handleConnectionChange(onChange) { export default function networkConnectionListener(onChange) { const connectionChanged = handleConnectionChange(onChange); - - NetInfo.isConnected.fetch().then((isConnected) => { - NetInfo.isConnected.addEventListener('connectionChange', connectionChanged); - connectionChanged(isConnected); - }); - - const removeEventListener = () => NetInfo.isConnected.removeEventListener('connectionChange', connectionChanged); + const removeEventListener = NetInfo.addEventListener(connectionChanged); return { removeEventListener, diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 470c82a44..7f8cbd8c3 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -376,8 +376,8 @@ platform :android do lane :build do unless configured configure - configure_telemetry_android end + configure_telemetry_android update_identifiers replace_assets link_sentry_android diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj index 3832c6925..21d885d96 100644 --- a/ios/Mattermost.xcodeproj/project.pbxproj +++ b/ios/Mattermost.xcodeproj/project.pbxproj @@ -2779,7 +2779,7 @@ CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 195; + CURRENT_PROJECT_VERSION = 196; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = UQ8HT4Q2XM; ENABLE_BITCODE = NO; @@ -2839,7 +2839,7 @@ CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 195; + CURRENT_PROJECT_VERSION = 196; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = UQ8HT4Q2XM; ENABLE_BITCODE = NO; diff --git a/ios/Mattermost/Info.plist b/ios/Mattermost/Info.plist index 4626b3262..df2f9b159 100644 --- a/ios/Mattermost/Info.plist +++ b/ios/Mattermost/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 195 + 196 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/ios/Mattermost/MattermostManaged.m b/ios/Mattermost/MattermostManaged.m index 4c261b3f5..4000f5902 100644 --- a/ios/Mattermost/MattermostManaged.m +++ b/ios/Mattermost/MattermostManaged.m @@ -8,7 +8,7 @@ #import "RCTUITextView.h" #import "MattermostManaged.h" -#import +#import @implementation MattermostManaged { bool hasListeners; diff --git a/ios/MattermostShare/Info.plist b/ios/MattermostShare/Info.plist index 8c275ebb6..abe8f9077 100644 --- a/ios/MattermostShare/Info.plist +++ b/ios/MattermostShare/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.20.0 CFBundleVersion - 195 + 196 NSAppTransportSecurity NSAllowsArbitraryLoads diff --git a/ios/MattermostTests/Info.plist b/ios/MattermostTests/Info.plist index b4bafc524..e92ca45f5 100644 --- a/ios/MattermostTests/Info.plist +++ b/ios/MattermostTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 195 + 196 diff --git a/ios/NotificationService/Info.plist b/ios/NotificationService/Info.plist index e19127bee..5b9061c05 100644 --- a/ios/NotificationService/Info.plist +++ b/ios/NotificationService/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.20.0 CFBundleVersion - 195 + 196 NSExtension NSExtensionPointIdentifier diff --git a/native_modules/RNCNetInfo.m b/native_modules/RNCNetInfo.m new file mode 100644 index 000000000..0985fec2b --- /dev/null +++ b/native_modules/RNCNetInfo.m @@ -0,0 +1,228 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RNCNetInfo.h" + +#if !TARGET_OS_TV + #import +#endif +#import +#import +#import + +// Start of the Mattermost fix +#import +// End of the Mattermost fix + +// Based on the ConnectionType enum described in the W3C Network Information API spec +// (https://wicg.github.io/netinfo/). +static NSString *const RNCConnectionTypeUnknown = @"unknown"; +static NSString *const RNCConnectionTypeNone = @"none"; +static NSString *const RNCConnectionTypeWifi = @"wifi"; +static NSString *const RNCConnectionTypeCellular = @"cellular"; + +// Based on the EffectiveConnectionType enum described in the W3C Network Information API spec +// (https://wicg.github.io/netinfo/). +static NSString *const RNCCellularGeneration2g = @"2g"; +static NSString *const RNCCellularGeneration3g = @"3g"; +static NSString *const RNCCellularGeneration4g = @"4g"; + +@implementation RNCNetInfo +{ + SCNetworkReachabilityRef _firstTimeReachability; + SCNetworkReachabilityRef _reachability; + NSString *_connectionType; + BOOL _connectionExpensive; + NSString *_effectiveConnectionType; + BOOL _isObserving; + NSMutableSet *_firstTimeReachabilityResolvers; +} + +RCT_EXPORT_MODULE() + +static void RNCReachabilityCallback(__unused SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) +{ + RNCNetInfo *self = (__bridge id)info; + BOOL didSetReachabilityFlags = [self setReachabilityStatus:flags]; + + if (self->_firstTimeReachability) { + [self->_firstTimeReachabilityResolvers enumerateObjectsUsingBlock:^(RCTPromiseResolveBlock resolver, BOOL *stop) { + resolver([self currentState]); + }]; + + [self cleanUpFirstTimeReachability]; + [self->_firstTimeReachabilityResolvers removeAllObjects]; + } + + if (didSetReachabilityFlags && self->_isObserving) { + [self sendEventWithName:@"netInfo.networkStatusDidChange" body:[self currentState]]; + } +} + +// We need RNCReachabilityCallback's and module methods to be called on the same thread so that we can have +// guarantees about when we mess with the reachability callbacks. +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + +#pragma mark - Lifecycle + +- (NSArray *)supportedEvents +{ + return @[@"netInfo.networkStatusDidChange"]; +} + +- (void)startObserving +{ + _isObserving = YES; + _connectionType = RNCConnectionTypeUnknown; + _effectiveConnectionType = nil; + _reachability = [self getReachabilityRef]; +} + +- (void)stopObserving +{ + _isObserving = NO; + if (_reachability) { + SCNetworkReachabilityUnscheduleFromRunLoop(_reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); + CFRelease(_reachability); + } +} + +- (void)dealloc +{ + [self cleanUpFirstTimeReachability]; +} + +- (SCNetworkReachabilityRef)getReachabilityRef +{ + // Start of the Mattermost fix + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *) &zeroAddress); + // End of the Mattermost fix + SCNetworkReachabilityContext context = { 0, ( __bridge void *)self, NULL, NULL, NULL }; + SCNetworkReachabilitySetCallback(reachability, RNCReachabilityCallback, &context); + SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); + + return reachability; +} + +- (BOOL)setReachabilityStatus:(SCNetworkReachabilityFlags)flags +{ + NSString *connectionType = RNCConnectionTypeUnknown; + bool connectionExpensive = false; + NSString *effectiveConnectionType = nil; + if ((flags & kSCNetworkReachabilityFlagsReachable) == 0 || + (flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0) { + connectionType = RNCConnectionTypeNone; + } + +#if !TARGET_OS_TV + + else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) { + connectionType = RNCConnectionTypeCellular; + connectionExpensive = true; + + CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init]; + if (netinfo) { + if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS] || + [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge] || + [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMA1x]) { + effectiveConnectionType = RNCCellularGeneration2g; + } else if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyWCDMA] || + [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSDPA] || + [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSUPA] || + [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0] || + [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA] || + [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB] || + [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyeHRPD]) { + effectiveConnectionType = RNCCellularGeneration3g; + } else if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) { + effectiveConnectionType = RNCCellularGeneration4g; + } + } + } + +#endif + + else { + connectionType = RNCConnectionTypeWifi; + } + + if (![connectionType isEqualToString:self->_connectionType] || + ![effectiveConnectionType isEqualToString:self->_effectiveConnectionType]) { + self->_connectionType = connectionType; + self->_connectionExpensive = connectionExpensive; + self->_effectiveConnectionType = effectiveConnectionType; + return YES; + } + + return NO; +} + +- (void)cleanUpFirstTimeReachability +{ + if (_firstTimeReachability) { + SCNetworkReachabilityUnscheduleFromRunLoop(self->_firstTimeReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); + CFRelease(self->_firstTimeReachability); + _firstTimeReachability = nil; + } +} + +- (id)currentState +{ + NSString *connectionType = self->_connectionType ?: RNCConnectionTypeUnknown; + NSString *effectiveConnectionType = self->_effectiveConnectionType; + + BOOL isConnected = ![connectionType isEqualToString:RNCConnectionTypeNone] && ![connectionType isEqualToString:RNCConnectionTypeUnknown]; + + NSMutableDictionary *details = nil; + if (isConnected) { + details = [NSMutableDictionary new]; + details[@"isConnectionExpensive"] = @(self->_connectionExpensive ?: false); + + if ([connectionType isEqualToString:RNCConnectionTypeCellular]) { + details[@"cellularGeneration"] = effectiveConnectionType ?: [NSNull null]; + } + } + + return @{ + @"type": connectionType, + @"isConnected": @(isConnected), + @"details": details ?: [NSNull null] + }; +} + +#pragma mark - Public API + +RCT_EXPORT_METHOD(getCurrentState:(RCTPromiseResolveBlock)resolve + reject:(__unused RCTPromiseRejectBlock)reject) +{ + // Setup the reacability listener if needed + if (!_firstTimeReachability) { + _firstTimeReachability = [self getReachabilityRef]; + } + + // Start of the Mattermost fix + if (_firstTimeReachability) { + SCNetworkReachabilityFlags flags; + SCNetworkReachabilityGetFlags(_firstTimeReachability, &flags); + [self setReachabilityStatus:flags]; + + resolve([self currentState]); + + [self cleanUpFirstTimeReachability]; + } + // End of the Mattermost fix +} + +@end diff --git a/package-lock.json b/package-lock.json index 74ecbe0bf..a5a97e4f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3625,9 +3625,9 @@ } }, "@react-native-community/netinfo": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-2.0.10.tgz", - "integrity": "sha512-NrIzyLe0eSbhgMnHl2QdSEhaA7yXh6p9jzMomfUa//hoTXE+xbObGDdiWWSQm2bnXnZJg8XCU3AB9qzvqcuLnA==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-3.1.3.tgz", + "integrity": "sha512-T9fFuxnMSZdVtpygW3BdmoE2o70BXrryi95Xi7FnRYmdr7PtEjNxEziR8i+VvrXXNG8KkQ2Bngc0YUqrNxNMtA==" }, "@react-navigation/core": { "version": "3.4.1", @@ -4932,9 +4932,9 @@ "optional": true }, "bluebird": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", - "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", "dev": true }, "boolbase": { @@ -5238,7 +5238,6 @@ "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" } @@ -5283,8 +5282,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "optional": true + "dev": true }, "is-glob": { "version": "4.0.1", @@ -5471,8 +5469,8 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" }, "commonmark": { - "version": "github:mattermost/commonmark.js#6c3136c18ae8bb7842f8491d160f370a334fd903", - "from": "github:mattermost/commonmark.js#6c3136c18ae8bb7842f8491d160f370a334fd903", + "version": "github:mattermost/commonmark.js#4224e725f14b5d4e340a62d7e56d5ad451e9f8cf", + "from": "github:mattermost/commonmark.js#4224e725f14b5d4e340a62d7e56d5ad451e9f8cf", "requires": { "entities": "~ 1.1.1", "mdurl": "~ 1.0.1", @@ -7365,27 +7363,6 @@ } } }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", @@ -7692,8 +7669,12 @@ "ansi-regex": { "version": "2.1.1", "resolved": false, +<<<<<<< HEAD "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "optional": true +======= + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" +>>>>>>> master }, "aproba": { "version": "1.2.0", @@ -7714,14 +7695,17 @@ "balanced-match": { "version": "1.0.0", "resolved": false, +<<<<<<< HEAD "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "optional": true +======= + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" +>>>>>>> master }, "brace-expansion": { "version": "1.1.11", "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7736,20 +7720,32 @@ "code-point-at": { "version": "1.1.0", "resolved": false, +<<<<<<< HEAD "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "optional": true +======= + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" +>>>>>>> master }, "concat-map": { "version": "0.0.1", "resolved": false, +<<<<<<< HEAD "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "optional": true +======= + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" +>>>>>>> master }, "console-control-strings": { "version": "1.1.0", "resolved": false, +<<<<<<< HEAD "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "optional": true +======= + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" +>>>>>>> master }, "core-util-is": { "version": "1.0.2", @@ -7866,8 +7862,12 @@ "inherits": { "version": "2.0.3", "resolved": false, +<<<<<<< HEAD "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "optional": true +======= + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" +>>>>>>> master }, "ini": { "version": "1.3.5", @@ -7879,7 +7879,6 @@ "version": "1.0.0", "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7894,7 +7893,6 @@ "version": "3.0.4", "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -7902,14 +7900,17 @@ "minimist": { "version": "0.0.8", "resolved": false, +<<<<<<< HEAD "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "optional": true +======= + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" +>>>>>>> master }, "minipass": { "version": "2.3.5", "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -7928,7 +7929,6 @@ "version": "0.5.1", "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "optional": true, "requires": { "minimist": "0.0.8" } @@ -8009,8 +8009,12 @@ "number-is-nan": { "version": "1.0.1", "resolved": false, +<<<<<<< HEAD "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "optional": true +======= + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" +>>>>>>> master }, "object-assign": { "version": "4.1.1", @@ -8022,7 +8026,6 @@ "version": "1.4.0", "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "optional": true, "requires": { "wrappy": "1" } @@ -8108,8 +8111,12 @@ "safe-buffer": { "version": "5.1.2", "resolved": false, +<<<<<<< HEAD "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "optional": true +======= + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" +>>>>>>> master }, "safer-buffer": { "version": "2.1.2", @@ -8145,7 +8152,6 @@ "version": "1.0.2", "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8165,7 +8171,6 @@ "version": "3.0.1", "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -8209,14 +8214,22 @@ "wrappy": { "version": "1.0.2", "resolved": false, +<<<<<<< HEAD "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "optional": true +======= + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" +>>>>>>> master }, "yallist": { "version": "3.0.3", "resolved": false, +<<<<<<< HEAD "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "optional": true +======= + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" +>>>>>>> master } } }, @@ -13202,9 +13215,9 @@ "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-4.9.1.tgz", "integrity": "sha512-XoOmfPyo9GEU/WLH9FgB47dNIN9l5ArjHes4o7vUWx9nxZoPxnVodhuHdyc4Ot+fMkdj3L2LTqSHhwrkr0QFUg==", "requires": { - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.4", - "lodash-es": "^4.17.4" + "json-stringify-safe": "5.0.1", + "lodash": "4.17.11", + "lodash-es": "4.17.11" } } } @@ -13718,7 +13731,7 @@ }, "mmjstool": { "version": "github:mattermost/mattermost-utilities#b55348242168df75da500fd813d4105c44eaea08", - "from": "github:mattermost/mattermost-utilities#b55348242168df75da500fd813d4105c44eaea08", + "from": "github:mattermost/mattermost-utilities", "dev": true, "requires": { "estree-walk": "2.2.0", @@ -16076,8 +16089,7 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true + "dev": true }, "braces": { "version": "2.3.2", @@ -16340,8 +16352,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true, - "optional": true + "dev": true }, "micromatch": { "version": "3.1.10", @@ -18128,9 +18139,9 @@ } }, "tapable": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", - "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true }, "temp": { @@ -18689,9 +18700,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "v8-compile-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz", - "integrity": "sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", + "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", "dev": true }, "validate-npm-package-license": { @@ -18752,9 +18763,9 @@ "dev": true }, "webpack-cli": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.0.tgz", - "integrity": "sha512-t1M7G4z5FhHKJ92WRKwZ1rtvi7rHc0NZoZRbSkol0YKl4HvcC8+DsmGDmK7MmZxHSAetHagiOsjOB6MmzC2TUw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.2.tgz", + "integrity": "sha512-FLkobnaJJ+03j5eplxlI0TUxhGCOdfewspIGuvDVtpOlrAuKMFC57K42Ukxqs1tn8947/PM6tP95gQc0DCzRYA==", "dev": true, "requires": { "chalk": "^2.4.1", @@ -18776,32 +18787,12 @@ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, "cliui": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", @@ -18824,14 +18815,6 @@ "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - } } }, "find-up": { @@ -18920,6 +18903,12 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", diff --git a/package.json b/package.json index 11abbc33b..6f4becfd7 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,9 @@ "dependencies": { "@babel/runtime": "7.4.4", "@react-native-community/async-storage": "1.4.0", - "@react-native-community/netinfo": "2.0.10", + "@react-native-community/netinfo": "3.1.3", "analytics-react-native": "1.2.0", - "commonmark": "github:mattermost/commonmark.js#6c3136c18ae8bb7842f8491d160f370a334fd903", + "commonmark": "github:mattermost/commonmark.js#4224e725f14b5d4e340a62d7e56d5ad451e9f8cf", "commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#3a2ac19cab725ad28b170fdc1d397dddedcf87eb", "deep-equal": "1.0.1", "emoji-regex": "8.0.0", @@ -97,7 +97,7 @@ "jest-enzyme": "7.0.2", "jsdom-global": "3.0.2", "metro-react-native-babel-preset": "0.54.0", - "mmjstool": "github:mattermost/mattermost-utilities#b55348242168df75da500fd813d4105c44eaea08", + "mmjstool": "github:mattermost/mattermost-utilities", "nyc": "14.1.1", "react-dom": "16.8.6", "react-test-renderer": "16.8.6",