From 938ff26a2b6d6e5f48bc1c0dc75bb07276866eaf Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 21 Feb 2018 14:03:24 +0100 Subject: [PATCH 1/2] Room: Inform user when they cannot post to a room because of low power level Create DisabledRoomInputToolbarView for such permission issue --- Riot.xcodeproj/project.pbxproj | 10 ++ .../DisabledRoomInputToolbarView.h | 40 ++++++++ .../DisabledRoomInputToolbarView.m | 66 +++++++++++++ .../DisabledRoomInputToolbarView.xib | 94 +++++++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 Riot/Views/RoomInputToolbar/DisabledRoomInputToolbarView.h create mode 100644 Riot/Views/RoomInputToolbar/DisabledRoomInputToolbarView.m create mode 100644 Riot/Views/RoomInputToolbar/DisabledRoomInputToolbarView.xib diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 4b1b4cec1..d0adc06f6 100755 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -66,6 +66,8 @@ 327382C21F276AED00356143 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382BC1F276AED00356143 /* InfoPlist.strings */; }; 327382C31F276AED00356143 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382BE1F276AED00356143 /* Localizable.strings */; }; 327382C41F276AED00356143 /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382C01F276AED00356143 /* Vector.strings */; }; + 32795C0B203DA4C4002420E2 /* DisabledRoomInputToolbarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 32795C09203DA4C3002420E2 /* DisabledRoomInputToolbarView.m */; }; + 32795C0C203DA4C4002420E2 /* DisabledRoomInputToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32795C0A203DA4C4002420E2 /* DisabledRoomInputToolbarView.xib */; }; 3284D7FF1FBB34B70090AA80 /* RoomKeyRequestViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3284D7FE1FBB34B70090AA80 /* RoomKeyRequestViewController.m */; }; 32918EA91F473BDB0076CA16 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 32918EA51F473BDB0076CA16 /* Localizable.strings */; }; 32918EAA1F473BDB0076CA16 /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 32918EA71F473BDB0076CA16 /* Vector.strings */; }; @@ -717,6 +719,9 @@ 327382BD1F276AED00356143 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = InfoPlist.strings; sourceTree = ""; }; 327382BF1F276AED00356143 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Localizable.strings; sourceTree = ""; }; 327382C11F276AED00356143 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Vector.strings; sourceTree = ""; }; + 32795C08203DA4C3002420E2 /* DisabledRoomInputToolbarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DisabledRoomInputToolbarView.h; sourceTree = ""; }; + 32795C09203DA4C3002420E2 /* DisabledRoomInputToolbarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DisabledRoomInputToolbarView.m; sourceTree = ""; }; + 32795C0A203DA4C4002420E2 /* DisabledRoomInputToolbarView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DisabledRoomInputToolbarView.xib; sourceTree = ""; }; 3284D7FD1FBB34B70090AA80 /* RoomKeyRequestViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoomKeyRequestViewController.h; sourceTree = ""; }; 3284D7FE1FBB34B70090AA80 /* RoomKeyRequestViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RoomKeyRequestViewController.m; sourceTree = ""; }; 32918EA61F473BDB0076CA16 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = Localizable.strings; sourceTree = ""; }; @@ -2470,6 +2475,9 @@ F083BCD81E7009EC00A9B29C /* RoomInputToolbar */ = { isa = PBXGroup; children = ( + 32795C08203DA4C3002420E2 /* DisabledRoomInputToolbarView.h */, + 32795C09203DA4C3002420E2 /* DisabledRoomInputToolbarView.m */, + 32795C0A203DA4C4002420E2 /* DisabledRoomInputToolbarView.xib */, F083BCD91E7009EC00A9B29C /* RoomInputToolbarView.h */, F083BCDA1E7009EC00A9B29C /* RoomInputToolbarView.m */, F083BCDB1E7009EC00A9B29C /* RoomInputToolbarView.xib */, @@ -3156,6 +3164,7 @@ F083BE0D1E7009ED00A9B29C /* ContactDetailsViewController.xib in Resources */, F083BDCB1E7009ED00A9B29C /* selection_tick.png in Resources */, F083BD6C1E7009ED00A9B29C /* disclosure_icon@2x.png in Resources */, + 32795C0C203DA4C4002420E2 /* DisabledRoomInputToolbarView.xib in Resources */, F083BD4A1E7009ED00A9B29C /* call_video_mute_on_icon@3x.png in Resources */, F083BE8F1E7009ED00A9B29C /* RoomAvatarTitleView.xib in Resources */, F083BD9E1E7009ED00A9B29C /* mod_icon.png in Resources */, @@ -3638,6 +3647,7 @@ F083BE191E7009ED00A9B29C /* RecentsViewController.m in Sources */, F0B4CBA51F418D0B008E99C5 /* WebViewViewController.m in Sources */, F0E5D90E1FF6DDCF00560D7F /* GroupRoomsViewController.m in Sources */, + 32795C0B203DA4C4002420E2 /* DisabledRoomInputToolbarView.m in Sources */, F083BE351E7009ED00A9B29C /* MediaAlbumTableCell.m in Sources */, F083BE4C1E7009ED00A9B29C /* RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */, F075BEDB1EBB26F100A7B68A /* TableViewCellWithCollectionView.m in Sources */, diff --git a/Riot/Views/RoomInputToolbar/DisabledRoomInputToolbarView.h b/Riot/Views/RoomInputToolbar/DisabledRoomInputToolbarView.h new file mode 100644 index 000000000..0e3880d9e --- /dev/null +++ b/Riot/Views/RoomInputToolbar/DisabledRoomInputToolbarView.h @@ -0,0 +1,40 @@ +/* + Copyright 2018 New Vector 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 + +/** + `DisabledRoomInputToolbarView` instance is an input toolbar to show to the end user + that they have limited permission to interact with the room. + */ +@interface DisabledRoomInputToolbarView : MXKRoomInputToolbarView + +@property (weak, nonatomic) IBOutlet UIView *mainToolbarView; + +@property (weak, nonatomic) IBOutlet UIView *separatorView; +@property (strong, nonatomic) IBOutlet MXKImageView *pictureView; +@property (weak, nonatomic) IBOutlet UITextView *disabledReasonTextView; + +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mainToolbarMinHeightConstraint; + +/** + Display the limitation reason message. + + @param reason the reason why the user cannot interact with the room. + */ +- (void)setDisabledReason:(NSString*)reason; + +@end diff --git a/Riot/Views/RoomInputToolbar/DisabledRoomInputToolbarView.m b/Riot/Views/RoomInputToolbar/DisabledRoomInputToolbarView.m new file mode 100644 index 000000000..0ed84f45e --- /dev/null +++ b/Riot/Views/RoomInputToolbar/DisabledRoomInputToolbarView.m @@ -0,0 +1,66 @@ +/* + Copyright 2018 New Vector 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 "DisabledRoomInputToolbarView.h" + +#import "RiotDesignValues.h" + +@implementation DisabledRoomInputToolbarView + ++ (UINib *)nib +{ + return [UINib nibWithNibName:NSStringFromClass([DisabledRoomInputToolbarView class]) + bundle:[NSBundle bundleForClass:[DisabledRoomInputToolbarView class]]]; +} + ++ (instancetype)roomInputToolbarView +{ + if ([[self class] nib]) + { + return [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject; + } + else + { + return [[self alloc] init]; + } +} + +#pragma mark - Override MXKView + +-(void)customizeViewRendering +{ + [super customizeViewRendering]; + + // Remove default toolbar background color + self.backgroundColor = [UIColor clearColor]; + + self.separatorView.backgroundColor = kRiotAuxiliaryColor; + + self.disabledReasonTextView.font = [UIFont systemFontOfSize:15]; + self.disabledReasonTextView.textColor = kRiotPrimaryTextColor; + self.disabledReasonTextView.tintColor = kRiotColorGreen; + self.disabledReasonTextView.editable = NO; + self.disabledReasonTextView.scrollEnabled = NO; +} + +#pragma mark - + +- (void)setDisabledReason:(NSString *)reason +{ + self.disabledReasonTextView.text = reason; +} + +@end diff --git a/Riot/Views/RoomInputToolbar/DisabledRoomInputToolbarView.xib b/Riot/Views/RoomInputToolbar/DisabledRoomInputToolbarView.xib new file mode 100644 index 000000000..b9558ffb4 --- /dev/null +++ b/Riot/Views/RoomInputToolbar/DisabledRoomInputToolbarView.xib @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From b04f476e282369299696fbbb7ca19ebde70bf7da Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 21 Feb 2018 16:10:38 +0100 Subject: [PATCH 2/2] Room: Inform user when they cannot post to a room because of low power level Use DisabledRoomInputToolbarView for such permission issue --- Riot/Assets/en.lproj/Vector.strings | 1 + Riot/ViewController/RoomViewController.m | 92 ++++++++++++++++++------ 2 files changed, 71 insertions(+), 22 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 3b0f40275..bbb4c8301 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -233,6 +233,7 @@ "room_two_users_are_typing" = "%@ & %@ are typing…"; "room_many_users_are_typing" = "%@, %@ & others are typing…"; "room_message_placeholder" = "Send a message (unencrypted)…"; +"room_do_not_have_permission_to_post" = "You do not have permission to post to this room"; "encrypted_room_message_placeholder" = "Send an encrypted message…"; "room_message_short_placeholder" = "Send a message…"; "room_offline_notification" = "Connectivity to the server has been lost."; diff --git a/Riot/ViewController/RoomViewController.m b/Riot/ViewController/RoomViewController.m index d8bdd27c6..004881301 100644 --- a/Riot/ViewController/RoomViewController.m +++ b/Riot/ViewController/RoomViewController.m @@ -23,6 +23,7 @@ #import "AppDelegate.h" #import "RoomInputToolbarView.h" +#import "DisabledRoomInputToolbarView.h" #import "RoomActivitiesView.h" @@ -348,10 +349,10 @@ // 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]; + [self setRoomInputToolbarViewClass]; // Update the inputToolBar height. - CGFloat height = (self.inputToolbarView ? ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant : 0); + CGFloat height = [self inputToolbarHeight]; // Disable animation during the update [UIView setAnimationsEnabled:NO]; [self roomInputToolbarView:self.inputToolbarView heightDidChanged:height completion:nil]; @@ -867,10 +868,10 @@ // Restore tool bar view and room activities view if none if (!self.inputToolbarView) { - [self setRoomInputToolbarViewClass:RoomInputToolbarView.class]; + [self setRoomInputToolbarViewClass]; // Update the inputToolBar height. - CGFloat height = (self.inputToolbarView ? ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant : 0); + CGFloat height = [self inputToolbarHeight]; // Disable animation during the update [UIView setAnimationsEnabled:NO]; [self roomInputToolbarView:self.inputToolbarView heightDidChanged:height completion:nil]; @@ -905,8 +906,24 @@ [super leaveRoomOnEvent:event]; } -- (void)setRoomInputToolbarViewClass:(Class)roomInputToolbarViewClass +// Set the input toolbar according to the current display +- (void)setRoomInputToolbarViewClass { + Class roomInputToolbarViewClass = RoomInputToolbarView.class; + + // Check the user has enough power to post message + if (self.roomDataSource.room.state) + { + MXRoomPowerLevels *powerLevels = self.roomDataSource.room.state.powerLevels; + NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId]; + + BOOL canSend = (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsMessage:kMXEventTypeStringRoomMessage]); + if (!canSend) + { + roomInputToolbarViewClass = DisabledRoomInputToolbarView.class; + } + } + // Do not show toolbar in case of preview if (self.isRoomPreview) { @@ -916,6 +933,23 @@ [super setRoomInputToolbarViewClass:roomInputToolbarViewClass]; } +// Get the height of the current room input toolbar +- (CGFloat)inputToolbarHeight +{ + CGFloat height = 0; + + if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) + { + height = ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant; + } + else if ([self.inputToolbarView isKindOfClass:DisabledRoomInputToolbarView.class]) + { + height = ((DisabledRoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant; + } + + return height; +} + - (void)setRoomActivitiesViewClass:(Class)roomActivitiesViewClass { // Do not show room activities in case of preview (FIXME: show it when live events will be supported during peeking) @@ -1271,6 +1305,8 @@ - (void)refreshRoomInputToolbar { + MXKImageView *userPictureView; + // Check whether the input toolbar is ready before updating it. if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { @@ -1279,22 +1315,8 @@ // Check whether the call option is supported roomInputToolbarView.supportCallOption = self.roomDataSource.mxSession.callManager && self.roomDataSource.room.state.joinedMembers.count >= 2; - // Set user picture in input toolbar - MXKImageView *userPictureView = roomInputToolbarView.pictureView; - if (userPictureView) - { - UIImage *preview = [AvatarGenerator generateAvatarForMatrixItem:self.mainSession.myUser.userId withDisplayName:self.mainSession.myUser.displayname]; - NSString *avatarThumbURL = nil; - if (self.mainSession.myUser.avatarUrl) - { - // Suppose this url is a matrix content uri, we use SDK to get the well adapted thumbnail from server - avatarThumbURL = [self.mainSession.matrixRestClient urlOfContentThumbnail:self.mainSession.myUser.avatarUrl toFitViewSize:userPictureView.frame.size withMethod:MXThumbnailingMethodCrop]; - } - userPictureView.enableInMemoryCache = YES; - [userPictureView setImageURL:avatarThumbURL withType:nil andImageOrientation:UIImageOrientationUp previewImage:preview]; - [userPictureView.layer setCornerRadius:userPictureView.frame.size.width / 2]; - userPictureView.clipsToBounds = YES; - } + // Get user picture view in input toolbar + userPictureView = roomInputToolbarView.pictureView; // Show the hangup button if there is an active call or an active jitsi // conference call in the current room @@ -1319,6 +1341,32 @@ roomInputToolbarView.isEncryptionEnabled = (self.mainSession.crypto != nil); } } + else if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:DisabledRoomInputToolbarView.class]) + { + DisabledRoomInputToolbarView *roomInputToolbarView = (DisabledRoomInputToolbarView*)self.inputToolbarView; + + // Get user picture view in input toolbar + userPictureView = roomInputToolbarView.pictureView; + + // For the moment, there is only one reason to use `DisabledRoomInputToolbarView` + [roomInputToolbarView setDisabledReason:NSLocalizedStringFromTable(@"room_do_not_have_permission_to_post", @"Vector", nil)]; + } + + // Set user picture in input toolbar + if (userPictureView) + { + UIImage *preview = [AvatarGenerator generateAvatarForMatrixItem:self.mainSession.myUser.userId withDisplayName:self.mainSession.myUser.displayname]; + NSString *avatarThumbURL = nil; + if (self.mainSession.myUser.avatarUrl) + { + // Suppose this url is a matrix content uri, we use SDK to get the well adapted thumbnail from server + avatarThumbURL = [self.mainSession.matrixRestClient urlOfContentThumbnail:self.mainSession.myUser.avatarUrl toFitViewSize:userPictureView.frame.size withMethod:MXThumbnailingMethodCrop]; + } + userPictureView.enableInMemoryCache = YES; + [userPictureView setImageURL:avatarThumbURL withType:nil andImageOrientation:UIImageOrientationUp previewImage:preview]; + [userPictureView.layer setCornerRadius:userPictureView.frame.size.width / 2]; + userPictureView.clipsToBounds = YES; + } } - (void)onSwipeGesture:(UISwipeGestureRecognizer*)swipeGestureRecognizer @@ -3265,7 +3313,7 @@ [self setRoomInputToolbarViewClass:RoomInputToolbarView.class]; // Update the inputToolBar height. - CGFloat height = (self.inputToolbarView ? ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant : 0); + CGFloat height = [self inputToolbarHeight]; // Disable animation during the update [UIView setAnimationsEnabled:NO]; [self roomInputToolbarView:self.inputToolbarView heightDidChanged:height completion:nil];