diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 3337981a0..3cee790a2 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -73,6 +73,7 @@ 32AE61F21F0D2183007255F4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 32AE61EC1F0D2183007255F4 /* InfoPlist.strings */; }; 32AE61F31F0D2183007255F4 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 32AE61EE1F0D2183007255F4 /* Localizable.strings */; }; 32AE61F41F0D2183007255F4 /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 32AE61F01F0D2183007255F4 /* Vector.strings */; }; + 32C2356F1F7B871800E38FC5 /* WidgetPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C2356E1F7B871800E38FC5 /* WidgetPickerViewController.m */; }; 32D392181EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D392161EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.m */; }; 32D392191EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32D392171EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib */; }; 32E84FA11F6BD32700CA0B89 /* apps-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 32E84F9E1F6BD32700CA0B89 /* apps-icon.png */; }; @@ -81,8 +82,8 @@ 32F3AE1A1F6FF4E600F0F004 /* WidgetViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F3AE191F6FF4E600F0F004 /* WidgetViewController.m */; }; 32FD0A3D1EB0CD9B0072B066 /* BugReportViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FD0A3B1EB0CD9B0072B066 /* BugReportViewController.m */; }; 32FD0A3E1EB0CD9B0072B066 /* BugReportViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32FD0A3C1EB0CD9B0072B066 /* BugReportViewController.xib */; }; - 92324BE31F4F66D3009DE194 /* IncomingCallView.m in Sources */ = {isa = PBXBuildFile; fileRef = 92324BE21F4F66D3009DE194 /* IncomingCallView.m */; }; 83711A7C1F6F8E7D008F0D4D /* KeyboardGrowingTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 83711A7B1F6F8E7D008F0D4D /* KeyboardGrowingTextView.m */; }; + 92324BE31F4F66D3009DE194 /* IncomingCallView.m in Sources */ = {isa = PBXBuildFile; fileRef = 92324BE21F4F66D3009DE194 /* IncomingCallView.m */; }; 92324BE61F4F6A60009DE194 /* CircleButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 92324BE51F4F6A60009DE194 /* CircleButton.m */; }; A27ECCE3FC4971745D2CB78D /* libPods-RiotShareExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7246451C668D6782166E22EC /* libPods-RiotShareExtension.a */; }; F0131DE51F2200D600CBF707 /* RiotSplitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F0131DE41F2200D600CBF707 /* RiotSplitViewController.m */; }; @@ -650,6 +651,8 @@ 32AE61ED1F0D2183007255F4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = InfoPlist.strings; sourceTree = ""; }; 32AE61EF1F0D2183007255F4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = Localizable.strings; sourceTree = ""; }; 32AE61F11F0D2183007255F4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = Vector.strings; sourceTree = ""; }; + 32C2356D1F7B871800E38FC5 /* WidgetPickerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WidgetPickerViewController.h; sourceTree = ""; }; + 32C2356E1F7B871800E38FC5 /* WidgetPickerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WidgetPickerViewController.m; sourceTree = ""; }; 32D392151EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectoryServerDetailTableViewCell.h; sourceTree = ""; }; 32D392161EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DirectoryServerDetailTableViewCell.m; sourceTree = ""; }; 32D392171EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DirectoryServerDetailTableViewCell.xib; sourceTree = ""; }; @@ -1351,6 +1354,8 @@ 3233F72E1F31F4BF006ACA81 /* JitsiViewController.h */, 3233F72F1F31F4BF006ACA81 /* JitsiViewController.m */, 3233F7301F31F4BF006ACA81 /* JitsiViewController.xib */, + 32C2356D1F7B871800E38FC5 /* WidgetPickerViewController.h */, + 32C2356E1F7B871800E38FC5 /* WidgetPickerViewController.m */, ); path = Widgets; sourceTree = ""; @@ -2972,7 +2977,7 @@ "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomTitle/MXKRoomTitleView.xib", "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomTitle/MXKRoomTitleViewWithTopic.xib", "${PODS_ROOT}/MatrixKit/MatrixKit/Views/Search/MXKSearchTableViewCell.xib", - "$PODS_CONFIGURATION_BUILD_DIR/MatrixKit/MatrixKit.bundle", + $PODS_CONFIGURATION_BUILD_DIR/MatrixKit/MatrixKit.bundle, "${PODS_ROOT}/MatrixSDK/MatrixSDK/Data/Store/MXCoreDataStore/MXCoreDataStore.xcdatamodeld", ); name = "[CP] Copy Pods Resources"; @@ -3078,6 +3083,7 @@ 32471CDC1F1373A100BDF50A /* RoomMembershipCollapsedWithPaginationTitleBubbleCell.m in Sources */, F083BE2B1E7009ED00A9B29C /* AuthInputsView.m in Sources */, 321082B21F0E9F40002E0091 /* RoomMembershipCollapsedBubbleCell.m in Sources */, + 32C2356F1F7B871800E38FC5 /* WidgetPickerViewController.m in Sources */, F083BE661E7009ED00A9B29C /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.m in Sources */, F083BE141E7009ED00A9B29C /* HomeViewController.m in Sources */, F083BDFB1E7009ED00A9B29C /* RoomSearchDataSource.m in Sources */, diff --git a/Riot/Utils/Widgets/WidgetManager.h b/Riot/Utils/Widgets/WidgetManager.h index 77b78340b..f717f96df 100644 --- a/Riot/Utils/Widgets/WidgetManager.h +++ b/Riot/Utils/Widgets/WidgetManager.h @@ -72,12 +72,20 @@ WidgetManagerErrorCode; /** List all active widgets of a given type in a room. - @param widgetType the types of widget to search. + @param widgetTypes the types of widget to search. Nil means all types. @param room the room to check. @return a list of widgets. */ - (NSArray *)widgetsOfTypes:(NSArray*)widgetTypes inRoom:(MXRoom*)room; +/** + List all active widgets of a given type in a room, excluding some types. + + @param notWidgetTypes the types of widget to not consider. Nil means all types. + @param room the room to check. + @return a list of widgets. + */ +- (NSArray *)widgetsNotOfTypes:(NSArray*)notWidgetTypes inRoom:(MXRoom*)room; /** Add a modular widget to a room. diff --git a/Riot/Utils/Widgets/WidgetManager.m b/Riot/Utils/Widgets/WidgetManager.m index 169ca5c1e..80a7de1a6 100644 --- a/Riot/Utils/Widgets/WidgetManager.m +++ b/Riot/Utils/Widgets/WidgetManager.m @@ -87,7 +87,17 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; return [self widgetsOfTypes:nil inRoom:room]; } -- (NSArray *)widgetsOfTypes:(NSArray *)widgetTypes inRoom:(MXRoom *)room +- (NSArray *)widgetsOfTypes:(NSArray*)widgetTypes inRoom:(MXRoom*)room; +{ + return [self widgetsOfTypes:widgetTypes butNotTypesOf:nil inRoom:room]; +} + +- (NSArray *)widgetsNotOfTypes:(NSArray*)notWidgetTypes inRoom:(MXRoom*)room +{ + return [self widgetsOfTypes:nil butNotTypesOf:notWidgetTypes inRoom:room]; +} + +- (NSArray *)widgetsOfTypes:(NSArray*)widgetTypes butNotTypesOf:(NSArray*)notWidgetTypes inRoom:(MXRoom*)room; { // Widget id -> widget NSMutableDictionary *widgets = [NSMutableDictionary dictionary]; @@ -118,14 +128,21 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; for (MXEvent *widgetEvent in widgetEvents) { // Filter widget types if required - if (widgetTypes) + if (widgetTypes || notWidgetTypes) { NSString *widgetType; MXJSONModelSetString(widgetType, widgetEvent.content[@"type"]); - if (widgetType && NSNotFound == [widgetTypes indexOfObject:widgetType]) + if (widgetType) { - continue; + if (widgetTypes && NSNotFound == [widgetTypes indexOfObject:widgetType]) + { + continue; + } + if (notWidgetTypes && NSNotFound != [notWidgetTypes indexOfObject:widgetType]) + { + continue; + } } } diff --git a/Riot/ViewController/RoomViewController.m b/Riot/ViewController/RoomViewController.m index d5669380d..dc2c4a079 100644 --- a/Riot/ViewController/RoomViewController.m +++ b/Riot/ViewController/RoomViewController.m @@ -110,7 +110,7 @@ #import "MXRoom+Riot.h" #import "IntegrationManagerViewController.h" -#import "WidgetViewController.h" +#import "WidgetPickerViewController.h" @interface RoomViewController () { @@ -1197,13 +1197,31 @@ barButtonItem.enabled = YES; } - BOOL matrixAppsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"matrixApps"]; - if (!matrixAppsEnabled && self.navigationItem.rightBarButtonItems.count == 2) + if (self.navigationItem.rightBarButtonItems.count == 2) { - // If the setting is disabled, do not show the icon - self.navigationItem.rightBarButtonItems = @[self.navigationItem.rightBarButtonItem]; + BOOL matrixAppsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"matrixApps"]; + if (!matrixAppsEnabled) + { + // If the setting is disabled, do not show the icon + self.navigationItem.rightBarButtonItems = @[self.navigationItem.rightBarButtonItem]; + } + else if (self.widgetsCount) + { + // Show there are widgets by changing the "apps" icon color + // TODO: Design must be reviewed + UIImage *icon = self.navigationItem.rightBarButtonItems[1].image; + icon = [MXKTools paintImage:icon withColor:kRiotColorPinkRed]; + icon = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; + + self.navigationItem.rightBarButtonItems[1].image = icon; + } + else + { + // Reset original icon + self.navigationItem.rightBarButtonItems[1].image = [UIImage imageNamed:@"apps-icon"]; + } } - + // Do not change title view class here if the expanded header is visible. if (self.expandedHeaderContainer.hidden) { @@ -2889,19 +2907,16 @@ // Matrix Apps button else if (self.navigationItem.rightBarButtonItems.count == 2 && sender == self.navigationItem.rightBarButtonItems[1]) { - // Temporary code to test `WidgetViewController` - // TODO: remove it -// NSArray *widgets = [[WidgetManager sharedManager] widgetsInRoom:self.roomDataSource.room]; -// if (widgets.count) -// { -// // Hide back button title -// self.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]; -// -// WidgetViewController *widgetVC = [[WidgetViewController alloc] initForWidget:widgets[0]]; -// [self.navigationController pushViewController:widgetVC animated:YES]; -// } -// else + if (self.widgetsCount) { + WidgetPickerViewController *widgetPicker = [[WidgetPickerViewController alloc] initForMXSession:self.roomDataSource.mxSession + inRoom:self.roomDataSource.roomId]; + + [widgetPicker showInViewController:self]; + } + else + { + // No widgets -> Directly show the integration manager IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc] initForMXSession:self.roomDataSource.mxSession inRoom:self.roomDataSource.roomId screen:kIntegrationManagerMainScreen @@ -3453,6 +3468,7 @@ // Update the bar [self refreshActivitiesViewDisplay]; [self refreshRoomInputToolbar]; + [self refreshRoomTitle]; } }]; } @@ -3473,6 +3489,12 @@ [[AppDelegate theDelegate] showErrorAsAlert:error]; } +- (NSUInteger)widgetsCount +{ + return [[WidgetManager sharedManager] widgetsNotOfTypes:@[kWidgetTypeJitsi] + inRoom:self.roomDataSource.room].count; +} + #pragma mark - Unreachable Network Handling - (void)refreshActivitiesViewDisplay diff --git a/Riot/ViewController/Widgets/WidgetPickerViewController.h b/Riot/ViewController/Widgets/WidgetPickerViewController.h new file mode 100644 index 000000000..8293c1029 --- /dev/null +++ b/Riot/ViewController/Widgets/WidgetPickerViewController.h @@ -0,0 +1,56 @@ +/* + Copyright 2017 Vector Creations 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 + +#import +#import + +/** + `WidgetPickerViewController` displays the list of widgets within a room plus a + way to open the integration manager for this room. + + TODO: The feature is still in dev. WidgetPickerViewController` is not yet a pure + UIViewController. + As there is no specified design, the list is displayed in a simple UIAlertController. + It would be nice if this picker could directly: + - Remove a widget + - Launch the integration manager to edit a widget + - Automatically updates on widgets change + */ +@interface WidgetPickerViewController : NSObject + +/** + The UIAlertController instance which handles the dialog. + */ +@property (nonatomic, readonly) UIAlertController *alertController; + +/** + Create the `WidgetPickerViewController` instance. + + @param mxSession the session to use. + @param roomId the room where to list available widgets. + */ +- (instancetype)initForMXSession:(MXSession*)mxSession inRoom:(NSString*)roomId; + +/** + Show the dialog in a given view controller. + + @param mxkViewController the mxkViewController where to show the dialog. + */ +- (void)showInViewController:(MXKViewController*)mxkViewController; + +@end diff --git a/Riot/ViewController/Widgets/WidgetPickerViewController.m b/Riot/ViewController/Widgets/WidgetPickerViewController.m new file mode 100644 index 000000000..a083fc3a3 --- /dev/null +++ b/Riot/ViewController/Widgets/WidgetPickerViewController.m @@ -0,0 +1,99 @@ +/* + Copyright 2017 Vector Creations 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 "WidgetPickerViewController.h" + +#import "AppDelegate.h" + +#import "WidgetManager.h" +#import "WidgetViewController.h" +#import "IntegrationManagerViewController.h" + +@interface WidgetPickerViewController () +{ + MXSession *mxSession; + NSString *roomId; +} + +@end + +@implementation WidgetPickerViewController + +- (instancetype)initForMXSession:(MXSession*)theMXSession inRoom:(NSString*)theRoomId +{ + self = [super init]; + if (self) + { + mxSession = theMXSession; + roomId = theRoomId; + + _alertController = [UIAlertController alertControllerWithTitle:@"Matrix Apps" + message:nil + preferredStyle:UIAlertControllerStyleAlert]; + } + return self; +} + +- (void)showInViewController:(MXKViewController *)mxkViewController +{ + UIAlertAction *alertAction; + + MXRoom *room = [mxSession roomWithRoomId:roomId]; + + NSArray *widgets = [[WidgetManager sharedManager] widgetsNotOfTypes:@[kWidgetTypeJitsi] + inRoom:room]; + // List widgets + for (Widget *widget in widgets) + { + alertAction = [UIAlertAction actionWithTitle:widget.name + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) + { + // Hide back button title + mxkViewController.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]; + + // Display the widget + WidgetViewController *widgetVC = [[WidgetViewController alloc] initForWidget:widget]; + [mxkViewController.navigationController pushViewController:widgetVC animated:YES]; + }]; + [_alertController addAction:alertAction]; + } + + // Link to the integration manager + alertAction = [UIAlertAction actionWithTitle:@"Manage integrations..." + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) + { + IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc] initForMXSession:self->mxSession + inRoom:self->roomId + screen:kIntegrationManagerMainScreen + widgetId:nil]; + + [mxkViewController presentViewController:modularVC animated:NO completion:nil]; + }]; + [_alertController addAction:alertAction]; + + // Cancel + alertAction = [UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:UIAlertActionStyleCancel + handler:nil]; + [_alertController addAction:alertAction]; + + // And show it + [mxkViewController presentViewController:_alertController animated:YES completion:nil]; +} + +@end