Fix iOS Share extension crash (#1832)

This commit is contained in:
Elias Nahum 2018-06-27 09:23:47 -04:00 committed by GitHub
parent a208df9417
commit 5680988590
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 82 additions and 24 deletions

View file

@ -2513,6 +2513,7 @@
"mobile.share_extension.error_close": "Close",
"mobile.share_extension.error_message": "An error has occurred while using the share extension.",
"mobile.share_extension.error_title": "Extension Error",
"mobile.share_extension.post_error": "An error has occurred while posting the message. Please try again.",
"mobile.share_extension.send": "Send",
"mobile.share_extension.team": "Team",
"mobile.suggestion.members": "Members",

View file

@ -3,6 +3,8 @@
@interface SessionManager : NSObject<NSURLSessionDelegate, NSURLSessionTaskDelegate>
@property (nonatomic, copy) void (^savedCompletionHandler)(void);
@property (nonatomic, copy) void (^sendShareEvent)(NSString *);
@property (nonatomic, copy) void (^closeExtension)(void);
@property MattermostBucket *bucket;

View file

@ -37,6 +37,7 @@
NSLog(@"ERROR %@", [error userInfo]);
NSLog(@"invalidating session %@", requestWithGroup);
[session invalidateAndCancel];
self.sendShareEvent(@"extensionPostFailed");
} else if (requestWithGroup != nil) {
NSString *appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
NSURL *requestUrl = [[task originalRequest] URL];
@ -80,6 +81,8 @@
}
[data setObject:fileIds forKey:@"file_ids"];
[self setDataForRequest:data forRequestWithGroup:requestWithGroup];
} else {
self.sendShareEvent(@"extensionPostFailed");
}
}
}
@ -124,6 +127,7 @@
NSString *appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
if (credentials == nil) {
self.sendShareEvent(@"extensionPostFailed");
return;
}
@ -164,6 +168,7 @@
NSDictionary *credentials = [self getCredentialsForRequest:requestWithGroup];
if (credentials == nil) {
self.sendShareEvent(@"extensionPostFailed");
return;
}
@ -196,6 +201,9 @@
NSURLSessionDataTask *createTask = [createSession dataTaskWithRequest:request];
NSLog(@"Executing post request");
[createTask resume];
self.closeExtension();
} else {
self.sendShareEvent(@"extensionPostFailed");
}
}

View file

@ -12,6 +12,8 @@ NSExtensionContext* extensionContext;
return YES;
}
@synthesize bridge = _bridge;
- (UIView*) shareView {
NSURL *jsCodeLocation;
@ -56,12 +58,23 @@ RCT_EXPORT_METHOD(close:(NSDictionary *)data appGroupId:(NSString *)appGroupId)
if (isBackgroundUpload) {
NSString *requestWithGroup = [NSString stringWithFormat:@"%@|%@", requestId, appGroupId];
[SessionManager sharedSession].closeExtension = ^{
[extensionContext completeRequestReturningItems:nil
completionHandler:nil];
NSLog(@"Extension closed");
};
[SessionManager sharedSession].sendShareEvent = ^(NSString* eventName) {
NSLog(@"Send Share Extension Event to JS");
[_bridge enqueueJSCall:@"RCTDeviceEventEmitter"
method:@"emit"
args:@[eventName]
completion:nil];
};
[[SessionManager sharedSession] setDataForRequest:data forRequestWithGroup:requestWithGroup];
[[SessionManager sharedSession] createPostForRequest:requestWithGroup];
[extensionContext completeRequestReturningItems:nil
completionHandler:nil];
NSLog(@"Extension closed");
} else {
NSDictionary *post = [data objectForKey:@"post"];
NSArray *files = [data objectForKey:@"files"];

View file

@ -1,11 +1,12 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {intlShape} from 'react-intl';
import {
ActivityIndicator,
DeviceEventEmitter,
Dimensions,
Image,
NativeModules,
@ -93,6 +94,7 @@ export default class ExtensionPost extends PureComponent {
}
componentWillMount() {
this.event = DeviceEventEmitter.addListener('extensionPostFailed', this.handlePostFailed);
this.loadData();
}
@ -100,6 +102,10 @@ export default class ExtensionPost extends PureComponent {
this.focusInput();
}
componentWillUnmount() {
this.event.remove();
}
componentDidUpdate() {
this.focusInput();
}
@ -141,6 +147,10 @@ export default class ExtensionPost extends PureComponent {
const {navigator, theme} = this.props;
const {channel, entities, team} = this.state;
if (!entities || !team || !channel) {
return;
}
navigator.push({
component: ExtensionChannels,
wrapperStyle: {
@ -161,7 +171,11 @@ export default class ExtensionPost extends PureComponent {
goToTeams = preventDoubleTap(() => {
const {navigator, theme} = this.props;
const {formatMessage} = this.context.intl;
const {team} = this.state;
const {entities, team} = this.state;
if (!entities || !team) {
return;
}
navigator.push({
component: ExtensionTeams,
@ -171,7 +185,7 @@ export default class ExtensionPost extends PureComponent {
backgroundColor: theme.centerChannelBg,
},
passProps: {
entities: this.state.entities,
entities,
currentTeamId: team.id,
onSelectTeam: this.selectTeam,
theme,
@ -187,6 +201,19 @@ export default class ExtensionPost extends PureComponent {
this.setState({value});
};
handlePostFailed = () => {
const {formatMessage} = this.context.intl;
this.setState({
error: {
message: formatMessage({
id: 'mobile.share_extension.post_error',
defaultMessage: 'An error has occurred while posting the message. Please try again.',
}),
},
sending: false,
});
};
loadData = async () => {
const {entities} = this.state;
@ -287,6 +314,16 @@ export default class ExtensionPost extends PureComponent {
const serverMaxFileSize = getAllowedServerMaxFileSize(config);
const maxSize = Math.min(MAX_FILE_SIZE, serverMaxFileSize);
if (error) {
return (
<View style={styles.unauthenticatedContainer}>
<Text style={styles.unauthenticated}>
{error.message}
</Text>
</View>
);
}
if (sending) {
return (
<View style={styles.sendingContainer}>
@ -336,16 +373,6 @@ export default class ExtensionPost extends PureComponent {
);
}
if (error) {
return (
<View style={styles.unauthenticatedContainer}>
<Text style={styles.unauthenticated}>
{error.message}
</Text>
</View>
);
}
return (
<View style={styles.unauthenticatedContainer}>
<Text style={styles.unauthenticated}>
@ -552,7 +579,7 @@ export default class ExtensionPost extends PureComponent {
this.setState({channel: townSquare});
}
} else {
this.setState({error});
this.setState({error, channel: null});
}
});
};
@ -627,15 +654,22 @@ export default class ExtensionPost extends PureComponent {
render() {
const {authenticated, theme} = this.props;
const {channel, totalSize, sending} = this.state;
const {channel, error, totalSize, sending} = this.state;
const {formatMessage} = this.context.intl;
const styles = getStyleSheet(theme);
let postButtonText = formatMessage({id: 'mobile.share_extension.send', defaultMessage: 'Send'});
if (totalSize >= MAX_FILE_SIZE || sending || !channel) {
if (totalSize >= MAX_FILE_SIZE || sending || error || !channel) {
postButtonText = null;
}
let cancelButton = formatMessage({id: 'mobile.share_extension.cancel', defaultMessage: 'Cancel'});
if (sending) {
cancelButton = null;
} else if (error) {
cancelButton = formatMessage({id: 'mobile.share_extension.error_close', defaultMessage: 'Close'});
}
return (
<View
onLayout={this.onLayout}
@ -643,15 +677,15 @@ export default class ExtensionPost extends PureComponent {
>
<ExtensionNavBar
authenticated={authenticated}
leftButtonTitle={sending ? null : formatMessage({id: 'mobile.share_extension.cancel', defaultMessage: 'Cancel'})}
leftButtonTitle={cancelButton}
onLeftButtonPress={this.handleCancel}
onRightButtonPress={this.sendMessage}
rightButtonTitle={postButtonText}
theme={theme}
/>
{this.renderBody(styles)}
{this.renderTeamButton(styles)}
{this.renderChannelButton(styles)}
{!error && this.renderTeamButton(styles)}
{!error && this.renderChannelButton(styles)}
</View>
);
}