vector-im/element-ios/issues/5009 - Implemented multi-room forwarding and added various tweaks following code review.

This commit is contained in:
Stefan Ceriu 2021-10-18 16:30:32 +03:00
parent e3f1bd25a9
commit 12c167ba6c
25 changed files with 620 additions and 484 deletions

View file

@ -1,6 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

View file

@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "radio-button-default.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "radio-button-default@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "radio-button-default@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "radio-button-selected.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "radio-button-selected@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "radio-button-selected@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 729 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -210,6 +210,8 @@ internal enum Asset {
internal static let cancel = ImageAsset(name: "cancel")
internal static let e2eVerified = ImageAsset(name: "e2e_verified")
internal static let horizontalLogo = ImageAsset(name: "horizontal_logo")
internal static let radioButtonDefault = ImageAsset(name: "radio-button-default")
internal static let radioButtonSelected = ImageAsset(name: "radio-button-selected")
}
}
// swiftlint:enable identifier_name line_length nesting type_body_length type_name

View file

@ -2399,10 +2399,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
// Set a default title view class without handling tap gesture (Let [self refreshRoomTitle] refresh this view correctly).
[self setRoomTitleViewClass:RoomTitleView.class];
// Remove details icon
RoomTitleView *roomTitleView = (RoomTitleView*)self.titleView;
// Remove the shadow image used to hide the bottom border of the navigation bar when the preview header is displayed
[mainNavigationController.navigationBar setShadowImage:nil];
[mainNavigationController.navigationBar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
@ -3198,7 +3195,8 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionForward]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
self.shareManager = [[ShareManager alloc] initWithShareItemProvider:[[SimpleShareItemProvider alloc] initWithTextMessage:selectedComponent.textMessage]];
self.shareManager = [[ShareManager alloc] initWithShareItemProvider:[[SimpleShareItemProvider alloc] initWithTextMessage:selectedComponent.textMessage]
type:ShareManagerTypeForward];
MXWeakify(self);
[self.shareManager setCompletionCallback:^(ShareManagerResult result) {
@ -3264,6 +3262,29 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
}
else // Add action for attachment
{
if (attachment.type == MXKAttachmentTypeFile ||
attachment.type == MXKAttachmentTypeImage ||
attachment.type == MXKAttachmentTypeVideo ||
attachment.type == MXKAttachmentTypeVoiceMessage) {
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionForward]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
self.shareManager = [[ShareManager alloc] initWithShareItemProvider:[[SimpleShareItemProvider alloc] initWithAttachment:attachment]
type:ShareManagerTypeForward];
MXWeakify(self);
[self.shareManager setCompletionCallback:^(ShareManagerResult result) {
MXStrongifyAndReturnIfNil(self);
[attachment onShareEnded];
[self dismissViewControllerAnimated:YES completion:nil];
self.shareManager = nil;
}];
[self presentViewController:self.shareManager.mainViewController animated:YES completion:nil];
}]];
}
if (BuildSettings.messageDetailsAllowSave)
{
if (attachment.type == MXKAttachmentTypeImage || attachment.type == MXKAttachmentTypeVideo)
@ -3390,37 +3411,6 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
}]];
}
}
if (attachment.type == MXKAttachmentTypeFile ||
attachment.type == MXKAttachmentTypeImage ||
attachment.type == MXKAttachmentTypeVideo ||
attachment.type == MXKAttachmentTypeVoiceMessage) {
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionForward]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self startActivityIndicator];
[attachment prepareShare:^(NSURL *fileURL) {
[self stopActivityIndicator];
self.shareManager = [[ShareManager alloc] initWithShareItemProvider:[[SimpleShareItemProvider alloc] initWithAttachment:attachment]];
MXWeakify(self);
[self.shareManager setCompletionCallback:^(ShareManagerResult result) {
MXStrongifyAndReturnIfNil(self);
[attachment onShareEnded];
[self dismissViewControllerAnimated:YES completion:nil];
self.shareManager = nil;
}];
[self presentViewController:self.shareManager.mainViewController animated:YES completion:nil];
} failure:^(NSError *error) {
[self showError:error];
[self stopActivityIndicator];
}];
}]];
}
}
// Check status of the selected event

View file

@ -16,25 +16,25 @@
#import <MatrixKit/MatrixKit.h>
typedef NS_ENUM(NSInteger, ShareDataSourceMode)
{
DataSourceModePeople,
DataSourceModeRooms
};
@class ShareDataSource;
@protocol ShareDataSourceDelegate <NSObject>
- (void)shareDataSourceDidChangeSelectedRoomIdentifiers:(ShareDataSource *)shareDataSource;
@end
@interface ShareDataSource : MXKRecentsDataSource
- (instancetype)initWithMode:(ShareDataSourceMode)dataSourceMode
fileStore:(MXFileStore *)fileStore
credentials:(MXCredentials *)credentials;
@property (nonatomic, weak) id<ShareDataSourceDelegate> shareDelegate;
/**
Returns the cell data at the index path
@param indexPath the index of the cell
@return the MXKRecentCellData instance if it exists
*/
- (MXKRecentCellData *)cellDataAtIndexPath:(NSIndexPath *)indexPath;
@property (nonatomic, strong, readonly) NSSet<NSString *> *selectedRoomIdentifiers;
- (instancetype)initWithFileStore:(MXFileStore *)fileStore
credentials:(MXCredentials *)credentials;
- (void)selectRoomWithIdentifier:(NSString *)roomIdentifier animated:(BOOL)animated;
- (void)deselectRoomWithIdentifier:(NSString *)roomIdentifier animated:(BOOL)animated;
@end

View file

@ -19,27 +19,28 @@
@interface ShareDataSource ()
@property (nonatomic, assign, readonly) ShareDataSourceMode dataSourceMode;
@property (nonatomic, strong, readonly) MXFileStore *fileStore;
@property (nonatomic, strong, readonly) MXCredentials *credentials;
@property NSArray <MXKRecentCellData *> *recentCellDatas;
@property NSMutableArray <MXKRecentCellData *> *visibleRoomCellDatas;
@property (nonatomic, strong) NSMutableSet<NSString *> *internalSelectedRoomIdentifiers;
@end
@implementation ShareDataSource
- (instancetype)initWithMode:(ShareDataSourceMode)dataSourceMode
fileStore:(MXFileStore *)fileStore
credentials:(MXCredentials *)credentials
- (instancetype)initWithFileStore:(MXFileStore *)fileStore
credentials:(MXCredentials *)credentials
{
if (self = [super init])
{
_dataSourceMode = dataSourceMode;
_fileStore = fileStore;
_credentials = credentials;
_internalSelectedRoomIdentifiers = [NSMutableSet set];
[self loadCellData];
}
return self;
@ -53,6 +54,25 @@
_visibleRoomCellDatas = nil;
}
- (NSSet<NSString *> *)selectedRoomIdentifiers
{
return self.internalSelectedRoomIdentifiers.copy;
}
- (void)selectRoomWithIdentifier:(NSString *)roomIdentifier animated:(BOOL)animated
{
[self.internalSelectedRoomIdentifiers addObject:roomIdentifier];
[self.shareDelegate shareDataSourceDidChangeSelectedRoomIdentifiers:self];
}
- (void)deselectRoomWithIdentifier:(NSString *)roomIdentifier animated:(BOOL)animated
{
[self.internalSelectedRoomIdentifiers removeObject:roomIdentifier];
[self.shareDelegate shareDataSourceDidChangeSelectedRoomIdentifiers:self];
}
#pragma mark - Private
- (void)loadCellData
@ -66,7 +86,7 @@
for (MXRoomSummary *roomSummary in roomsSummaries)
{
if (!roomSummary.hiddenFromUser && ((self.dataSourceMode == DataSourceModeRooms) ^ roomSummary.isDirect))
if (!roomSummary.hiddenFromUser)
{
[roomSummary setMatrixSession:session];
@ -137,6 +157,7 @@
{
self.visibleRoomCellDatas = nil;
}
[self.delegate dataSource:self didCellChange:nil];
}
@ -160,7 +181,11 @@
{
RecentRoomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[RecentRoomTableViewCell defaultReuseIdentifier]];
[cell render:[self cellDataAtIndexPath:indexPath]];
MXKRecentCellData *data = [self cellDataAtIndexPath:indexPath];
[cell render:data];
[cell setCustomSelected:[self.selectedRoomIdentifiers containsObject:data.roomSummary.roomId] animated:NO];
return cell;
}

View file

@ -60,6 +60,12 @@ private class SimpleShareItem: ShareItemProtocol {
let items: [ShareItemProtocol]
private override init() {
attachment = nil
textMessage = nil
self.items = []
}
@objc public init(withAttachment attachment: MXKAttachment) {
self.attachment = attachment
self.items = [SimpleShareItem(withAttachment: attachment)];
@ -78,10 +84,18 @@ private class SimpleShareItem: ShareItemProtocol {
return
}
attachment?.prepareShare({ url in
completion(url, nil)
guard let attachment = attachment else {
fatalError("[SimpleShareItemProvider] Invalid item provider state.")
}
attachment.prepareShare({ url in
DispatchQueue.main.async {
completion(url, nil)
}
}, failure: { error in
completion(nil, error)
DispatchQueue.main.async {
completion(nil, error)
}
})
}

View file

@ -20,6 +20,11 @@
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, ShareManagerType) {
ShareManagerTypeSend,
ShareManagerTypeForward,
};
typedef NS_ENUM(NSUInteger, ShareManagerResult) {
ShareManagerResultFinished,
ShareManagerResultCancelled,
@ -30,17 +35,12 @@ typedef NS_ENUM(NSUInteger, ShareManagerResult) {
@property (nonatomic, copy) void (^completionCallback)(ShareManagerResult);
- (instancetype)initWithShareItemProvider:(id<ShareItemProviderProtocol>)shareItemProvider;
- (instancetype)initWithShareItemProvider:(id<ShareItemProviderProtocol>)shareItemProvider
type:(ShareManagerType)type;
- (UIViewController *)mainViewController;
@end
@interface NSItemProvider (ShareManager)
@property BOOL isLoaded;
@end
NS_ASSUME_NONNULL_END

View file

@ -31,6 +31,7 @@
#endif
static const CGFloat kLargeImageSizeMaxDimension = 2048.0;
static const CGSize kThumbnailSize = {800.0, 600.0};
typedef NS_ENUM(NSInteger, ImageCompressionMode)
{
@ -61,6 +62,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
@implementation ShareManager
- (instancetype)initWithShareItemProvider:(id<ShareItemProviderProtocol>)shareItemProvider
type:(ShareManagerType)type
{
if (self = [super init])
{
@ -91,7 +93,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
[MXLog configure:configuration];
_shareViewController = [[ShareViewController alloc] initWithType:ShareViewControllerTypeSend
_shareViewController = [[ShareViewController alloc] initWithType:(type == ShareManagerTypeForward ? ShareViewControllerTypeForward : ShareViewControllerTypeSend)
currentState:ShareViewControllerAccountStateNotConfigured];
[_shareViewController setDelegate:self];
@ -101,7 +103,6 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
[NSBundle mxk_setLanguage:language];
[NSBundle mxk_setFallbackLanguage:@"en"];
// Check the current matrix user.
[self checkUserAccount];
}
@ -117,8 +118,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
#pragma mark - ShareViewControllerDelegate
- (void)shareViewControllerDidRequestShare:(ShareViewController *)shareViewController
forRoomIdentifier:(NSString *)roomIdentifier
- (void)shareViewController:(ShareViewController *)shareViewController didRequestShareForRoomIdentifiers:(NSSet<NSString *> *)roomIdentifiers
{
MXSession *session = [[MXSession alloc] initWithMatrixRestClient:[[MXRestClient alloc] initWithCredentials:self.userAccount.mxCredentials andOnUnrecognizedCertificateBlock:nil]];
[MXFileStore setPreloadOptions:0];
@ -129,12 +129,22 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
session.crypto.warnOnUnknowDevices = NO; // Do not warn for unknown devices. We have cross-signing now
MXRoom *selectedRoom = [MXRoom loadRoomFromStore:self.fileStore withRoomId:roomIdentifier matrixSession:session];
[self sendContentToRoom:selectedRoom success:nil failure:^(NSError *error){
NSMutableArray<MXRoom *> *rooms = [NSMutableArray array];
for (NSString *roomIdentifier in roomIdentifiers) {
MXRoom *room = [MXRoom loadRoomFromStore:self.fileStore withRoomId:roomIdentifier matrixSession:session];
if (room) {
[rooms addObject:room];
}
}
[self sendContentToRooms:rooms success:^{
self.completionCallback(ShareManagerResultFinished);
} failure:^(NSError *error){
[self showFailureAlert:[VectorL10n roomEventFailedToSend]];
}];
} failure:^(NSError *error) {
MXLogError(@"[ShareManager] Failed preparign matrix session");
MXLogError(@"[ShareManager] Failed preparing matrix session");
}];
}
@ -145,16 +155,37 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
#pragma mark - Private
- (void)sendContentToRoom:(MXRoom *)room success:(void(^)(void))success failure:(void(^)(NSError *))failure
- (void)showFailureAlert:(NSString *)title
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
MXWeakify(self);
UIAlertAction *okAction = [UIAlertAction actionWithTitle:[MatrixKitL10n ok] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
MXStrongifyAndReturnIfNil(self);
if (self.completionCallback)
{
self.completionCallback(ShareManagerResultFailed);
}
}];
[alertController addAction:okAction];
[self.mainViewController presentViewController:alertController animated:YES completion:nil];
}
- (void)sendContentToRooms:(NSArray<MXRoom *> *)rooms success:(void(^)(void))success failure:(void(^)(NSError *))failure
{
[self resetPendingData];
NSMutableArray <id<ShareItemProtocol>> *pendingImagesItemProviders = [NSMutableArray array]; // Used to keep the items associated to pending images (used only when all items are images).
__block NSError *firstRequestError = nil;
dispatch_group_t dispatchGroup = dispatch_group_create();
void (^requestFailure)(NSError*) = ^(NSError *requestError) {
void (^requestSuccess)(void) = ^() {
dispatch_group_leave(dispatchGroup);
};
void (^requestFailure)(NSError *) = ^(NSError *requestError) {
if (requestError && !firstRequestError)
{
firstRequestError = requestError;
@ -166,68 +197,93 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
MXWeakify(self);
for (id<ShareItemProtocol> item in self.shareItemProvider.items)
{
if (item.type == ShareItemTypeText || item.type == ShareItemTypeURL) {
dispatch_group_enter(dispatchGroup);
[self.shareItemProvider loadItem:item completion:^(id item, NSError *error) {
MXStrongifyAndReturnIfNil(self);
if (error)
{
requestFailure(error);
return;
}
NSString *text = nil;
if([item isKindOfClass:[NSString class]])
{
text = item;
}
else if([item isKindOfClass:[NSURL class]])
{
text = [(NSURL *)item absoluteString];
}
if(text.length == 0)
{
requestFailure(nil);
return;
}
[self sendText:text toRooms:rooms success:requestSuccess failure:requestFailure];
}];
}
if (item.type == ShareItemTypeFileURL) {
dispatch_group_enter(dispatchGroup);
[self.shareItemProvider loadItem:item completion:^(NSURL *url, NSError *error) {
if (error) {
MXStrongifyAndReturnIfNil(self);
if (error)
{
requestFailure(error);
dispatch_group_leave(dispatchGroup);
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
MXStrongifyAndReturnIfNil(self);
[self sendFileWithUrl:url toRoom:room success:^{
dispatch_group_leave(dispatchGroup);
} failure:requestFailure];
});
[self sendFileWithUrl:url toRooms:rooms success:requestSuccess failure:requestFailure];
}];
}
if (item.type == ShareItemTypeText) {
dispatch_group_enter(dispatchGroup);
[self.shareItemProvider loadItem:item completion:^(NSString *text, NSError *error) {
if (error) {
requestFailure(error);
dispatch_group_leave(dispatchGroup);
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
MXStrongifyAndReturnIfNil(self);
[self sendText:text toRoom:room success:^{
dispatch_group_leave(dispatchGroup);
} failure:requestFailure];
});
}];
}
if (item.type == ShareItemTypeURL)
if (item.type == ShareItemTypeVideo || item.type == ShareItemTypeMovie)
{
dispatch_group_enter(dispatchGroup);
[self.shareItemProvider loadItem:item completion:^(NSURL *url, NSError *error) {
if (error) {
[self.shareItemProvider loadItem:item completion:^(NSURL *videoLocalUrl, NSError *error) {
MXStrongifyAndReturnIfNil(self);
if (error)
{
requestFailure(error);
dispatch_group_leave(dispatchGroup);
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
MXStrongifyAndReturnIfNil(self);
[self sendText:url.absoluteString toRoom:room success:^{
dispatch_group_leave(dispatchGroup);
} failure:requestFailure];
});
[self sendVideo:videoLocalUrl toRooms:rooms success:requestSuccess failure:requestFailure];
}];
}
if (item.type == ShareItemTypeVoiceMessage)
{
dispatch_group_enter(dispatchGroup);
[self.shareItemProvider loadItem:item completion:^(NSURL *fileURL, NSError *error) {
MXStrongifyAndReturnIfNil(self);
if (error)
{
requestFailure(error);
return;
}
[self sendVoiceMessage:fileURL toRooms:rooms success:requestSuccess failure:requestFailure];
}];
}
if (item.type == ShareItemTypeImage)
{
dispatch_group_enter(dispatchGroup);
[self.shareItemProvider loadItem:item completion:^(id<NSSecureCoding> itemProviderItem, NSError *error) {
if (error) {
MXStrongifyAndReturnIfNil(self);
if (error)
{
requestFailure(error);
dispatch_group_leave(dispatchGroup);
return;
}
@ -250,30 +306,23 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
imageData = UIImagePNGRepresentation(image);
}
MXStrongifyAndReturnIfNil(self);
if (imageData)
if (!imageData)
{
if ([self.shareItemProvider areAllItemsImages])
{
[self.pendingImages addObject:imageData];
[pendingImagesItemProviders addObject:item];
}
else
{
CGSize imageSize = [self imageSizeFromImageData:imageData];
self.imageCompressionMode = ImageCompressionModeNone;
self.actualLargeSize = MAX(imageSize.width, imageSize.height);
[self sendImageData:imageData withItem:item toRoom:room success:^{
dispatch_group_leave(dispatchGroup);
} failure:requestFailure];
}
requestFailure(error);
return;
}
if ([self.shareItemProvider areAllItemsImages])
{
[self.pendingImages addObject:imageData];
}
else
{
MXLogError(@"[ShareManager] sendContentToRoom: failed to loadItemForTypeIdentifier. Error: %@", error);
dispatch_group_leave(dispatchGroup);
CGSize imageSize = [self imageSizeFromImageData:imageData];
self.imageCompressionMode = ImageCompressionModeNone;
self.actualLargeSize = MAX(imageSize.width, imageSize.height);
[self sendImageData:imageData toRooms:rooms success:requestSuccess failure:requestFailure];
}
// Only prompt for image resize if all items are images
@ -283,9 +332,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
if ([self.shareItemProvider areAllItemsLoaded])
{
UIAlertController *compressionPrompt = [self compressionPromptForPendingImagesWithShareBlock:^{
[self sendImageDatas:self.pendingImages.copy withItems:pendingImagesItemProviders toRoom:room success:^{
dispatch_group_leave(dispatchGroup);
} failure:requestFailure];
[self sendImageDatas:self.pendingImages.copy toRooms:rooms success:requestSuccess failure:requestFailure];
}];
if (compressionPrompt)
@ -300,63 +347,6 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
}
}];
}
if (item.type == ShareItemTypeVideo)
{
dispatch_group_enter(dispatchGroup);
[self.shareItemProvider loadItem:item completion:^(NSURL *videoLocalUrl, NSError *error) {
if (error) {
requestFailure(error);
dispatch_group_leave(dispatchGroup);
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
MXStrongifyAndReturnIfNil(self);
[self sendVideo:videoLocalUrl toRoom:room success:^{
dispatch_group_leave(dispatchGroup);
} failure:requestFailure];
});
}];
}
if (item.type == ShareItemTypeMovie)
{
dispatch_group_enter(dispatchGroup);
[self.shareItemProvider loadItem:item completion:^(NSURL *videoLocalUrl, NSError *error) {
if (error) {
requestFailure(error);
dispatch_group_leave(dispatchGroup);
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
MXStrongifyAndReturnIfNil(self);
[self sendVideo:videoLocalUrl toRoom:room success:^{
dispatch_group_leave(dispatchGroup);
} failure:requestFailure];
});
}];
}
if (item.type == ShareItemTypeVoiceMessage)
{
dispatch_group_enter(dispatchGroup);
[self.shareItemProvider loadItem:item completion:^(NSURL *fileURL, NSError *error) {
if (error) {
requestFailure(error);
dispatch_group_leave(dispatchGroup);
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
MXStrongifyAndReturnIfNil(self);
[self sendVoiceMessage:fileURL toRoom:room success:^{
dispatch_group_leave(dispatchGroup);
} failure:requestFailure];
});
}];
}
}
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
@ -368,30 +358,11 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
}
else
{
self.completionCallback(ShareManagerResultFinished);
success();
}
});
}
- (void)showFailureAlert:(NSString *)title
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
MXWeakify(self);
UIAlertAction *okAction = [UIAlertAction actionWithTitle:[MatrixKitL10n ok] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
MXStrongifyAndReturnIfNil(self);
if (self.completionCallback)
{
self.completionCallback(ShareManagerResultFailed);
}
}];
[alertController addAction:okAction];
[self.mainViewController presentViewController:alertController animated:YES completion:nil];
}
- (void)checkUserAccount
{
// Force account manager to reload account from the local storage.
@ -428,21 +399,14 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
{
_fileStore = [[MXFileStore alloc] initWithCredentials:self.userAccount.mxCredentials];
ShareDataSource *roomDataSource = [[ShareDataSource alloc] initWithMode:DataSourceModeRooms
fileStore:_fileStore
credentials:self.userAccount.mxCredentials];
ShareDataSource *peopleDataSource = [[ShareDataSource alloc] initWithMode:DataSourceModePeople
fileStore:_fileStore
credentials:self.userAccount.mxCredentials];
ShareDataSource *roomDataSource = [[ShareDataSource alloc] initWithFileStore:_fileStore
credentials:self.userAccount.mxCredentials];
[self.shareViewController configureWithState:ShareViewControllerAccountStateConfigured
roomDataSource:roomDataSource
peopleDataSource:peopleDataSource];
roomDataSource:roomDataSource];
} else {
[self.shareViewController configureWithState:ShareViewControllerAccountStateNotConfigured
roomDataSource:nil
peopleDataSource:nil];
roomDataSource:nil];
}
}
@ -581,7 +545,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
return compressionPrompt;
}
- (void)didStartSendingToRoom:(MXRoom *)room
- (void)didStartSending
{
[self.shareViewController showProgressIndicator];
}
@ -738,11 +702,9 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
- (void)presentCompressionPrompt:(UIAlertController *)compressionPrompt
{
dispatch_async(dispatch_get_main_queue(), ^{
[compressionPrompt popoverPresentationController].sourceView = self.mainViewController.view;
[compressionPrompt popoverPresentationController].sourceRect = self.mainViewController.view.frame;
[self.mainViewController presentViewController:compressionPrompt animated:YES completion:nil];
});
[compressionPrompt popoverPresentationController].sourceView = self.mainViewController.view;
[compressionPrompt popoverPresentationController].sourceRect = self.mainViewController.view.frame;
[self.mainViewController presentViewController:compressionPrompt animated:YES completion:nil];
}
#pragma mark - Notifications
@ -781,11 +743,11 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
#pragma mark - Sharing
- (void)sendText:(NSString *)text
toRoom:(MXRoom *)room
toRooms:(NSArray<MXRoom *> *)rooms
success:(dispatch_block_t)success
failure:(void(^)(NSError *error))failure
{
[self didStartSendingToRoom:room];
[self didStartSending];
if (!text)
{
MXLogError(@"[ShareManager] Invalid text.");
@ -793,19 +755,34 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
return;
}
[room sendTextMessage:text success:^(NSString *eventId) {
success();
} failure:^(NSError *error) {
MXLogError(@"[ShareManager] sendTextMessage failed with error %@", error);
failure(error);
}];
__block NSError *error = nil;
dispatch_group_t dispatchGroup = dispatch_group_create();
for (MXRoom *room in rooms) {
dispatch_group_enter(dispatchGroup);
[room sendTextMessage:text success:^(NSString *eventId) {
dispatch_group_leave(dispatchGroup);
} failure:^(NSError *innerError) {
MXLogError(@"[ShareManager] sendTextMessage failed with error %@", error);
error = innerError;
dispatch_group_leave(dispatchGroup);
}];
}
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
if(error) {
failure(error);
} else {
success();
}
});
}
- (void)sendFileWithUrl:(NSURL *)fileUrl toRoom:(MXRoom *)room
success:(dispatch_block_t)success
failure:(void(^)(NSError *error))failure
- (void)sendFileWithUrl:(NSURL *)fileUrl
toRooms:(NSArray<MXRoom *> *)rooms
success:(dispatch_block_t)success
failure:(void(^)(NSError *error))failure
{
[self didStartSendingToRoom:room];
[self didStartSending];
if (!fileUrl)
{
MXLogError(@"[ShareManager] Invalid file url.");
@ -818,21 +795,185 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
mimeType = [self mimeTypeFromUTI:(__bridge NSString *)uti];
CFRelease(uti);
[room sendFile:fileUrl mimeType:mimeType localEcho:nil success:^(NSString *eventId) {
success();
} failure:^(NSError *error) {
MXLogError(@"[ShareManager] sendFile failed with error %@", error);
failure(error);
} keepActualFilename:YES];
__block NSError *error = nil;
dispatch_group_t dispatchGroup = dispatch_group_create();
for (MXRoom *room in rooms) {
dispatch_group_enter(dispatchGroup);
[room sendFile:fileUrl mimeType:mimeType localEcho:nil success:^(NSString *eventId) {
dispatch_group_leave(dispatchGroup);
} failure:^(NSError *innerError) {
MXLogError(@"[ShareManager] sendFile failed with error %@", innerError);
error = innerError;
dispatch_group_leave(dispatchGroup);
} keepActualFilename:YES];
}
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
if(error) {
failure(error);
} else {
success();
}
});
}
- (void)sendVideo:(NSURL *)videoLocalUrl
toRooms:(NSArray<MXRoom *> *)rooms
success:(dispatch_block_t)success
failure:(void(^)(NSError *error))failure
{
AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoLocalUrl options:nil];
MXWeakify(self);
// Ignore showMediaCompressionPrompt setting due to memory constraints when encrypting large videos.
UIAlertController *compressionPrompt = [MXKTools videoConversionPromptForVideoAsset:videoAsset withCompletion:^(NSString *presetName) {
MXStrongifyAndReturnIfNil(self);
// If the preset name is nil, the user cancelled.
if (!presetName)
{
return;
}
// Set the chosen video conversion preset.
[MXSDKOptions sharedInstance].videoConversionPresetName = presetName;
[self didStartSending];
if (!videoLocalUrl)
{
MXLogError(@"[ShareManager] Invalid video file url.");
failure(nil);
return;
}
// Retrieve the video frame at 1 sec to define the video thumbnail
AVAssetImageGenerator *assetImageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:videoAsset];
assetImageGenerator.appliesPreferredTrackTransform = YES;
CMTime time = CMTimeMake(1, 1);
CGImageRef imageRef = [assetImageGenerator copyCGImageAtTime:time actualTime:NULL error:nil];
// Finalize video attachment
UIImage *videoThumbnail = [[UIImage alloc] initWithCGImage:imageRef];
CFRelease(imageRef);
__block NSError *error = nil;
dispatch_group_t dispatchGroup = dispatch_group_create();
for (MXRoom *room in rooms) {
dispatch_group_enter(dispatchGroup);
[room sendVideoAsset:videoAsset withThumbnail:videoThumbnail localEcho:nil success:^(NSString *eventId) {
dispatch_group_leave(dispatchGroup);
} failure:^(NSError *innerError) {
MXLogError(@"[ShareManager] Failed sending video with error %@", innerError);
error = innerError;
dispatch_group_leave(dispatchGroup);
}];
}
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
if(error) {
failure(error);
} else {
success();
}
});
}];
[self presentCompressionPrompt:compressionPrompt];
}
- (void)sendVoiceMessage:(NSURL *)fileUrl
toRooms:(NSArray<MXRoom *> *)rooms
success:(dispatch_block_t)success
failure:(void(^)(NSError *error))failure
{
[self didStartSending];
if (!fileUrl)
{
MXLogError(@"[ShareManager] Invalid voice message file url.");
failure(nil);
return;
}
__block NSError *error = nil;
dispatch_group_t dispatchGroup = dispatch_group_create();
for (MXRoom *room in rooms) {
dispatch_group_enter(dispatchGroup);
[room sendVoiceMessage:fileUrl mimeType:nil duration:0.0 samples:nil localEcho:nil success:^(NSString *eventId) {
dispatch_group_leave(dispatchGroup);
} failure:^(NSError *innerError) {
MXLogError(@"[ShareManager] sendVoiceMessage failed with error %@", error);
error = innerError;
dispatch_group_leave(dispatchGroup);
} keepActualFilename:YES];
}
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
if(error) {
failure(error);
} else {
success();
}
});
}
- (void)sendImageDatas:(NSArray<id<ShareItemProtocol>> *)imageDatas
toRooms:(NSArray<MXRoom *> *)rooms
success:(dispatch_block_t)success
failure:(void(^)(NSError *error))failure
{
if (imageDatas.count == 0)
{
MXLogError(@"[ShareManager] sendImages: no images to send.");
failure(nil);
return;
}
[self didStartSending];
dispatch_group_t requestsGroup = dispatch_group_create();
__block NSError *firstRequestError;
NSUInteger index = 0;
for (NSData *imageData in imageDatas)
{
@autoreleasepool
{
dispatch_group_enter(requestsGroup);
[self sendImageData:imageData toRooms:rooms success:^{
dispatch_group_leave(requestsGroup);
} failure:^(NSError *error) {
if (error && !firstRequestError)
{
firstRequestError = error;
}
dispatch_group_leave(requestsGroup);
}];
}
index++;
}
dispatch_group_notify(requestsGroup, dispatch_get_main_queue(), ^{
if (firstRequestError)
{
failure(firstRequestError);
}
else
{
success();
}
});
}
- (void)sendImageData:(NSData *)imageData
withItem:(id<ShareItemProtocol>)item
toRoom:(MXRoom *)room
toRooms:(NSArray<MXRoom *> *)rooms
success:(dispatch_block_t)success
failure:(void(^)(NSError *error))failure
{
[self didStartSendingToRoom:room];
[self didStartSending];
NSString *imageUTI;
NSString *mimeType;
@ -848,7 +989,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
if (!mimeType)
{
MXLogError(@"[ShareManager] sendImage failed. Cannot determine MIME type of %@", item);
MXLogError(@"[ShareManager] sendImage failed. Cannot determine MIME type .");
if (failure)
{
failure(nil);
@ -921,142 +1062,33 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
imageSize = [self imageSizeFromImageData:imageData];
}
UIImage *thumbnail = nil;
// Thumbnail is useful only in case of encrypted room
if (room.summary.isEncrypted)
{
thumbnail = [MXKTools resizeImageWithData:imageData toFitInSize:CGSizeMake(800, 600)];
}
[room sendImage:finalImageData withImageSize:imageSize mimeType:mimeType andThumbnail:thumbnail localEcho:nil success:^(NSString *eventId) {
success();
} failure:^(NSError *error) {
MXLogError(@"[ShareManager] sendImage failed with error %@", error);
failure(error);
}];
}
- (void)sendImageDatas:(NSArray<id<ShareItemProtocol>> *)imageDatas
withItems:(NSArray<id<ShareItemProtocol>> *)items toRoom:(MXRoom *)room
success:(dispatch_block_t)success
failure:(void(^)(NSError *error))failure
{
if (imageDatas.count == 0 || imageDatas.count != items.count)
{
MXLogError(@"[ShareManager] sendImages: no images to send.");
failure(nil);
return;
}
[self didStartSendingToRoom:room];
dispatch_group_t requestsGroup = dispatch_group_create();
__block NSError *firstRequestError;
NSUInteger index = 0;
for (NSData *imageData in imageDatas)
{
@autoreleasepool
{
dispatch_group_enter(requestsGroup);
[self sendImageData:imageData withItem:items[index] toRoom:room success:^{
dispatch_group_leave(requestsGroup);
} failure:^(NSError *error) {
if (error && !firstRequestError)
{
firstRequestError = error;
}
__block NSError *error = nil;
dispatch_group_t dispatchGroup = dispatch_group_create();
for (MXRoom *room in rooms) {
dispatch_group_leave(requestsGroup);
}];
UIImage *thumbnail = nil;
if (room.summary.isEncrypted) // Thumbnail is useful only in case of encrypted room
{
thumbnail = [MXKTools resizeImageWithData:imageData toFitInSize:kThumbnailSize];
}
index++;
dispatch_group_enter(dispatchGroup);
[room sendImage:finalImageData withImageSize:imageSize mimeType:mimeType andThumbnail:thumbnail localEcho:nil success:^(NSString *eventId) {
dispatch_group_leave(dispatchGroup);
} failure:^(NSError *innerError) {
MXLogError(@"[ShareManager] sendImage failed with error %@", error);
error = innerError;
dispatch_group_leave(dispatchGroup);
}];
}
dispatch_group_notify(requestsGroup, dispatch_get_main_queue(), ^{
if (firstRequestError)
{
failure(firstRequestError);
}
else
{
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
if(error) {
failure(error);
} else {
success();
}
});
}
- (void)sendVideo:(NSURL *)videoLocalUrl
toRoom:(MXRoom *)room
success:(dispatch_block_t)success
failure:(void(^)(NSError *error))failure
{
AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoLocalUrl options:nil];
MXWeakify(self);
// Ignore showMediaCompressionPrompt setting due to memory constraints when encrypting large videos.
UIAlertController *compressionPrompt = [MXKTools videoConversionPromptForVideoAsset:videoAsset withCompletion:^(NSString *presetName) {
MXStrongifyAndReturnIfNil(self);
// If the preset name is nil, the user cancelled.
if (!presetName)
{
return;
}
// Set the chosen video conversion preset.
[MXSDKOptions sharedInstance].videoConversionPresetName = presetName;
[self didStartSendingToRoom:room];
if (!videoLocalUrl)
{
MXLogError(@"[ShareManager] Invalid video file url.");
failure(nil);
return;
}
// Retrieve the video frame at 1 sec to define the video thumbnail
AVAssetImageGenerator *assetImageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:videoAsset];
assetImageGenerator.appliesPreferredTrackTransform = YES;
CMTime time = CMTimeMake(1, 1);
CGImageRef imageRef = [assetImageGenerator copyCGImageAtTime:time actualTime:NULL error:nil];
// Finalize video attachment
UIImage *videoThumbnail = [[UIImage alloc] initWithCGImage:imageRef];
CFRelease(imageRef);
[room sendVideoAsset:videoAsset withThumbnail:videoThumbnail localEcho:nil success:^(NSString *eventId) {
success();
} failure:^(NSError *error) {
MXLogError(@"[ShareManager] Failed sending video with error %@", error);
failure(error);
}];
}];
[self presentCompressionPrompt:compressionPrompt];
}
- (void)sendVoiceMessage:(NSURL *)fileUrl
toRoom:(MXRoom *)room
success:(dispatch_block_t)success
failure:(void(^)(NSError *error))failure
{
[self didStartSendingToRoom:room];
if (!fileUrl)
{
MXLogError(@"[ShareManager] Invalid voice message file url.");
failure(nil);
return;
}
[room sendVoiceMessage:fileUrl mimeType:nil duration:0.0 samples:nil localEcho:nil success:^(NSString *eventId) {
success();
} failure:^(NSError *error) {
MXLogError(@"[ShareManager] sendVoiceMessage failed with error %@", error);
failure(error);
} keepActualFilename:YES];
}
@end

View file

@ -20,4 +20,6 @@
+ (CGFloat)cellHeight;
- (void)setCustomSelected:(BOOL)selected animated:(BOOL)animated;
@end

View file

@ -30,6 +30,7 @@
@property (weak, nonatomic) IBOutlet MXKImageView *avatarImageView;
@property (weak, nonatomic) IBOutlet UILabel *roomTitleLabel;
@property (weak, nonatomic) IBOutlet UIImageView *encryptedRoomIcon;
@property (weak, nonatomic) IBOutlet UIButton *selectionButton;
@end
@ -56,6 +57,12 @@
self.roomTitleLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
self.contentView.backgroundColor = ThemeService.shared.theme.backgroundColor;
[self.selectionButton setImage:[UIImage imageNamed:@"radio-button-default"] forState:UIControlStateNormal];
[self.selectionButton setImage:[UIImage imageNamed:@"radio-button-selected"] forState:UIControlStateSelected];
[self.selectionButton setTitle:@"" forState:UIControlStateNormal];
[self.selectionButton setTitle:@"" forState:UIControlStateSelected];
}
- (void)layoutSubviews
@ -92,4 +99,11 @@
return 74;
}
- (void)setCustomSelected:(BOOL)selected animated:(BOOL)animated
{
[UIView animateWithDuration:(animated ? 0.25f : 0.0f) animations:^{
[self.selectionButton setSelected:selected];
}];
}
@end

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19144"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -25,12 +25,9 @@
<constraint firstAttribute="width" constant="42" id="Xp0-5S-1wI"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vBS-iO-8H6">
<rect key="frame" x="78" y="27" width="33" height="20"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" text="Text" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vBS-iO-8H6">
<rect key="frame" x="76" y="15" width="32" height="44"/>
<accessibility key="accessibilityConfiguration" identifier="TitleLabel"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="H21-1K-fGz"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
@ -43,15 +40,27 @@
<constraint firstAttribute="width" constant="11" id="Mai-xD-TqV"/>
</constraints>
</imageView>
<button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="m0f-of-6xq">
<rect key="frame" x="562" y="29" width="16" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="Wue-Ih-eVg"/>
<constraint firstAttribute="width" constant="16" id="s5a-PW-58p"/>
</constraints>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" image="radio-button-selected"/>
</button>
</subviews>
<constraints>
<constraint firstItem="vBS-iO-8H6" firstAttribute="top" secondItem="b83-aU-AJ3" secondAttribute="topMargin" constant="4" id="0nI-h1-enE"/>
<constraint firstItem="HJd-Am-cCx" firstAttribute="leading" secondItem="b83-aU-AJ3" secondAttribute="leadingMargin" constant="6" id="4df-2f-865"/>
<constraint firstItem="m0f-of-6xq" firstAttribute="centerY" secondItem="b83-aU-AJ3" secondAttribute="centerY" id="B3g-N6-bh1"/>
<constraint firstItem="hqD-YY-LWz" firstAttribute="top" secondItem="b83-aU-AJ3" secondAttribute="topMargin" constant="34" id="Cdm-v3-js8"/>
<constraint firstItem="hqD-YY-LWz" firstAttribute="leading" secondItem="b83-aU-AJ3" secondAttribute="leadingMargin" constant="42" id="UIE-13-LGw"/>
<constraint firstItem="m0f-of-6xq" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="vBS-iO-8H6" secondAttribute="trailing" constant="16" id="dyz-rB-vUR"/>
<constraint firstItem="HJd-Am-cCx" firstAttribute="centerY" secondItem="b83-aU-AJ3" secondAttribute="centerY" id="flh-LO-k3n"/>
<constraint firstItem="vBS-iO-8H6" firstAttribute="centerY" secondItem="b83-aU-AJ3" secondAttribute="centerY" id="ocY-mt-0n0"/>
<constraint firstAttribute="trailingMargin" relation="greaterThanOrEqual" secondItem="vBS-iO-8H6" secondAttribute="trailing" constant="15" id="qqy-zs-Ccy"/>
<constraint firstItem="vBS-iO-8H6" firstAttribute="leading" secondItem="HJd-Am-cCx" secondAttribute="trailing" constant="14" id="quv-47-R9n"/>
<constraint firstAttribute="trailing" secondItem="m0f-of-6xq" secondAttribute="trailing" constant="22" id="k1V-mv-zgW"/>
<constraint firstAttribute="bottomMargin" secondItem="vBS-iO-8H6" secondAttribute="bottom" constant="4" id="k1n-ZD-FS0"/>
<constraint firstItem="vBS-iO-8H6" firstAttribute="leading" secondItem="HJd-Am-cCx" secondAttribute="trailing" constant="12" id="quv-47-R9n"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -60,11 +69,13 @@
<outlet property="avatarImageView" destination="HJd-Am-cCx" id="Mv2-Kb-aG4"/>
<outlet property="encryptedRoomIcon" destination="hqD-YY-LWz" id="Ikp-Sf-Vlc"/>
<outlet property="roomTitleLabel" destination="vBS-iO-8H6" id="0J9-p3-Lzx"/>
<outlet property="selectionButton" destination="m0f-of-6xq" id="pzc-VN-t72"/>
</connections>
<point key="canvasLocation" x="-53" y="-43"/>
</tableViewCell>
</objects>
<resources>
<image name="e2e_verified" width="10" height="12"/>
<image name="radio-button-selected" width="24" height="24"/>
</resources>
</document>

View file

@ -18,6 +18,7 @@
#import "RoomsListViewController.h"
#import "RecentRoomTableViewCell.h"
#import "ShareDataSource.h"
#import "RecentCellData.h"
#import "ThemeService.h"
@ -140,6 +141,22 @@
return [RecentRoomTableViewCell cellHeight];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSString *roomIdentifier = [self.dataSource cellDataAtIndexPath:indexPath].roomSummary.roomId;
ShareDataSource *dataSource = (ShareDataSource *)self.dataSource;
if ([dataSource.selectedRoomIdentifiers containsObject:roomIdentifier]) {
[dataSource deselectRoomWithIdentifier:roomIdentifier animated:YES];
} else {
[dataSource selectRoomWithIdentifier:roomIdentifier animated:YES];
}
[self.recentsTableView reloadData];
}
#pragma mark - MXKDataSourceDelegate
- (Class<MXKCellRendering>)cellViewClassForCellData:(MXKCellData*)cellData

View file

@ -33,9 +33,7 @@ typedef NS_ENUM(NSUInteger, ShareViewControllerAccountState) {
@protocol ShareViewControllerDelegate <NSObject>
- (void)shareViewControllerDidRequestShare:(ShareViewController *)shareViewController
forRoomIdentifier:(NSString *)roomIdentifier;
- (void)shareViewController:(ShareViewController *)shareViewController didRequestShareForRoomIdentifiers:(NSSet<NSString *> *)roomIdentifiers;
- (void)shareViewControllerDidRequestDismissal:(ShareViewController *)shareViewController;
@end
@ -48,8 +46,7 @@ typedef NS_ENUM(NSUInteger, ShareViewControllerAccountState) {
currentState:(ShareViewControllerAccountState)state;
- (void)configureWithState:(ShareViewControllerAccountState)state
roomDataSource:(nullable ShareDataSource *)roomDataSource
peopleDataSource:(nullable ShareDataSource *)peopleDataSource;
roomDataSource:(nullable ShareDataSource *)roomDataSource;
- (void)showProgressIndicator;

View file

@ -15,10 +15,9 @@
*/
#import "ShareViewController.h"
#import "SegmentedViewController.h"
#import "ShareDataSource.h"
#import "RoomsListViewController.h"
#import "FallbackViewController.h"
#import "ShareDataSource.h"
#import "ThemeService.h"
@ -28,13 +27,16 @@
#import "Riot-Swift.h"
#endif
@interface ShareViewController () <MXKRecentListViewControllerDelegate>
@interface ShareViewController () <MXKRecentListViewControllerDelegate, ShareDataSourceDelegate>
@property (nonatomic, assign, readonly) ShareViewControllerType type;
@property (nonatomic, assign) ShareViewControllerAccountState state;
@property (nonatomic, strong) RoomsListViewController *roomListViewController;
@property (nonatomic, strong) ShareDataSource *roomDataSource;
@property (nonatomic, strong) ShareDataSource *peopleDataSource;
@property (nonatomic, strong) FallbackViewController *fallbackViewController;
@property (nonatomic, weak) IBOutlet UIView *masterContainerView;
@property (nonatomic, weak) IBOutlet UIButton *cancelButton;
@ -42,8 +44,6 @@
@property (nonatomic, weak) IBOutlet UIButton *shareButton;
@property (nonatomic, weak) IBOutlet UIView *contentView;
@property (nonatomic, strong) SegmentedViewController *segmentedViewController;
@property (nonatomic, strong) MXKPieChartHUD *hudView;
@end
@ -76,17 +76,17 @@
[self.cancelButton setTitle:[VectorL10n cancel] forState:UIControlStateNormal];
[self.shareButton setTintColor:ThemeService.shared.theme.tintColor];
[self.shareButton setEnabled:NO];
[self configureWithState:self.state roomDataSource:self.roomDataSource peopleDataSource:self.peopleDataSource];
[self configureWithState:self.state roomDataSource:self.roomDataSource];
}
- (void)configureWithState:(ShareViewControllerAccountState)state
roomDataSource:(ShareDataSource *)roomDataSource
peopleDataSource:(ShareDataSource *)peopleDataSource
{
self.state = state;
self.roomDataSource = roomDataSource;
self.peopleDataSource = peopleDataSource;
self.roomDataSource.shareDelegate = self;
if (!self.isViewLoaded) {
return;
@ -110,19 +110,11 @@
[self.hudView setProgress:progress];
}
#pragma mark - MXKRecentListViewControllerDelegate
#pragma mark - ShareDataSourceDelegate
- (void)recentListViewController:(MXKRecentListViewController *)recentListViewController
didSelectRoom:(NSString *)roomId
inMatrixSession:(MXSession *)mxSession
- (void)shareDataSourceDidChangeSelectedRoomIdentifiers:(ShareDataSource *)shareDataSource
{
[self.delegate shareViewControllerDidRequestShare:self forRoomIdentifier:roomId];
}
- (void)recentListViewController:(MXKRecentListViewController *)recentListViewController
didSelectSuggestedRoom:(MXSpaceChildInfo *)childInfo
{
[self.delegate shareViewControllerDidRequestShare:self forRoomIdentifier:childInfo.childRoomId];
self.shareButton.enabled = (shareDataSource.selectedRoomIdentifiers.count > 0);
}
#pragma mark - Private
@ -133,60 +125,53 @@
if (self.state == ShareViewControllerAccountStateConfigured)
{
self.titleLabel.text = [VectorL10n sendTo:@""];
[self.shareButton setTitle:[VectorL10n roomEventActionForward] forState:UIControlStateNormal];
[self configureSegmentedViewController];
[self.shareButton setHidden:NO];
if (self.type == ShareViewControllerTypeSend) {
[self.titleLabel setText:[VectorL10n sendTo:@""]];
[self.shareButton setTitle:[VectorL10n sendTo:@""] forState:UIControlStateNormal];
} else {
[self.titleLabel setText:[VectorL10n roomEventActionForward]];
[self.shareButton setTitle:[VectorL10n roomEventActionForward] forState:UIControlStateNormal];
}
}
else
{
self.titleLabel.text = [AppInfo.current displayName];
[self configureFallbackViewController];
[self.shareButton setHidden:NO];
self.titleLabel.text = [AppInfo.current displayName];
}
}
- (void)configureSegmentedViewController
{
RoomsListViewController *roomsViewController = [RoomsListViewController recentListViewController];
[roomsViewController displayList:self.roomDataSource];
[roomsViewController setDelegate:self];
RoomsListViewController *peopleViewController = [RoomsListViewController recentListViewController];
[peopleViewController setDelegate:self];
[peopleViewController displayList:self.peopleDataSource];
self.segmentedViewController = [SegmentedViewController segmentedViewController];
[self.segmentedViewController initWithTitles:@[[VectorL10n titleRooms], [VectorL10n titlePeople]]
viewControllers:@[roomsViewController, peopleViewController] defaultSelected:0];
[self addChildViewController:self.segmentedViewController];
[self.contentView vc_addSubViewMatchingParent:self.segmentedViewController.view];
[self.segmentedViewController didMoveToParentViewController:self];
self.roomListViewController = [RoomsListViewController recentListViewController];
[self.roomListViewController displayList:self.roomDataSource];
[self addChildViewController:self.roomListViewController];
[self.contentView vc_addSubViewMatchingParent:self.roomListViewController.view];
[self.roomListViewController didMoveToParentViewController:self];
}
- (void)configureFallbackViewController
{
FallbackViewController *fallbackVC = [FallbackViewController new];
[self addChildViewController:fallbackVC];
[self.contentView vc_addSubViewMatchingParent:fallbackVC.view];
[fallbackVC didMoveToParentViewController:self];
self.fallbackViewController = [FallbackViewController new];
[self addChildViewController:self.fallbackViewController];
[self.contentView vc_addSubViewMatchingParent:self.fallbackViewController.view];
[self.fallbackViewController didMoveToParentViewController:self];
}
- (void)resetContentView
{
NSArray *subviews = self.contentView.subviews;
for (UIView *subview in subviews)
{
[subview removeFromSuperview];
}
[self.roomListViewController willMoveToParentViewController:nil];
[self.roomListViewController.view removeFromSuperview];
[self.roomListViewController removeFromParentViewController];
if (self.segmentedViewController)
{
[self.segmentedViewController removeFromParentViewController];
[self.segmentedViewController destroy];
self.segmentedViewController = nil;
}
[self.fallbackViewController willMoveToParentViewController:nil];
[self.fallbackViewController.view removeFromSuperview];
[self.fallbackViewController removeFromParentViewController];
}
#pragma mark - Actions
@ -198,7 +183,11 @@
- (IBAction)onShareButtonTap:(UIButton *)sender
{
if (self.roomDataSource.selectedRoomIdentifiers.count == 0) {
return;
}
[self.delegate shareViewController:self didRequestShareForRoomIdentifiers:self.roomDataSource.selectedRoomIdentifiers];
}
@end

View file

@ -42,7 +42,7 @@
<action selector="onCancelButtonTap:" destination="-1" eventType="touchUpInside" id="47D-fR-8PQ"/>
</connections>
</button>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="otu-Ag-Bwo">
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="otu-Ag-Bwo">
<rect key="frame" x="315" y="12" width="44" height="33"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<state key="normal" title="Share"/>

View file

@ -39,7 +39,7 @@
[ThemeService.shared setThemeId:RiotSettings.shared.userInterfaceTheme];
ShareExtensionShareItemProvider *provider = [[ShareExtensionShareItemProvider alloc] initWithExtensionContext:self.extensionContext];
_shareManager = [[ShareManager alloc] initWithShareItemProvider:provider];
_shareManager = [[ShareManager alloc] initWithShareItemProvider:provider type:ShareManagerTypeSend];
MXWeakify(self);
[_shareManager setCompletionCallback:^(ShareManagerResult result) {
@ -49,27 +49,28 @@
{
case ShareManagerResultFinished:
[self.extensionContext completeRequestReturningItems:nil completionHandler:nil];
[self _dismiss];
[self dismiss];
break;
case ShareManagerResultCancelled:
[self.extensionContext cancelRequestWithError:[NSError errorWithDomain:@"MXUserCancelErrorDomain" code:4201 userInfo:nil]];
[self _dismiss];
[self dismiss];
break;
case ShareManagerResultFailed:
[self.extensionContext cancelRequestWithError:[NSError errorWithDomain:@"MXFailureErrorDomain" code:500 userInfo:nil]];
[self _dismiss];
[self dismiss];
break;
default:
break;
}
}];
[self.shareManager.mainViewController setModalInPopover:YES];
[self presentViewController:self.shareManager.mainViewController animated:YES completion:nil];
}
#pragma mark - Private
- (void)_dismiss
- (void)dismiss
{
[self dismissViewControllerAnimated:true completion:^{
[self.presentingViewController dismissViewControllerAnimated:false completion:nil];

View file

@ -16,13 +16,7 @@
import Foundation
import MobileCoreServices
let UTTypeText = kUTTypeText as String
let UTTypeURL = kUTTypeURL as String
let UTTypeFileUrl = kUTTypeFileURL as String
let UTTypeImage = kUTTypeImage as String
let UTTypeVideo = kUTTypeVideo as String
let UTTypeMovie = kUTTypeMovie as String
import MatrixKit
private class ShareExtensionItem: ShareItemProtocol {
let itemProvider: NSItemProvider
@ -34,17 +28,17 @@ private class ShareExtensionItem: ShareItemProtocol {
}
var type: ShareItemType {
if itemProvider.hasItemConformingToTypeIdentifier(UTTypeText) {
if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.text.rawValue) {
return .text
} else if itemProvider.hasItemConformingToTypeIdentifier(UTTypeURL) {
} else if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.url.rawValue) {
return .URL
} else if itemProvider.hasItemConformingToTypeIdentifier(UTTypeFileUrl) {
} else if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.fileUrl.rawValue) {
return .fileURL
} else if itemProvider.hasItemConformingToTypeIdentifier(UTTypeImage) {
} else if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.image.rawValue) {
return .image
} else if itemProvider.hasItemConformingToTypeIdentifier(UTTypeVideo) {
} else if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.video.rawValue) {
return .video
} else if itemProvider.hasItemConformingToTypeIdentifier(UTTypeMovie) {
} else if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.movie.rawValue) {
return .movie
}
@ -105,7 +99,9 @@ class ShareExtensionShareItemProvider: NSObject, ShareItemProviderProtocol {
shareExtensionItem.loaded = true
}
completion(result, error)
DispatchQueue.main.async {
completion(result, error)
}
}
}
@ -114,19 +110,19 @@ class ShareExtensionShareItemProvider: NSObject, ShareItemProviderProtocol {
private func typeIdentifierForType(_ type: ShareItemType) -> String {
switch type {
case .text:
return UTTypeText
return MXKUTI.text.rawValue
case .URL:
return UTTypeURL
return MXKUTI.url.rawValue
case .fileURL:
return UTTypeFileUrl
return MXKUTI.fileUrl.rawValue
case .image:
return UTTypeImage
return MXKUTI.image.rawValue
case .video:
return UTTypeVideo
return MXKUTI.video.rawValue
case .movie:
return UTTypeMovie
return MXKUTI.movie.rawValue
case .voiceMessage:
return UTTypeFileUrl
return MXKUTI.fileUrl.rawValue
default:
return ""
}