Fix iOS Share extension crash (#1832)
This commit is contained in:
parent
a208df9417
commit
5680988590
5 changed files with 82 additions and 24 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"];
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue