Merge pull request #1847 from mattermost/merge-1.9

Merge release 1.9 into master
This commit is contained in:
Elias Nahum 2018-06-27 18:34:04 -04:00 committed by GitHub
commit 188e6d2b46
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 160 additions and 67 deletions

View file

@ -113,7 +113,7 @@ android {
applicationId "com.mattermost.rnbeta"
minSdkVersion 21
targetSdkVersion 23
versionCode 115
versionCode 117
versionName "1.9.2"
ndk {
abiFilters "armeabi-v7a", "x86"

View file

@ -10,6 +10,8 @@ import android.content.ContentUris;
import android.content.ContentResolver;
import android.os.Environment;
import android.webkit.MimeTypeMap;
import android.util.Log;
import android.text.TextUtils;
import android.os.ParcelFileDescriptor;
import java.io.*;
@ -37,10 +39,19 @@ public class RealPathUtil {
// DownloadsProvider
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
if (!TextUtils.isEmpty(id)) {
if (id.startsWith("raw:")) {
return id.replaceFirst("raw:", "");
}
try {
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
} catch (NumberFormatException e) {
Log.e("ReactNative", "DownloadsProvider unexpected uri " + uri.toString());
return null;
}
}
} else if (isMediaDocument(uri)) {
// MediaProvider

View file

@ -147,6 +147,13 @@ public class ShareModule extends ReactContextBaseJavaModule {
Uri uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
text = "file://" + RealPathUtil.getRealPathFromURI(currentActivity, uri);
map.putString("value", text);
if (type.equals("image/*")) {
type = "image/jpeg";
} else if (type.equals("video/*")) {
type = "video/mp4";
}
map.putString("type", type);
items.pushMap(map);
} else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
@ -156,7 +163,15 @@ public class ShareModule extends ReactContextBaseJavaModule {
map = Arguments.createMap();
text = "file://" + filePath;
map.putString("value", text);
map.putString("type", RealPathUtil.getMimeTypeFromUri(currentActivity, uri));
type = RealPathUtil.getMimeTypeFromUri(currentActivity, uri);
if (type.equals("image/*")) {
type = "image/jpeg";
} else if (type.equals("video/*")) {
type = "video/mp4";
}
map.putString("type", type);
items.pushMap(map);
}
}

View file

@ -10,6 +10,8 @@ import {
StyleSheet,
} from 'react-native';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import Post from 'app/components/post';
import {DATE_LINE, START_OF_NEW_MESSAGES} from 'app/selectors/post_list';
import mattermostManaged from 'app/mattermost_managed';
@ -77,6 +79,7 @@ export default class PostList extends PureComponent {
}
componentDidMount() {
EventEmitter.on('reset_channel', this.scrollToBottomOffset);
this.setManagedConfig();
}
@ -98,6 +101,7 @@ export default class PostList extends PureComponent {
}
componentWillUnmount() {
EventEmitter.off('reset_channel', this.scrollToBottomOffset);
mattermostManaged.removeEventListener(this.listenerId);
}

View file

@ -14,6 +14,7 @@ import * as Animatable from 'react-native-animatable';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import {General} from 'mattermost-redux/constants';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import FormattedText from 'app/components/formatted_text';
import Loading from 'app/components/loading';
@ -200,22 +201,28 @@ export default class Permalink extends PureComponent {
actions.selectPost('');
if (channelId === currentChannelId) {
EventEmitter.emit('reset_channel');
} else {
navigator.resetTo({
screen: 'Channel',
animated: true,
animationType: 'fade',
navigatorStyle: {
navBarHidden: true,
statusBarHidden: false,
statusBarHideWithNavBar: false,
screenBackgroundColor: theme.centerChannelBg,
},
});
}
navigator.dismissAllModals({animationType: 'slide-down'});
if (onClose) {
onClose();
}
navigator.resetTo({
screen: 'Channel',
animated: true,
animationType: 'fade',
navigatorStyle: {
navBarHidden: true,
statusBarHidden: false,
statusBarHideWithNavBar: false,
screenBackgroundColor: theme.centerChannelBg,
},
});
if (channelTeamId && currentTeamId !== channelTeamId) {
handleTeamChange(channelTeamId, false);
}

View file

@ -2587,6 +2587,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

@ -2533,7 +2533,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 115;
CURRENT_PROJECT_VERSION = 117;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;
@ -2583,7 +2583,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 115;
CURRENT_PROJECT_VERSION = 117;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;

View file

@ -34,7 +34,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>115</string>
<string>117</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>

View file

@ -23,7 +23,7 @@
<key>CFBundleShortVersionString</key>
<string>1.9.2</string>
<key>CFBundleVersion</key>
<string>115</string>
<string>117</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>

View file

@ -4,6 +4,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;
@property (nonatomic, retain) KeyChainDataSource *keyChain;
@property (nonatomic, strong) NSString *requestWithGroup;

View file

@ -78,16 +78,16 @@
NSString *channelId = [post objectForKey:@"channel_id"];
NSURLSession *session = [self createSessionForRequestRequest:requestWithGroup];
NSString *appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
for (id file in files) {
NSURL *filePath = [NSURL fileURLWithPath:[file objectForKey:@"filePath"]];
NSString *fileName = [file objectForKey:@"filename"];
NSError *err;
NSURL *tempContainerURL = [self tempContainerURL:appGroupId];
NSURL *destinationURL = [tempContainerURL URLByAppendingPathComponent: fileName];
BOOL bVal = [[NSFileManager defaultManager] copyItemAtURL:filePath toURL:destinationURL error:&err];
NSCharacterSet *allowedCharacters = [NSCharacterSet URLQueryAllowedCharacterSet];
NSString *encodedFilename = [[file objectForKey:@"filename"] stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters];
NSString *url = [serverUrl stringByAppendingString:@"/api/v4/files"];
@ -97,7 +97,7 @@
[uploadRequest setHTTPMethod:@"POST"];
[uploadRequest setValue:[@"Bearer " stringByAppendingString:token] forHTTPHeaderField:@"Authorization"];
[uploadRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"];
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:uploadRequest fromFile:destinationURL];
NSLog(@"Executing file request %@", fileName);
[task resume];
@ -113,7 +113,7 @@
if (fileIds != nil && [fileIds count] > 0) {
[post setObject:fileIds forKey:@"file_ids"];
}
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:post options:NSJSONWritingPrettyPrinted error:&error];
@ -134,6 +134,9 @@
NSURLSessionDataTask *createTask = [createSession dataTaskWithRequest:request];
NSLog(@"Executing post request");
[createTask resume];
self.closeExtension();
} else {
self.sendShareEvent(@"extensionPostFailed");
}
}
@ -144,9 +147,10 @@
NSDictionary *credentials = [self getCredentialsForRequest:requestWithGroup];
if (credentials == nil) {
self.sendShareEvent(@"extensionPostFailed");
return;
}
if (files != nil && [files count] > 0) {
[self createPost:post withFiles:files credentials:credentials requestWithGroup:requestWithGroup];
}
@ -158,10 +162,10 @@
-(void)sendPostRequestForId:(NSString *)requestWithGroup {
NSDictionary *data = [self getDataForRequest:requestWithGroup];
NSDictionary *credentials = [self getCredentialsForRequest:requestWithGroup];
NSMutableDictionary *post = [[data objectForKey:@"post"] mutableCopy];
NSArray *fileIds = [data objectForKey:@"file_ids"];
[self createPost:post withFileIds:fileIds credentials:credentials];
}
@ -199,21 +203,20 @@
if ([emails count] > 0){
email = [emails objectAtIndex:0];
}
NSLog(@"completition %@ %@", tagstr, email);
NSLog(@"completion %@ %@", tagstr, email);
const void *certs[] = {certificate};
CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
NSLog(@"completition handler for certificate");
NSLog(@"completion handler for certificate");
return;
}
}
}
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
NSLog(@"completition handler that should not be reached");
} else {
NSLog(@"completition handler regular stuff");
NSLog(@"completion handler regular stuff");
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}
}
@ -226,7 +229,7 @@
requestWithGroup = self.requestWithGroup;
}
NSURL *requestUrl = [[dataTask originalRequest] URL];
if ([[requestUrl absoluteString] containsString:@"files"]) {
NSString *appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
NSError *error;
@ -239,7 +242,7 @@
if (fileIds == nil && data != nil) {
fileIds = [[NSMutableArray alloc] init];
}
for (id file in fileInfos) {
[fileIds addObject:[file objectForKey:@"id"]];
NSString * filename = [file objectForKey:@"name"];
@ -250,6 +253,8 @@
}
[data setObject:fileIds forKey:@"file_ids"];
[self setDataForRequest:data forRequestWithGroup:requestWithGroup];
} else {
self.sendShareEvent(@"extensionPostFailed");
}
}
}
@ -266,14 +271,15 @@
NSLog(@"completition 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];
NSDictionary *data = [self getDataForRequest:requestWithGroup];
NSArray *files = [data objectForKey:@"files"];
NSMutableArray *fileIds = [data objectForKey:@"file_ids"];
if ([[requestUrl absoluteString] containsString:@"files"] &&
[files count] == [fileIds count]) {
[self sendPostRequestForId:requestWithGroup];

View file

@ -12,6 +12,8 @@ NSExtensionContext* extensionContext;
return YES;
}
@synthesize bridge = _bridge;
- (UIView*) shareView {
NSURL *jsCodeLocation;
@ -57,13 +59,23 @@ RCT_EXPORT_METHOD(close:(NSDictionary *)data appGroupId:(NSString *)appGroupId)
if (tryToUploadInTheBackgound) {
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] setRequestWithGroup:requestWithGroup certificateName:certificateName];
[[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

@ -19,6 +19,6 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>115</string>
<string>117</string>
</dict>
</plist>

8
package-lock.json generated
View file

@ -14533,8 +14533,8 @@
}
},
"react-native": {
"version": "github:enahum/react-native#d7aa4677f3290cb0650952782a797aea9729e47e",
"from": "github:enahum/react-native#text-0.55",
"version": "github:enahum/react-native#b295c057c20bf3432f339874965e14268410d70b",
"from": "github:enahum/react-native#mm",
"requires": {
"absolute-path": "^0.0.0",
"art": "^0.10.0",
@ -14703,8 +14703,8 @@
}
},
"react-native-fetch-blob": {
"version": "github:enahum/react-native-fetch-blob#27983c83d7fad1cb4821dd6ba8e405da4af64f3a",
"from": "github:enahum/react-native-fetch-blob#27983c83d7fad1cb4821dd6ba8e405da4af64f3a",
"version": "github:enahum/react-native-fetch-blob#729c4b49b2e2195b1793c4d3490ab67cb9a8e472",
"from": "github:enahum/react-native-fetch-blob#729c4b49b2e2195b1793c4d3490ab67cb9a8e472",
"requires": {
"base-64": "0.1.0",
"glob": "7.0.6"

View file

@ -20,7 +20,7 @@
"prop-types": "15.6.1",
"react": "16.3.2",
"react-intl": "2.4.0",
"react-native": "github:enahum/react-native#text-0.55",
"react-native": "github:enahum/react-native#mm",
"react-native-animatable": "1.2.4",
"react-native-bottom-sheet": "1.0.3",
"react-native-button": "2.3.0",
@ -32,7 +32,7 @@
"react-native-drawer-layout": "2.0.0",
"react-native-exception-handler": "2.7.5",
"react-native-fast-image": "4.0.8",
"react-native-fetch-blob": "enahum/react-native-fetch-blob.git#27983c83d7fad1cb4821dd6ba8e405da4af64f3a",
"react-native-fetch-blob": "enahum/react-native-fetch-blob.git#729c4b49b2e2195b1793c4d3490ab67cb9a8e472",
"react-native-image-gallery": "enahum/react-native-image-gallery#a98b1051e94f5a394541ca1ff9b15e2c9ffed84f",
"react-native-image-picker": "0.26.7",
"react-native-keyboard-aware-scroll-view": "0.5.0",

View file

@ -190,6 +190,7 @@ export default class ExtensionTeam extends PureComponent {
titleCancelColor={defaultTheme.centerChannelColor}
onChangeText={this.handleSearch}
autoCapitalize='none'
value={this.state.term}
/>
</View>
);

View file

@ -6,6 +6,7 @@ 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});
}
});
};
@ -629,15 +656,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}
@ -645,15 +679,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>
);
}