From d68c0177943373e43928ca4400ca1382cbde8354 Mon Sep 17 00:00:00 2001 From: giomfo Date: Thu, 14 Apr 2016 02:34:30 +0200 Subject: [PATCH] Prepare Room Preview support. --- Vector.xcodeproj/project.pbxproj | 10 + Vector/Assets/en.lproj/Vector.strings | 8 +- Vector/ViewController/RoomViewController.h | 18 +- Vector/ViewController/RoomViewController.m | 406 +++++++++++++++--- Vector/ViewController/RoomViewController.xib | 16 +- .../RoomList/InviteRecentTableViewCell.m | 8 +- Vector/Views/RoomTitle/PreviewRoomTitleView.h | 37 ++ Vector/Views/RoomTitle/PreviewRoomTitleView.m | 158 +++++++ .../Views/RoomTitle/PreviewRoomTitleView.xib | 152 +++++++ Vector/Views/RoomTitle/RoomAvatarTitleView.h | 2 + Vector/Views/RoomTitle/RoomAvatarTitleView.m | 11 + Vector/Views/RoomTitle/RoomTitleView.h | 5 + Vector/Views/RoomTitle/RoomTitleView.m | 30 +- 13 files changed, 787 insertions(+), 74 deletions(-) create mode 100644 Vector/Views/RoomTitle/PreviewRoomTitleView.h create mode 100644 Vector/Views/RoomTitle/PreviewRoomTitleView.m create mode 100644 Vector/Views/RoomTitle/PreviewRoomTitleView.xib diff --git a/Vector.xcodeproj/project.pbxproj b/Vector.xcodeproj/project.pbxproj index 8b55a7d3c..2c5902b5b 100644 --- a/Vector.xcodeproj/project.pbxproj +++ b/Vector.xcodeproj/project.pbxproj @@ -134,6 +134,8 @@ F025290C1C11B6FC00E1FE1B /* voice_call_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = F02528D01C11B6FC00E1FE1B /* voice_call_icon.png */; }; F025290D1C11B6FC00E1FE1B /* voice_call_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F02528D11C11B6FC00E1FE1B /* voice_call_icon@2x.png */; }; F025290E1C11B6FC00E1FE1B /* voice_call_icon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = F02528D21C11B6FC00E1FE1B /* voice_call_icon@3x.png */; }; + F02BB04B1CBE2EE70022A025 /* PreviewRoomTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = F02BB0491CBE2EE70022A025 /* PreviewRoomTitleView.m */; }; + F02BB04C1CBE2EE70022A025 /* PreviewRoomTitleView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F02BB04A1CBE2EE70022A025 /* PreviewRoomTitleView.xib */; }; F02D87C69D1FFCD2C1531F3D /* libPods-Vector.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B179239B79688A61A3F465F /* libPods-Vector.a */; }; F03FBCC51CBBF521000A5770 /* admin_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = F03FBCB31CBBF521000A5770 /* admin_icon.png */; }; F03FBCC61CBBF521000A5770 /* admin_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F03FBCB41CBBF521000A5770 /* admin_icon@2x.png */; }; @@ -414,6 +416,9 @@ F02528D01C11B6FC00E1FE1B /* voice_call_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voice_call_icon.png; sourceTree = ""; }; F02528D11C11B6FC00E1FE1B /* voice_call_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "voice_call_icon@2x.png"; sourceTree = ""; }; F02528D21C11B6FC00E1FE1B /* voice_call_icon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "voice_call_icon@3x.png"; sourceTree = ""; }; + F02BB0481CBE2EE70022A025 /* PreviewRoomTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PreviewRoomTitleView.h; path = RoomTitle/PreviewRoomTitleView.h; sourceTree = ""; }; + F02BB0491CBE2EE70022A025 /* PreviewRoomTitleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PreviewRoomTitleView.m; path = RoomTitle/PreviewRoomTitleView.m; sourceTree = ""; }; + F02BB04A1CBE2EE70022A025 /* PreviewRoomTitleView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = PreviewRoomTitleView.xib; path = RoomTitle/PreviewRoomTitleView.xib; sourceTree = ""; }; F03FBCB31CBBF521000A5770 /* admin_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = admin_icon.png; sourceTree = ""; }; F03FBCB41CBBF521000A5770 /* admin_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "admin_icon@2x.png"; sourceTree = ""; }; F03FBCB51CBBF521000A5770 /* admin_icon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "admin_icon@3x.png"; sourceTree = ""; }; @@ -585,6 +590,9 @@ 71046D581C0C631100DCA984 /* RoomTitle */ = { isa = PBXGroup; children = ( + F02BB0481CBE2EE70022A025 /* PreviewRoomTitleView.h */, + F02BB0491CBE2EE70022A025 /* PreviewRoomTitleView.m */, + F02BB04A1CBE2EE70022A025 /* PreviewRoomTitleView.xib */, 32A8871E1C89B9580037DC17 /* SimpleRoomTitleView.h */, 32A8871F1C89B9580037DC17 /* SimpleRoomTitleView.m */, 32A887201C89B9580037DC17 /* SimpleRoomTitleView.xib */, @@ -1214,6 +1222,7 @@ F02528D81C11B6FC00E1FE1B /* camera_picture.png in Resources */, F02528E01C11B6FC00E1FE1B /* create_room.png in Resources */, F02528DF1C11B6FC00E1FE1B /* camera_video.png in Resources */, + F02BB04C1CBE2EE70022A025 /* PreviewRoomTitleView.xib in Resources */, F056418C1C7CBEBD002276ED /* group.png in Resources */, F0C34B681C15C28300C36F09 /* RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib in Resources */, F0026B631C916E68001D2C04 /* priorityLow@3x.png in Resources */, @@ -1481,6 +1490,7 @@ 71B2A3BB1C2013DC00472061 /* TableViewCellWithLabelAndMXKImageView.m in Sources */, F0D2D9851C197DCB007B8C96 /* RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m in Sources */, 71046D5E1C0C639300DCA984 /* RoomTitleView.m in Sources */, + F02BB04B1CBE2EE70022A025 /* PreviewRoomTitleView.m in Sources */, F0C34B631C15C28300C36F09 /* RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m in Sources */, F09EE0061C5134BE0078712F /* RoomOutgoingTextMsgWithoutSenderNameBubbleCell.m in Sources */, F09EE0081C5134BE0078712F /* RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, diff --git a/Vector/Assets/en.lproj/Vector.strings b/Vector/Assets/en.lproj/Vector.strings index 20467351a..ae54bce0f 100644 --- a/Vector/Assets/en.lproj/Vector.strings +++ b/Vector/Assets/en.lproj/Vector.strings @@ -33,7 +33,8 @@ "cancel" = "Cancel"; "save" = "Save"; "join" = "Join"; -"reject" = "Reject"; +"decline" = "Decline"; +"preview" = "Preview"; "camera" = "Camera"; "voice" = "Voice"; "video" = "Video"; @@ -152,6 +153,11 @@ "room_title_multiple_active_members" = "%d/%d active members"; "room_title_one_active_member" = "%d/%d active member"; +// Room Preview +"room_preview_invitation_format" = "You have been invited to join this room by %@"; +"room_preview_subtitle" = "This is a preview of this room. Files and other attachments have been disabled."; +"room_preview_unlinked_email_warning" = "This invitation was sent to %@, which is not associated with this account.\nYou may wish to login with a different account, or add this email to your this account."; + // Settings "account_logout_all" = "Logout all accounts"; diff --git a/Vector/ViewController/RoomViewController.h b/Vector/ViewController/RoomViewController.h index 562562e17..9e7f1e78c 100644 --- a/Vector/ViewController/RoomViewController.h +++ b/Vector/ViewController/RoomViewController.h @@ -18,20 +18,32 @@ #import "RoomTitleView.h" +#import "RoomEmailInvitation.h" + #import "UIViewController+VectorSearch.h" @interface RoomViewController : MXKRoomViewController // The expanded header @property (weak, nonatomic) IBOutlet UIView *expandedHeaderContainer; - @property (weak, nonatomic) IBOutlet NSLayoutConstraint *expandedHeaderContainerHeightConstraint; +// The preview header +@property (weak, nonatomic) IBOutlet UIView *previewHeaderContainer; +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *previewHeaderContainerHeightConstraint; + /** - Hide/Show the expanded header. + Show/Hide the expanded header. By default this header is hidden on new instantiated RoomViewController object. */ -- (void)hideExpandedHeader:(BOOL)isHidden; +- (void)showExpandedHeader:(BOOL)isVisible; + +/** + Display an invitation preview. + + @param emailInvitation the invitation received by email. + */ +- (void)displayEmailInvitation:(RoomEmailInvitation*)emailInvitation; @end diff --git a/Vector/ViewController/RoomViewController.m b/Vector/ViewController/RoomViewController.m index 734126f1c..f5b6d87ec 100644 --- a/Vector/ViewController/RoomViewController.m +++ b/Vector/ViewController/RoomViewController.m @@ -31,6 +31,7 @@ #import "RoomAvatarTitleView.h" #import "ExpandedRoomTitleView.h" #import "SimpleRoomTitleView.h" +#import "PreviewRoomTitleView.h" #import "RoomParticipantsViewController.h" @@ -67,6 +68,9 @@ // The expanded header ExpandedRoomTitleView *expandedHeader; + // The preview header + PreviewRoomTitleView *previewHeader; + // The content offset at the beginning of scrolling CGFloat storedContentOffset; @@ -85,6 +89,9 @@ // The first tab is selected by default in room details screen in of case 'showRoomDetails' segue. // Use this flag to select a specific tab (0: people, 1: settings). NSUInteger selectedRoomDetailsIndex; + + // The room invitation received by email + RoomEmailInvitation *roomEmailInvitation; } @property (strong, nonatomic) MXKAlert *currentAlert; @@ -109,6 +116,32 @@ #pragma mark - +- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) + { + // Disable auto join + self.autoJoinInvitedRoom = NO; + } + + return self; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) + { + // Disable auto join + self.autoJoinInvitedRoom = NO; + } + + return self; +} + +#pragma mark - + - (void)viewDidLoad { [super viewDidLoad]; @@ -177,6 +210,10 @@ [NSLayoutConstraint activateConstraints:@[leftConstraint, rightConstraint, topConstraint, bottomConstraint]]; + // Prepare preview header container + self.previewHeaderContainer.backgroundColor = kVectorColorLightGrey; + self.previewHeaderContainerHeightConstraint.constant = 368; + // Replace the default input toolbar view. // Note: this operation will force the layout of subviews. That is why cell view classes must be registered before. [self setRoomInputToolbarViewClass:RoomInputToolbarView.class]; @@ -200,21 +237,7 @@ if (self.roomDataSource) { // Set room title view - if (self.roomDataSource.isLive) - { - self.navigationItem.rightBarButtonItem.enabled = YES; - - [self setRoomTitleViewClass:RoomTitleView.class]; - ((RoomTitleView*)self.titleView).tapGestureDelegate = self; - } - else - { - // Hide the search button - self.navigationItem.rightBarButtonItem = nil; - - [self setRoomTitleViewClass:SimpleRoomTitleView.class]; - self.titleView.editable = NO; - } + [self refreshRoomTitle]; [self refreshRoomInputToolbar]; } @@ -259,8 +282,9 @@ } } - // Hide expanded header to restore navigation bar settings - [self hideExpandedHeader:YES]; + // Hide expanded/preview header to restore navigation bar settings + [self showExpandedHeader:NO]; + [self showPreviewHeader:NO]; } - (void)viewDidAppear:(BOOL)animated @@ -316,7 +340,19 @@ - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id )coordinator { // Hide expanded header on device rotation - [self hideExpandedHeader:YES]; + [self showExpandedHeader:NO]; + + // Hide preview header (if any) during device rotation + BOOL isPreview = !self.previewHeaderContainer.isHidden; + if (isPreview) + { + [self showPreviewHeader:NO]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((coordinator.transitionDuration + 0.5) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + + [self showPreviewHeader:YES]; + }); + } [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; } @@ -333,22 +369,9 @@ { // This room view controller has its own typing management. self.roomDataSource.showTypingNotifications = NO; - - if (self.roomDataSource.isLive) - { - self.navigationItem.rightBarButtonItem.enabled = YES; - - [self setRoomTitleViewClass:RoomTitleView.class]; - ((RoomTitleView*)self.titleView).tapGestureDelegate = self; - } - else - { - // Hide the search button - self.navigationItem.rightBarButtonItem = nil; - - [self setRoomTitleViewClass:SimpleRoomTitleView.class]; - self.titleView.editable = NO; - } + + // Set room title view + [self refreshRoomTitle]; // Store ref on customized room data source if ([dataSource isKindOfClass:RoomDataSource.class]) @@ -364,15 +387,46 @@ [self refreshRoomInputToolbar]; } +- (void)onRoomDataSourceReady +{ + // Handle here invitation + if (self.roomDataSource.room.state.membership == MXMembershipInvite) + { + self.navigationItem.rightBarButtonItem.enabled = NO; + + // Show preview header + [self showPreviewHeader:YES]; + } + else + { + [super onRoomDataSourceReady]; + } +} + - (void)updateViewControllerAppearanceOnRoomDataSourceState { [super updateViewControllerAppearanceOnRoomDataSourceState]; - self.navigationItem.rightBarButtonItem.enabled = (self.roomDataSource != nil); - - self.titleView.editable = NO; - - expandedHeader.mxRoom = self.roomDataSource.room; + if (self.isRoomPreview) + { + self.navigationItem.rightBarButtonItem.enabled = NO; + + // Hide input tool bar and activity view. FIXME: These items should be removed instead of being hidden until they will be used for preview. + self.inputToolbarView.hidden = YES; + self.activitiesView.hidden = YES; + + previewHeader.mxRoom = self.roomDataSource.room; + } + else + { + [self showPreviewHeader:NO]; + + self.navigationItem.rightBarButtonItem.enabled = (self.roomDataSource != nil); + + self.titleView.editable = NO; + + expandedHeader.mxRoom = self.roomDataSource.room; + } } - (BOOL)isIRCStyleCommand:(NSString*)string @@ -420,7 +474,7 @@ // Dispatch this operation to prevent flickering in navigation bar. dispatch_async(dispatch_get_main_queue(), ^{ - [self hideExpandedHeader:YES]; + [self showExpandedHeader:NO]; }); } @@ -442,11 +496,71 @@ customizedRoomDataSource = nil; } + if (expandedHeader) + { + [expandedHeader removeFromSuperview]; + expandedHeader = nil; + } + + if (previewHeader) + { + [previewHeader removeFromSuperview]; + previewHeader = nil; + } + [super destroy]; } #pragma mark - Internals +- (BOOL)isRoomPreview +{ + if (self.roomDataSource && self.roomDataSource.state == MXKDataSourceStateReady && self.roomDataSource.room.state.membership == MXMembershipInvite) + { + return YES; + } + + if (roomEmailInvitation) + { + return YES; + } + + return NO; +} + +- (void)refreshRoomTitle +{ + // Set the right room title view + if (self.isRoomPreview) + { + // Disable the search button + self.navigationItem.rightBarButtonItem.enabled = NO; + + [self showPreviewHeader:YES]; + } + else if (self.roomDataSource) + { + [self showPreviewHeader:NO]; + + if (self.roomDataSource.isLive) + { + // Enable the search button + self.navigationItem.rightBarButtonItem.enabled = YES; + + [self setRoomTitleViewClass:RoomTitleView.class]; + ((RoomTitleView*)self.titleView).tapGestureDelegate = self; + } + else + { + // Hide the search button + self.navigationItem.rightBarButtonItem = nil; + + [self setRoomTitleViewClass:SimpleRoomTitleView.class]; + self.titleView.editable = NO; + } + } +} + - (void)refreshRoomInputToolbar { // Check whether the input toolbar is ready before updating it. @@ -478,16 +592,17 @@ #pragma mark - Hide/Show expanded header -- (void)hideExpandedHeader:(BOOL)isHidden +- (void)showExpandedHeader:(BOOL)isVisible { // Check conditions before applying change on room header. // This operation is ignored: // - if a screen rotation is in progress. // - if the room data source has been removed. // - if the room data source does not manage a live timeline. - if (self.expandedHeaderContainer.isHidden != isHidden && isSizeTransitionInProgress == NO && self.roomDataSource && self.roomDataSource.isLive) + // - if the user's membership is not 'join'. + if (self.expandedHeaderContainer.isHidden == isVisible && isSizeTransitionInProgress == NO && self.roomDataSource && self.roomDataSource.isLive && self.roomDataSource.room.state.membership == MXMembershipJoin) { - self.expandedHeaderContainer.hidden = isHidden; + self.expandedHeaderContainer.hidden = !isVisible; // Consider the main navigation controller if the current view controller is embedded inside a split view controller. UINavigationController *mainNavigationController = self.navigationController; @@ -504,12 +619,7 @@ UIImage *shadowImage = nil; MXKImageView *roomAvatarView = nil; - if (isHidden) - { - [self setRoomTitleViewClass:RoomTitleView.class]; - ((RoomTitleView*)self.titleView).tapGestureDelegate = self; - } - else + if (isVisible) { [self setRoomTitleViewClass:RoomAvatarTitleView.class]; // Note the avatar title view does not define tap gesture. @@ -522,6 +632,11 @@ // Dismiss the keyboard when header is expanded. [self.inputToolbarView dismissKeyboard]; } + else + { + [self setRoomTitleViewClass:RoomTitleView.class]; + ((RoomTitleView*)self.titleView).tapGestureDelegate = self; + } // Report shadow image [mainNavigationController.navigationBar setShadowImage:shadowImage]; @@ -529,7 +644,7 @@ [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn animations:^{ - self.bubblesTableViewTopConstraint.constant = (isHidden ? 0 : self.expandedHeaderContainerHeightConstraint.constant - self.bubblesTableView.contentInset.top); + self.bubblesTableViewTopConstraint.constant = (isVisible ? self.expandedHeaderContainerHeightConstraint.constant - self.bubblesTableView.contentInset.top : 0); if (roomAvatarView) { @@ -544,6 +659,149 @@ } } +#pragma mark - Hide/Show preview header + +- (void)showPreviewHeader:(BOOL)isVisible +{ + // Check conditions before applying change on room header. + // This operation is ignored when a screen rotation is in progress. + if (self.previewHeaderContainer.isHidden == isVisible && isSizeTransitionInProgress == NO) + { + if (isVisible && !previewHeader) + { + previewHeader = [PreviewRoomTitleView roomTitleView]; + previewHeader.delegate = self; + previewHeader.tapGestureDelegate = self; + previewHeader.translatesAutoresizingMaskIntoConstraints = NO; + [self.previewHeaderContainer addSubview:previewHeader]; + // Force preview header in full width + NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:previewHeader + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.previewHeaderContainer + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0]; + NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:previewHeader + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.previewHeaderContainer + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0]; + // Vertical constraints are required for iOS > 8 + NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:previewHeader + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.previewHeaderContainer + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0]; + NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:previewHeader + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.previewHeaderContainer + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0]; + + [NSLayoutConstraint activateConstraints:@[leftConstraint, rightConstraint, topConstraint, bottomConstraint]]; + + if (self.roomDataSource) + { + previewHeader.mxRoom = self.roomDataSource.room; + } + else + { + previewHeader.emailInvitation = roomEmailInvitation; + } + } + + self.previewHeaderContainer.hidden = !isVisible; + + // Consider the main navigation controller if the current view controller is embedded inside a split view controller. + UINavigationController *mainNavigationController = self.navigationController; + if (self.splitViewController && self.splitViewController.isCollapsed && self.splitViewController.viewControllers.count) + { + mainNavigationController = self.splitViewController.viewControllers.firstObject; + } + + // When the expanded header is displayed, we hide the bottom border of the navigation bar (the shadow image). + // The default shadow image is nil. When non-nil, this property represents a custom shadow image to show instead + // of the default. For a custom shadow image to be shown, a custom background image must also be set with the + // setBackgroundImage:forBarMetrics: method. If the default background image is used, then the default shadow + // image will be used regardless of the value of this property. + UIImage *shadowImage = nil; + MXKImageView *roomAvatarView = nil; + + if (isVisible) + { + [self setRoomTitleViewClass:RoomAvatarTitleView.class]; + // Note the avatar title view does not define tap gesture. + + roomAvatarView = ((RoomAvatarTitleView*)self.titleView).roomAvatar; + roomAvatarView.alpha = 0.0; + + shadowImage = [[UIImage alloc] init]; + } + else + { + [self setRoomTitleViewClass:RoomTitleView.class]; + // We don't want to handle tap gesture here + } + + // Report shadow image + [mainNavigationController.navigationBar setShadowImage:shadowImage]; + [mainNavigationController.navigationBar setBackgroundImage:shadowImage forBarMetrics:UIBarMetricsDefault]; + + [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn + animations:^{ + self.bubblesTableViewTopConstraint.constant = (isVisible ? self.previewHeaderContainerHeightConstraint.constant - self.bubblesTableView.contentInset.top : 0); + + if (roomAvatarView) + { + roomAvatarView.alpha = 1; + } + + // Force to render the view + [self.view layoutIfNeeded]; + } + completion:^(BOOL finished){ + }]; + } +} + +#pragma mark - Preview + +- (void)displayEmailInvitation:(RoomEmailInvitation*)emailInvitation +{ + if (emailInvitation) + { + roomEmailInvitation = emailInvitation; + previewHeader.emailInvitation = emailInvitation; + + [self refreshRoomTitle]; + + if (emailInvitation.roomAvatarUrl) + { + if ([self.titleView isKindOfClass:RoomAvatarTitleView.class]) + { + RoomAvatarTitleView *roomAvatarTitleView = (RoomAvatarTitleView*)self.titleView; + MXKImageView *roomAvatarView = roomAvatarTitleView.roomAvatar; + NSString *roomAvatarUrl = [self.roomDataSource.mxSession.matrixRestClient urlOfContentThumbnail:emailInvitation.roomAvatarUrl toFitViewSize:roomAvatarView.frame.size withMethod:MXThumbnailingMethodCrop]; + + roomAvatarTitleView.roomAvatarURL = roomAvatarUrl; + } + } + + if (emailInvitation.email) + { + // FIXME: Check whether the email is linked or not to the account + // If it is not linked, use 'previewHeader.subInvitationLabel' to warm the user with predefined string 'room_preview_unlinked_email_warning'. + } + } +} + #pragma mark - MXKDataSourceDelegate - (Class)cellViewClassForCellData:(MXKCellData*)cellData @@ -1123,7 +1381,7 @@ // Hide expanded header on scroll down if (storedContentOffset < scrollView.contentOffset.y) { - [self hideExpandedHeader:YES]; + [self showExpandedHeader:NO]; } } @@ -1146,7 +1404,7 @@ if (self.expandedHeaderContainer.isHidden) { // Expand the header - [self hideExpandedHeader:NO]; + [self showExpandedHeader:YES]; } else { @@ -1161,6 +1419,50 @@ selectedRoomDetailsIndex = 0; [self performSegueWithIdentifier:@"showRoomDetails" sender:self]; } + else if (view == previewHeader.leftButton) + { + if (roomEmailInvitation) + { + //FIXME Accept the invitation + } + else + { + [self joinRoom:^(BOOL succeed) { + + if (succeed) + { + [self refreshRoomTitle]; + } + + }]; + } + } + else if (view == previewHeader.rightButton) + { + if (roomEmailInvitation) + { + //FIXME Decline this invitation + } + else + { + [self startActivityIndicator]; + + [self.roomDataSource.room leave:^{ + + [self stopActivityIndicator]; + + // We remove the current view controller. + // Pop to homes view controller + [[AppDelegate theDelegate] restoreInitialDisplay:^{}]; + + } failure:^(NSError *error) { + + [self stopActivityIndicator]; + NSLog(@"[Vector RoomVC] Failed to reject an invited room (%@) failed", self.roomDataSource.room.state.roomId); + + }]; + } + } } #pragma mark - Typing management diff --git a/Vector/ViewController/RoomViewController.xib b/Vector/ViewController/RoomViewController.xib index cbd68153f..c0a8e13c2 100644 --- a/Vector/ViewController/RoomViewController.xib +++ b/Vector/ViewController/RoomViewController.xib @@ -1,8 +1,8 @@ - + - + @@ -12,6 +12,8 @@ + + @@ -36,6 +38,13 @@ + @@ -57,15 +66,18 @@ + + + diff --git a/Vector/Views/RoomList/InviteRecentTableViewCell.m b/Vector/Views/RoomList/InviteRecentTableViewCell.m index 129a28a22..3cb0a30d4 100644 --- a/Vector/Views/RoomList/InviteRecentTableViewCell.m +++ b/Vector/Views/RoomList/InviteRecentTableViewCell.m @@ -34,16 +34,16 @@ [self.leftButton.layer setCornerRadius:5]; self.leftButton.clipsToBounds = YES; self.leftButton.backgroundColor = kVectorColorGreen; - [self.leftButton setTitle:NSLocalizedStringFromTable(@"join", @"Vector", nil) forState:UIControlStateNormal]; - [self.leftButton setTitle:NSLocalizedStringFromTable(@"join", @"Vector", nil) forState:UIControlStateHighlighted]; + [self.leftButton setTitle:NSLocalizedStringFromTable(@"preview", @"Vector", nil) forState:UIControlStateNormal]; + [self.leftButton setTitle:NSLocalizedStringFromTable(@"preview", @"Vector", nil) forState:UIControlStateHighlighted]; [self.leftButton addTarget:self action:@selector(onJoinPressed:) forControlEvents:UIControlEventTouchUpInside]; [self.rightButton.layer setCornerRadius:5]; self.rightButton.clipsToBounds = YES; self.rightButton.backgroundColor = kVectorColorGreen; - [self.rightButton setTitle:NSLocalizedStringFromTable(@"reject", @"Vector", nil) forState:UIControlStateNormal]; - [self.rightButton setTitle:NSLocalizedStringFromTable(@"reject", @"Vector", nil) forState:UIControlStateHighlighted]; + [self.rightButton setTitle:NSLocalizedStringFromTable(@"decline", @"Vector", nil) forState:UIControlStateNormal]; + [self.rightButton setTitle:NSLocalizedStringFromTable(@"decline", @"Vector", nil) forState:UIControlStateHighlighted]; [self.rightButton addTarget:self action:@selector(onRejectedPressed:) forControlEvents:UIControlEventTouchUpInside]; self.selectionStyle = UITableViewCellSelectionStyleNone; diff --git a/Vector/Views/RoomTitle/PreviewRoomTitleView.h b/Vector/Views/RoomTitle/PreviewRoomTitleView.h new file mode 100644 index 000000000..5996e0bc0 --- /dev/null +++ b/Vector/Views/RoomTitle/PreviewRoomTitleView.h @@ -0,0 +1,37 @@ +/* + Copyright 2016 OpenMarket Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "RoomTitleView.h" + +#import "RoomEmailInvitation.h" + +@interface PreviewRoomTitleView : RoomTitleView + +@property (weak, nonatomic) IBOutlet UIView *mainHeaderContainer; + +@property (weak, nonatomic) IBOutlet UILabel *roomMembers; +@property (weak, nonatomic) IBOutlet UIView *roomMembersDetailsIcon; + +@property (weak, nonatomic) IBOutlet UILabel *invitationLabel; +@property (weak, nonatomic) IBOutlet UIView *buttonsContainer; +@property (weak, nonatomic) IBOutlet UIButton *leftButton; +@property (weak, nonatomic) IBOutlet UIButton *rightButton; +@property (weak, nonatomic) IBOutlet UILabel *subInvitationLabel; +@property (weak, nonatomic) IBOutlet UIView *bottomBorderView; + +@property (strong, nonatomic) RoomEmailInvitation *emailInvitation; + +@end \ No newline at end of file diff --git a/Vector/Views/RoomTitle/PreviewRoomTitleView.m b/Vector/Views/RoomTitle/PreviewRoomTitleView.m new file mode 100644 index 000000000..9092a0ed6 --- /dev/null +++ b/Vector/Views/RoomTitle/PreviewRoomTitleView.m @@ -0,0 +1,158 @@ +/* + Copyright 2016 OpenMarket Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PreviewRoomTitleView.h" + +#import "VectorDesignValues.h" + +#import "MXRoom+Vector.h" + +@implementation PreviewRoomTitleView + ++ (UINib *)nib +{ + return [UINib nibWithNibName:NSStringFromClass([self class]) + bundle:[NSBundle bundleForClass:[self class]]]; +} + +- (void)awakeFromNib +{ + [super awakeFromNib]; + + self.displayNameTextField.textColor = kVectorTextColorBlack; + self.roomMembers.textColor = kVectorColorGreen; + + self.invitationLabel.textColor = kVectorTextColorDarkGray; + self.invitationLabel.numberOfLines = 0; + self.subInvitationLabel.text = nil; + + self.subInvitationLabel.textColor = kVectorTextColorGray; + self.subInvitationLabel.numberOfLines = 0; + + self.subInvitationLabel.text = nil;// FIXME: Use NSLocalizedStringFromTable(@"room_preview_subtitle", @"Vector", nil); + + self.bottomBorderView.backgroundColor = kVectorColorLightGrey; + + [self.leftButton.layer setCornerRadius:5]; + self.leftButton.clipsToBounds = YES; + self.leftButton.backgroundColor = kVectorColorGreen; + [self.leftButton setTitle:NSLocalizedStringFromTable(@"join", @"Vector", nil) forState:UIControlStateNormal]; + [self.leftButton setTitle:NSLocalizedStringFromTable(@"join", @"Vector", nil) forState:UIControlStateHighlighted]; + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reportTapGesture:)]; + [tap setNumberOfTouchesRequired:1]; + [tap setNumberOfTapsRequired:1]; + [tap setDelegate:self]; + [self.leftButton addGestureRecognizer:tap]; + self.leftButton.userInteractionEnabled = YES; + + [self.rightButton.layer setCornerRadius:5]; + self.rightButton.clipsToBounds = YES; + self.rightButton.backgroundColor = kVectorColorGreen; + [self.rightButton setTitle:NSLocalizedStringFromTable(@"decline", @"Vector", nil) forState:UIControlStateNormal]; + [self.rightButton setTitle:NSLocalizedStringFromTable(@"decline", @"Vector", nil) forState:UIControlStateHighlighted]; + tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reportTapGesture:)]; + [tap setNumberOfTouchesRequired:1]; + [tap setNumberOfTapsRequired:1]; + [tap setDelegate:self]; + [self.rightButton addGestureRecognizer:tap]; + self.rightButton.userInteractionEnabled = YES; +} + +- (void)refreshDisplay +{ + [super refreshDisplay]; + + if (self.mxRoom) + { + self.displayNameTextField.text = self.mxRoom.vectorDisplayname; + if (!self.displayNameTextField.text.length) + { + self.displayNameTextField.text = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil); + self.displayNameTextField.textColor = kVectorTextColorGray; + } + else + { + self.displayNameTextField.textColor = kVectorTextColorBlack; + } + + // Compute active members count, and look for the inviter + NSArray *members = self.mxRoom.state.members; + NSUInteger activeCount = 0; + NSUInteger memberCount = 0; + NSString *inviter = nil; + + for (MXRoomMember *mxMember in members) + { + if (mxMember.membership == MXMembershipJoin) + { + memberCount ++; + + // Get the user that corresponds to this member + MXUser *user = [self.mxRoom.mxSession userWithUserId:mxMember.userId]; + // existing user ? + if (user && user.presence == MXPresenceOnline) + { + activeCount ++; + } + + // Presently only one member is available from invited rom data + // This is the inviter + inviter = mxMember.displayname.length ? mxMember.displayname : mxMember.userId; + } + } + + // FIXME: Display members status when it will be available + self.roomMembers.text = nil; +// if (memberCount) +// { +// if (activeCount > 1) +// { +// self.roomMembers.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_title_multiple_active_members", @"Vector", nil), activeCount, memberCount]; +// } +// else +// { +// self.roomMembers.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_title_one_active_member", @"Vector", nil), activeCount, memberCount]; +// } +// } +// else +// { +// // Should not happen +// self.roomMembers.text = nil; +// } + + self.invitationLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_preview_invitation_format", @"Vector", nil), inviter]; + } + else if (self.emailInvitation) + { + self.displayNameTextField.text = self.emailInvitation.roomName; + self.roomMembers.text = nil; + self.invitationLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_preview_invitation_format", @"Vector", nil), self.emailInvitation.inviterName]; + } + else + { + self.roomMembers.text = nil; + self.invitationLabel.text = nil; + } +} + +- (void)setEmailInvitation:(RoomEmailInvitation *)emailInvitation +{ + _emailInvitation = emailInvitation; + + [self refreshDisplay]; +} + +@end diff --git a/Vector/Views/RoomTitle/PreviewRoomTitleView.xib b/Vector/Views/RoomTitle/PreviewRoomTitleView.xib new file mode 100644 index 000000000..0a125cd14 --- /dev/null +++ b/Vector/Views/RoomTitle/PreviewRoomTitleView.xib @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Vector/Views/RoomTitle/RoomAvatarTitleView.h b/Vector/Views/RoomTitle/RoomAvatarTitleView.h index 0b64ce86a..c354d7fab 100644 --- a/Vector/Views/RoomTitle/RoomAvatarTitleView.h +++ b/Vector/Views/RoomTitle/RoomAvatarTitleView.h @@ -21,4 +21,6 @@ @property (weak, nonatomic) IBOutlet MXKImageView *roomAvatar; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomAvatarCenterXConstraint; +@property (nonatomic) NSString *roomAvatarURL; + @end \ No newline at end of file diff --git a/Vector/Views/RoomTitle/RoomAvatarTitleView.m b/Vector/Views/RoomTitle/RoomAvatarTitleView.m index 58a08e21b..e3d05c31b 100644 --- a/Vector/Views/RoomTitle/RoomAvatarTitleView.m +++ b/Vector/Views/RoomTitle/RoomAvatarTitleView.m @@ -77,6 +77,10 @@ self.roomAvatar.clipsToBounds = YES; } + else if (self.roomAvatarURL) + { + [self.roomAvatar setImageURL:self.roomAvatarURL withType:nil andImageOrientation:UIImageOrientationUp previewImage:[UIImage imageNamed:@"placeholder"]]; + } else { self.roomAvatar.image = [UIImage imageNamed:@"placeholder"]; @@ -85,4 +89,11 @@ self.roomAvatar.backgroundColor = kVectorColorLightGrey; } +- (void)setRoomAvatarURL:(NSString *)roomAvatarURL +{ + _roomAvatarURL = roomAvatarURL; + + [self refreshDisplay]; +} + @end diff --git a/Vector/Views/RoomTitle/RoomTitleView.h b/Vector/Views/RoomTitle/RoomTitleView.h index eead0112b..843a7abff 100644 --- a/Vector/Views/RoomTitle/RoomTitleView.h +++ b/Vector/Views/RoomTitle/RoomTitleView.h @@ -41,4 +41,9 @@ */ @property (nonatomic) id tapGestureDelegate; +/** + The method used to handle the gesture recognized by a receiver. + */ +- (void)reportTapGesture:(UITapGestureRecognizer*)tapGestureRecognizer; + @end \ No newline at end of file diff --git a/Vector/Views/RoomTitle/RoomTitleView.m b/Vector/Views/RoomTitle/RoomTitleView.m index dda205d22..55d099b5b 100644 --- a/Vector/Views/RoomTitle/RoomTitleView.m +++ b/Vector/Views/RoomTitle/RoomTitleView.m @@ -34,19 +34,25 @@ self.displayNameTextField.textColor = kVectorTextColorBlack; - UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reportTapGesture:)]; - [tap setNumberOfTouchesRequired:1]; - [tap setNumberOfTapsRequired:1]; - [tap setDelegate:self]; - [self.titleMask addGestureRecognizer:tap]; - self.titleMask.userInteractionEnabled = YES; + if (_titleMask) + { + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reportTapGesture:)]; + [tap setNumberOfTouchesRequired:1]; + [tap setNumberOfTapsRequired:1]; + [tap setDelegate:self]; + [self.titleMask addGestureRecognizer:tap]; + self.titleMask.userInteractionEnabled = YES; + } - tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reportTapGesture:)]; - [tap setNumberOfTouchesRequired:1]; - [tap setNumberOfTapsRequired:1]; - [tap setDelegate:self]; - [self.roomDetailsMask addGestureRecognizer:tap]; - self.roomDetailsMask.userInteractionEnabled = YES; + if (_roomDetailsMask) + { + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reportTapGesture:)]; + [tap setNumberOfTouchesRequired:1]; + [tap setNumberOfTapsRequired:1]; + [tap setDelegate:self]; + [self.roomDetailsMask addGestureRecognizer:tap]; + self.roomDetailsMask.userInteractionEnabled = YES; + } } - (void)layoutSubviews