Merge branch 'develop' into callkit

This commit is contained in:
Giom Foret
2017-09-19 09:50:21 +02:00
80 changed files with 1744 additions and 285 deletions
@@ -61,6 +61,11 @@
self.view.backgroundColor = kRiotPrimaryBgColor;
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
self.navigationBar.tintColor = kRiotSecondaryBgColor;
self.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: kRiotPrimaryTextColor};
self.backButton.tintColor = kRiotColorGreen;
}
- (void)viewWillAppear:(BOOL)animated
@@ -176,6 +176,7 @@
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
[self.authInputsView customizeViewRendering];
@@ -722,6 +723,27 @@
// Hide the custom server details in order to save customized inputs
[self hideCustomServers:YES];
// Create DM with Riot-bot on new account creation.
if (self.authType == MXKAuthenticationTypeRegister)
{
MXKAccount *account = [[MXKAccountManager sharedManager] accountForUserId:userId];
[account.mxSession createRoom:nil
visibility:kMXRoomDirectoryVisibilityPrivate
roomAlias:nil
topic:nil
invite:@[@"@riot-bot:matrix.org"]
invite3PID:nil
isDirect:YES
preset:kMXRoomPresetTrustedPrivateChat
success:nil
failure:^(NSError *error) {
NSLog(@"[AuthenticationVC] Create chat with riot-bot failed");
}];
}
// Remove auth view controller on successful login
if (self.navigationController)
{
@@ -34,6 +34,8 @@
@property (nonatomic) BOOL sendLogs;
@property (nonatomic) BOOL sendScreenshot;
@property (weak, nonatomic) IBOutlet UIView *overlayView;
@end
@implementation BugReportViewController
@@ -137,6 +139,10 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
self.overlayView.backgroundColor = kRiotOverlayColor;
self.overlayView.alpha = 1.0;
self.containerView.backgroundColor = kRiotPrimaryBgColor;
self.sendingContainer.backgroundColor = kRiotPrimaryBgColor;
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina5_5" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
@@ -17,6 +17,7 @@
<outlet property="containerView" destination="7U4-br-xRl" id="kJI-mL-pkw"/>
<outlet property="descriptionLabel" destination="Q2l-Yi-SlQ" id="VPO-0N-huQ"/>
<outlet property="logsDescriptionLabel" destination="ggt-kq-Zy9" id="3Nl-m3-1EE"/>
<outlet property="overlayView" destination="CXZ-fI-kPd" id="ZJb-0n-Tmf"/>
<outlet property="scrollView" destination="4fS-3N-tbW" id="Dsn-gw-VuW"/>
<outlet property="scrollViewBottomConstraint" destination="fnt-1e-ZW4" id="W1B-1m-h13"/>
<outlet property="sendButton" destination="z8A-7B-jAi" id="VSX-a9-yfs"/>
+3 -4
View File
@@ -21,8 +21,6 @@
#import "AvatarGenerator.h"
#import "MXRoom+Riot.h"
#import "UsersDevicesViewController.h"
#import "RiotNavigationController.h"
@@ -101,6 +99,7 @@
self.view.backgroundColor = kRiotPrimaryBgColor;
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
self.callerNameLabel.textColor = kRiotPrimaryTextColor;
self.callStatusLabel.textColor = kRiotTopicTextColor;
@@ -358,7 +357,7 @@
}
else if (self.mxCall.room)
{
return [AvatarGenerator generateAvatarForMatrixItem:self.mxCall.room.roomId withDisplayName:self.mxCall.room.riotDisplayname size:self.callerImageViewWidthConstraint.constant andFontSize:fontSize];
return [AvatarGenerator generateAvatarForMatrixItem:self.mxCall.room.roomId withDisplayName:self.mxCall.room.summary.displayname size:self.callerImageViewWidthConstraint.constant andFontSize:fontSize];
}
return [UIImage imageNamed:@"placeholder"];
@@ -387,7 +386,7 @@
}
else if (self.mxCall.isConferenceCall)
{
peerDisplayName = self.mxCall.room.riotDisplayname;
peerDisplayName = self.mxCall.room.summary.displayname;
peerAvatarURL = self.mxCall.room.state.avatar;
}
@@ -224,6 +224,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
self.headerView.backgroundColor = kRiotSecondaryBgColor;
self.contactNameLabel.textColor = kRiotPrimaryTextColor;
@@ -105,6 +105,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
// Check the table view style to select its bg color.
self.contactsTableView.backgroundColor = ((self.contactsTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor);
@@ -73,6 +73,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
self.searchBar.barStyle = kRiotDesignSearchBarStyle;
self.searchBar.tintColor = kRiotDesignSearchBarTintColor;
@@ -121,6 +121,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
// Check the table view style to select its bg color.
self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor);
@@ -72,6 +72,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
// Check the table view style to select its bg color.
self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor);
@@ -72,6 +72,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
// Check the table view style to select its bg color.
self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor);
@@ -79,6 +79,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
// Check the table view style to select its bg color.
self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor);
@@ -70,6 +70,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
self.searchBar.barStyle = kRiotDesignSearchBarStyle;
self.searchBar.tintColor = kRiotDesignSearchBarTintColor;
@@ -114,6 +114,7 @@
self.assetsCollectionView.backgroundColor = kRiotPrimaryBgColor;
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
}
- (UIStatusBarStyle)preferredStatusBarStyle
@@ -183,6 +183,7 @@ static void *RecordingContext = &RecordingContext;
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
self.cameraVideoCaptureProgressView.progressColor = kRiotPrimaryBgColor;
self.cameraVideoCaptureProgressView.unprogressColor = [UIColor clearColor];
@@ -96,6 +96,10 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
self.overlayView.backgroundColor = kRiotOverlayColor;
self.overlayView.alpha = 1.0;
self.titleLabel.textColor = kRiotPrimaryTextColor;
self.containerView.backgroundColor = kRiotPrimaryBgColor;
@@ -159,6 +159,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
// Use the primary bg color for the recents table view in plain style.
self.recentsTableView.backgroundColor = kRiotPrimaryBgColor;
@@ -73,6 +73,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
// Check the table view style to select its bg color.
self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor);
@@ -111,6 +111,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
// Check the table view style to select its bg color.
self.bubblesTableView.backgroundColor = ((self.bubblesTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor);
@@ -209,6 +209,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
self.memberHeaderView.backgroundColor = kRiotSecondaryBgColor;
self.roomMemberNameLabel.textColor = kRiotPrimaryTextColor;
@@ -74,6 +74,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
// Check the table view style to select its bg color.
self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor);
@@ -158,10 +158,11 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
[self refreshSearchBarItemsColor:_searchBarView];
_searchBarHeaderBorder.backgroundColor = kRiotColorSilver;
_searchBarHeaderBorder.backgroundColor = kRiotAuxiliaryColor;
// Check the table view style to select its bg color.
self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor);
+13 -1
View File
@@ -63,7 +63,7 @@
[super viewDidLoad];
// Add the Vector background image when search bar is empty
// Add the Riot background image when search bar is empty
[self addBackgroundImageViewToView:self.view];
// Initialize here the data sources if a matrix session has been already set.
@@ -72,6 +72,18 @@
self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
}
- (void)userInterfaceThemeDidChange
{
[super userInterfaceThemeDidChange];
UIImageView *backgroundImageView = self.backgroundImageView;
if (backgroundImageView)
{
UIImage *image = [MXKTools paintImage:backgroundImageView.image withColor:kRiotKeyboardColor];
backgroundImageView.image = image;
}
}
- (void)destroy
{
[super destroy];
@@ -26,6 +26,7 @@
#import "Tools.h"
#import "MXRoom+Riot.h"
#import "MXRoomSummary+Riot.h"
#import "AppDelegate.h"
@@ -246,6 +247,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
// Check the table view style to select its bg color.
self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor);
@@ -1964,6 +1966,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
}
cell = roomNotifCell;
// Force layout before reusing a cell (fix switch displayed outside the screen)
[cell layoutIfNeeded];
}
else if (row == ROOM_SETTINGS_MAIN_SECTION_ROW_PHOTO)
{
@@ -1994,7 +1999,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
}
else
{
[mxRoom setRoomAvatarImageIn:roomPhotoCell.mxkImageView];
[mxRoom.summary setRoomAvatarImageIn:roomPhotoCell.mxkImageView];
roomPhotoCell.userInteractionEnabled = (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomAvatar]);
roomPhotoCell.mxkImageView.alpha = roomPhotoCell.userInteractionEnabled ? 1.0f : 0.5f;
@@ -2181,6 +2186,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
directoryVisibilitySwitch.enabled = (oneSelfPowerLevel >= powerLevels.stateDefault);
cell = directoryToggleCell;
// Force layout before reusing a cell (fix switch displayed outside the screen)
[cell layoutIfNeeded];
}
else if (indexPath.row == missingAddressWarningIndex)
{
@@ -2491,6 +2499,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
roomEncryptionBlacklistUnverifiedDevicesSwitch.on = blacklistUnverifiedDevices;
cell = roomBlacklistUnverifiedDevicesCell;
// Force layout before reusing a cell (fix switch displayed outside the screen)
[cell layoutIfNeeded];
}
else if (indexPath.row == 2)
{
@@ -2547,6 +2558,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
roomEncryptionSwitch.on = ([updatedItemsDict objectForKey:kRoomSettingsEncryptionKey] != nil);
cell = roomEncryptionCell;
// Force layout before reusing a cell (fix switch displayed outside the screen)
[cell layoutIfNeeded];
}
else
{
+64 -16
View File
@@ -109,6 +109,9 @@
#import "MXRoom+Riot.h"
#import "IntegrationManagerViewController.h"
#import "WidgetViewController.h"
@interface RoomViewController ()
{
// The expanded header
@@ -180,9 +183,9 @@
// Tell whether the view controller is appeared or not.
BOOL isAppeared;
// The search bar buttom item back up.
UIBarButtonItem *searchBarButtonItem;
// The right bar button items back up.
NSArray<UIBarButtonItem *> *rightBarButtonItems;
// Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change.
id kRiotDesignValuesDidChangeThemeNotificationObserver;
}
@@ -360,9 +363,12 @@
[self setEventDetailsViewClass:EventDetailsView.class];
// Update navigation bar items
self.navigationItem.rightBarButtonItem.target = self;
self.navigationItem.rightBarButtonItem.action = @selector(onButtonPressed:);
for (UIBarButtonItem *barButtonItem in self.navigationItem.rightBarButtonItems)
{
barButtonItem.target = self;
barButtonItem.action = @selector(onButtonPressed:);
}
// Prepare missed dicussion badge
missedDiscussionsBarButtonCustomView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 21)];
missedDiscussionsBarButtonCustomView.backgroundColor = [UIColor clearColor];
@@ -417,6 +423,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
// Prepare jump to last unread banner
self.jumpToLastUnreadBannerContainer.backgroundColor = kRiotPrimaryBgColor;
@@ -1046,8 +1053,11 @@
- (void)destroy
{
searchBarButtonItem = nil;
self.navigationItem.rightBarButtonItem.enabled = NO;
rightBarButtonItems = nil;
for (UIBarButtonItem *barButtonItem in self.navigationItem.rightBarButtonItems)
{
barButtonItem.enabled = NO;
}
if (currentAlert)
{
@@ -1150,17 +1160,20 @@
- (void)refreshRoomTitle
{
if (searchBarButtonItem && !self.navigationItem.rightBarButtonItem)
if (rightBarButtonItems && !self.navigationItem.rightBarButtonItems)
{
// Restore by default the search bar button.
self.navigationItem.rightBarButtonItem = searchBarButtonItem;
self.navigationItem.rightBarButtonItems = rightBarButtonItems;
}
// Set the right room title view
if (self.isRoomPreview)
{
// Disable the search button
self.navigationItem.rightBarButtonItem.enabled = NO;
// Disable the right buttons
for (UIBarButtonItem *barButtonItem in self.navigationItem.rightBarButtonItems)
{
barButtonItem.enabled = NO;
}
[self showPreviewHeader:YES];
}
@@ -1170,8 +1183,18 @@
if (self.roomDataSource.isLive)
{
// Enable the search button
self.navigationItem.rightBarButtonItem.enabled = YES;
// Enable the right buttons (Search and Integrations)
for (UIBarButtonItem *barButtonItem in self.navigationItem.rightBarButtonItems)
{
barButtonItem.enabled = YES;
}
BOOL matrixAppsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"matrixApps"];
if (!matrixAppsEnabled && self.navigationItem.rightBarButtonItems.count == 2)
{
// If the setting is disabled, do not show the icon
self.navigationItem.rightBarButtonItems = @[self.navigationItem.rightBarButtonItem];
}
// Do not change title view class here if the expanded header is visible.
if (self.expandedHeaderContainer.hidden)
@@ -1191,8 +1214,8 @@
else
{
// Remove the search button temporarily
searchBarButtonItem = self.navigationItem.rightBarButtonItem;
self.navigationItem.rightBarButtonItem = nil;
rightBarButtonItems = self.navigationItem.rightBarButtonItems;
self.navigationItem.rightBarButtonItems = nil;
[self setRoomTitleViewClass:SimpleRoomTitleView.class];
self.titleView.editable = NO;
@@ -2850,10 +2873,35 @@
- (IBAction)onButtonPressed:(id)sender
{
// Search button
if (sender == self.navigationItem.rightBarButtonItem)
{
[self performSegueWithIdentifier:@"showRoomSearch" sender:self];
}
// Matrix Apps button
else if (self.navigationItem.rightBarButtonItems.count == 2 && sender == self.navigationItem.rightBarButtonItems[1])
{
// Temporary code to test `WidgetViewController`
// TODO: remove it
// NSArray<Widget *> *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
{
IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc] initForMXSession:self.roomDataSource.mxSession
inRoom:self.roomDataSource.roomId
screen:kIntegrationManagerMainScreen
widgetId:nil];
[self presentViewController:modularVC animated:NO completion:nil];
}
}
else if (sender == self.jumpToLastUnreadButton)
{
// Hide expanded header to restore navigation bar settings.
@@ -77,6 +77,11 @@ limitations under the License.
@param viewControllers the list of viewControllers to display.
@param defaultSelected index of the default selected UIViewController in the list.
*/
- (void)initWithTitles:(NSArray*)titles viewControllers:(NSArray*)viewControllers defaultSelected:(NSUInteger)index;
- (void)initWithTitles:(NSArray*)titles viewControllers:(NSArray*)viewControllers defaultSelected:(NSUInteger)defaultSelected;
/**
Callback used to take into account the change of the user interface theme.
*/
- (void)userInterfaceThemeDidChange;
@end
@@ -71,11 +71,11 @@
@param viewControllers the list of viewControllers to display.
@param defaultSelected index of the default selected UIViewController in the list.
*/
- (void)initWithTitles:(NSArray*)titles viewControllers:(NSArray*)someViewControllers defaultSelected:(NSUInteger)index
- (void)initWithTitles:(NSArray*)titles viewControllers:(NSArray*)someViewControllers defaultSelected:(NSUInteger)defaultSelected
{
viewControllers = someViewControllers;
sectionTitles = titles;
_selectedIndex = index;
_selectedIndex = defaultSelected;
}
- (void)destroy
@@ -172,6 +172,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
self.view.backgroundColor = kRiotPrimaryBgColor;
}
+60 -2
View File
@@ -105,8 +105,9 @@ enum
enum
{
#ifdef USE_JITSI_WIDGET
LABS_MATRIX_APPS_INDEX = 0,
#ifdef USE_JITSI_WIDGET
LABS_USE_JITSI_WIDGET_INDEX,
#endif
LABS_CRYPTO_INDEX,
LABS_COUNT
@@ -314,6 +315,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
// Check the table view style to select its bg color.
self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor);
@@ -1269,6 +1271,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
cell.alpha = 1.0f;
cell.userInteractionEnabled = YES;
[cell layoutIfNeeded];
return cell;
}
@@ -1281,6 +1285,9 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
cell.mxkLabel.textColor = kRiotPrimaryTextColor;
// Force layout before reusing a cell (fix switch displayed outside the screen)
[cell layoutIfNeeded];
return cell;
}
@@ -1331,6 +1338,12 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
{
signOutCell = [[MXKTableViewCellWithButton alloc] init];
}
else
{
// Fix https://github.com/vector-im/riot-ios/issues/1354
// Do not move this line in prepareForReuse because of https://github.com/vector-im/riot-ios/issues/1323
signOutCell.mxkButton.titleLabel.text = nil;
}
NSString* title = NSLocalizedStringFromTable(@"settings_sign_out", @"Vector", nil);
@@ -1909,6 +1922,11 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
{
markAllBtnCell = [[MXKTableViewCellWithButton alloc] init];
}
else
{
// Fix https://github.com/vector-im/riot-ios/issues/1354
markAllBtnCell.mxkButton.titleLabel.text = nil;
}
NSString *btnTitle = NSLocalizedStringFromTable(@"settings_mark_all_as_read", @"Vector", nil);
[markAllBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
@@ -1929,6 +1947,11 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
{
clearCacheBtnCell = [[MXKTableViewCellWithButton alloc] init];
}
else
{
// Fix https://github.com/vector-im/riot-ios/issues/1354
clearCacheBtnCell.mxkButton.titleLabel.text = nil;
}
NSString *btnTitle = NSLocalizedStringFromTable(@"settings_clear_cache", @"Vector", nil);
[clearCacheBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
@@ -1949,6 +1972,11 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
{
reportBugBtnCell = [[MXKTableViewCellWithButton alloc] init];
}
else
{
// Fix https://github.com/vector-im/riot-ios/issues/1354
reportBugBtnCell.mxkButton.titleLabel.text = nil;
}
NSString *btnTitle = NSLocalizedStringFromTable(@"settings_report_bug", @"Vector", nil);
[reportBugBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
@@ -1965,11 +1993,23 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
}
else if (section == SETTINGS_SECTION_LABS_INDEX)
{
#ifdef USE_JITSI_WIDGET
if (row == LABS_MATRIX_APPS_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_matrix_apps", @"Vector", nil);
labelAndSwitchCell.mxkSwitch.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"matrixApps"];
[labelAndSwitchCell.mxkSwitch removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleLabsMatrixApps:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
#ifdef USE_JITSI_WIDGET
else if (row == LABS_USE_JITSI_WIDGET_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_create_conference_with_jitsi", @"Vector", nil);
labelAndSwitchCell.mxkSwitch.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"createConferenceCallsWithJitsi"];
@@ -2052,6 +2092,11 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
{
exportKeysBtnCell = [[MXKTableViewCellWithButton alloc] init];
}
else
{
// Fix https://github.com/vector-im/riot-ios/issues/1354
exportKeysBtnCell.mxkButton.titleLabel.text = nil;
}
NSString *btnTitle = NSLocalizedStringFromTable(@"settings_crypto_export", @"Vector", nil);
[exportKeysBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
@@ -2747,6 +2792,19 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
}
}
- (void)toggleLabsMatrixApps:(id)sender
{
if (sender && [sender isKindOfClass:UISwitch.class])
{
UISwitch *switchButton = (UISwitch*)sender;
[[NSUserDefaults standardUserDefaults] setBool:switchButton.isOn forKey:@"matrixApps"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self.tableView reloadData];
}
}
- (void)toggleJitsiForConference:(id)sender
{
if (sender && [sender isKindOfClass:UISwitch.class])
@@ -148,7 +148,7 @@
[self refreshSearchBarItemsColor:_searchBarView];
_searchBarHeaderBorder.backgroundColor = kRiotColorSilver;
_searchBarHeaderBorder.backgroundColor = kRiotAuxiliaryColor;
// Check the table view style to select its bg color.
self.contactsTableView.backgroundColor = ((self.contactsTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor);
@@ -95,7 +95,7 @@
[super viewDidLoad];
// Add the Vector background image when search bar is empty
// Add the Riot background image when search bar is empty
[self addBackgroundImageViewToView:self.view];
// Initialize here the data sources if a matrix session has been already set.
@@ -107,6 +107,18 @@
[super showSearch:NO];
}
- (void)userInterfaceThemeDidChange
{
[super userInterfaceThemeDidChange];
UIImageView *backgroundImageView = self.backgroundImageView;
if (backgroundImageView)
{
UIImage *image = [MXKTools paintImage:backgroundImageView.image withColor:kRiotKeyboardColor];
backgroundImageView.image = image;
}
}
- (void)destroy
{
[super destroy];
@@ -83,6 +83,7 @@
{
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
// Check the table view style to select its bg color.
self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor);
@@ -54,6 +54,7 @@
self.view.backgroundColor = kRiotPrimaryBgColor;
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.barTitleColor = kRiotPrimaryTextColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
webView.backgroundColor = kRiotPrimaryBgColor;
}
@@ -0,0 +1,40 @@
/*
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 "WebViewViewController.h"
#import <MatrixSDK/MatrixSDK.h>
FOUNDATION_EXPORT NSString *const kIntegrationManagerMainScreen;
FOUNDATION_EXPORT NSString *const kIntegrationManagerAddIntegrationScreen;
/**
`IntegrationManagerViewController` displays the Modular integration manager webapp
into a webview.
*/
@interface IntegrationManagerViewController : WebViewViewController <UIWebViewDelegate>
/**
Initialise with params for the Modular interface webapp.
@param mxSession the session to use.
@param roomId the room where to set up widgets.
@param screen the screen to display in the Modular interface webapp. Can be nil.
@param widgetId the id of the widget in case of widget configuration edition. Can be nil.
*/
- (instancetype)initForMXSession:(MXSession*)mxSession inRoom:(NSString*)roomId screen:(NSString*)screen widgetId:(NSString*)widgetId;
@end
@@ -0,0 +1,741 @@
/*
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 "IntegrationManagerViewController.h"
#import "WidgetManager.h"
#import <JavaScriptCore/JavaScriptCore.h>
NSString *const kIntegrationManagerMainScreen = nil;
NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@', %@);";
@interface IntegrationManagerViewController ()
{
MXSession *mxSession;
NSString *roomId;
NSString *screen;
NSString *widgetId;
NSString *scalarToken;
MXHTTPOperation *operation;
}
@end
@implementation IntegrationManagerViewController
- (instancetype)initForMXSession:(MXSession *)theMXSession inRoom:(NSString *)theRoomId screen:(NSString *)theScreen widgetId:(NSString *)theWidgetId
{
self = [super init];
if (self)
{
mxSession = theMXSession;
roomId = theRoomId;
screen = theScreen;
widgetId = theWidgetId;
}
return self;
}
- (void)destroy
{
[super destroy];
[operation cancel];
operation = nil;
}
- (void)viewDidLoad
{
[super viewDidLoad];
webView.scalesPageToFit = NO;
webView.scrollView.bounces = NO;
webView.delegate = self;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.URL && !operation)
{
__weak __typeof__(self) weakSelf = self;
[self startActivityIndicator];
// Make sure we a scalar token
operation = [[WidgetManager sharedManager] getScalarTokenForMXSession:mxSession success:^(NSString *theScalarToken) {
typeof(self) self = weakSelf;
if (self)
{
self->operation = nil;
[self stopActivityIndicator];
scalarToken = theScalarToken;
// Launch the webview on the right modular webapp page
self.URL = [self interfaceUrl];
}
} failure:^(NSError *error) {
typeof(self) self = weakSelf;
if (self)
{
self->operation = nil;
[self stopActivityIndicator];
}
}];
}
}
#pragma mark - Private methods
/**
Build the URL to use in the Modular interface webapp.
*/
- (NSString *)interfaceUrl
{
NSMutableString *url;
if (scalarToken)
{
url = [NSMutableString stringWithFormat:@"%@?scalar_token=%@&room_id=%@",
[[NSUserDefaults standardUserDefaults] objectForKey:@"integrationsUiUrl"],
[scalarToken stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
[roomId stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]
];
if (screen)
{
[url appendString:@"&screen="];
[url appendString:[screen stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}
if (widgetId)
{
[url appendString:@"&integ_id="];
[url appendString:[widgetId stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}
}
return url;
}
- (void)enableDebug
{
// Setup console.log() -> NSLog() route
JSContext *ctx = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
ctx[@"console"][@"log"] = ^(JSValue * msg) {
NSLog(@"-- JavaScript: %@", msg);
};
// Redirect all console.* logging methods to console.log
[webView stringByEvaluatingJavaScriptFromString:@"console.debug = console.log; console.info = console.log; console.warn = console.log; console.error = console.log;"];
}
#pragma mark - UIWebViewDelegate
-(void)webViewDidFinishLoad:(UIWebView *)theWebView
{
[self enableDebug];
// Setup js code
NSString *path = [[NSBundle mainBundle] pathForResource:@"IntegrationManager" ofType:@"js"];
NSString *js = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[webView stringByEvaluatingJavaScriptFromString:js];
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString *urlString = [[request URL] absoluteString];
if ([urlString hasPrefix:@"js:"])
{
// Listen only to scheme of the JS-UIWebView bridge
NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSDictionary *parameters = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers
error:&error];
if (!error)
{
[self onMessage:parameters];
}
return NO;
}
return YES;
}
#pragma mark - Modular postMessage API
- (void)onMessage:(NSDictionary*)JSData
{
NSDictionary *eventData;
MXJSONModelSetDictionary(eventData, JSData[@"event.data"]);
NSString *roomIdInEvent, *userId, *action;
MXJSONModelSetString(roomIdInEvent, eventData[@"room_id"]);
MXJSONModelSetString(userId, eventData[@"user_id"]);
MXJSONModelSetString(action, eventData[@"action"]);
if ([action isEqualToString:@"close_scalar"])
{
[self withdrawViewControllerAnimated:YES completion:nil];
return;
}
if (!roomIdInEvent)
{
[self sendLocalisedError:@"widget_integration_missing_room_id" toEvent:eventData];
return;
}
if (![roomIdInEvent isEqualToString:roomId])
{
[self sendError:[NSString stringWithFormat:NSLocalizedStringFromTable(@"widget_integration_room_not_visible", @"Vector", nil), roomIdInEvent] toEvent:eventData];
return;
}
// These APIs don't require userId
if ([@"join_rules_state" isEqualToString:action])
{
[self getJoinRules:eventData];
return;
}
else if ([@"set_plumbing_state" isEqualToString:action])
{
[self setPlumbingState:eventData];
return;
}
else if ([@"get_membership_count" isEqualToString:action])
{
[self getMembershipCount:eventData];
return;
}
else if ([@"set_widget" isEqualToString:action])
{
[self setWidget:eventData];
return;
}
else if ([@"get_widgets" isEqualToString:action])
{
[self getWidgets:eventData];
return;
}
else if ([@"can_send_event" isEqualToString:action])
{
[self canSendEvent:eventData];
return;
}
if (!userId)
{
[self sendLocalisedError:@"widget_integration_missing_user_id" toEvent:eventData];
return;
}
if ([@"membership_state" isEqualToString:action])
{
[self getMembershipState:userId eventData:eventData];
}
else if ([@"invite" isEqualToString:action])
{
[self inviteUser:userId eventData:eventData];
}
else if ([@"bot_options" isEqualToString:action])
{
[self getBotOptions:userId eventData:eventData];
}
else if ([@"set_bot_options" isEqualToString:action])
{
[self setBotOptions:userId eventData:eventData];
}
else if ([@"set_bot_power" isEqualToString:action])
{
[self setBotPower:userId eventData:eventData];
}
else
{
NSLog(@"[IntegrationManagerViewControllerVC] Unhandled postMessage event with action %@: %@", action, JSData);
}
}
- (void)sendBoolResponse:(BOOL)response toEvent:(NSDictionary*)eventData
{
// Convert BOOL to "true" or "false"
NSString *js = [NSString stringWithFormat:kJavascriptSendResponseToModular,
eventData[@"_id"],
response ? @"true" : @"false"];
[webView stringByEvaluatingJavaScriptFromString:js];
}
- (void)sendIntegerResponse:(NSUInteger)response toEvent:(NSDictionary*)eventData
{
NSString *js = [NSString stringWithFormat:kJavascriptSendResponseToModular,
eventData[@"_id"],
@(response)];
[webView stringByEvaluatingJavaScriptFromString:js];
}
- (void)sendNSObjectResponse:(NSObject*)response toEvent:(NSDictionary*)eventData
{
NSString *jsString;
if (response)
{
// Convert response into a JS object through a JSON string
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:response
options:0
error:0];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
jsString = [NSString stringWithFormat:@"JSON.parse('%@')", jsonString];
}
else
{
jsString = @"null";
}
NSString *js = [NSString stringWithFormat:kJavascriptSendResponseToModular,
eventData[@"_id"],
jsString];
[webView stringByEvaluatingJavaScriptFromString:js];
}
- (void)sendError:(NSString*)message toEvent:(NSDictionary*)eventData
{
NSLog(@"[IntegrationManagerVC] sendError: Action %@ failed with message: %@", eventData[@"action"], message);
// TODO: JS has an additional optional parameter: nestedError
[self sendNSObjectResponse:@{
@"error": @{
@"message": message
}
}
toEvent:eventData];
}
- (void)sendLocalisedError:(NSString*)errorKey toEvent:(NSDictionary*)eventData
{
[self sendError:NSLocalizedStringFromTable(errorKey, @"Vector", nil) toEvent:eventData];
}
#pragma mark - Modular postMessage Implementation
- (MXRoom *)roomCheckWithEvent:(NSDictionary*)eventData
{
MXRoom *room = [mxSession roomWithRoomId:roomId];
if (!room)
{
[self sendLocalisedError:@"widget_integration_room_not_recognised" toEvent:eventData];
}
return room;
}
- (void)inviteUser:(NSString*)userId eventData:(NSDictionary*)eventData
{
NSLog(@"[IntegrationManagerVC] Received request to invite %@ into room %@.", userId, roomId);
MXRoom *room = [self roomCheckWithEvent:eventData];
if (room)
{
MXRoomMember *member = [room.state memberWithUserId:userId];
if (member && member.membership == MXMembershipJoin)
{
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toEvent:eventData];
}
else
{
__weak __typeof__(self) weakSelf = self;
[room inviteUser:userId success:^{
typeof(self) self = weakSelf;
if (self)
{
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toEvent:eventData];
}
} failure:^(NSError *error) {
typeof(self) self = weakSelf;
if (self)
{
[self sendLocalisedError:@"widget_integration_need_to_be_able_to_invite" toEvent:eventData];
}
}];
}
}
}
- (void)setWidget:(NSDictionary*)eventData
{
NSLog(@"[IntegrationManagerVC] Received request to set widget in room %@.", roomId);
MXRoom *room = [self roomCheckWithEvent:eventData];
if (room)
{
NSString *widget_id, *widgetType, *widgetUrl;
NSString *widgetName; // optional
NSDictionary *widgetData ; // optional
MXJSONModelSetString(widget_id, eventData[@"widget_id"]);
MXJSONModelSetString(widgetType, eventData[@"type"]);
MXJSONModelSetString(widgetUrl, eventData[@"url"]);
MXJSONModelSetString(widgetName, eventData[@"name"]);
MXJSONModelSetDictionary(widgetData, eventData[@"data"]);
if (!widget_id)
{
[self sendLocalisedError:@"widget_integration_unable_to_create" toEvent:eventData]; // new Error("Missing required widget fields."));
return;
}
NSMutableDictionary *widgetEventContent = [NSMutableDictionary dictionary];
if (widgetUrl)
{
if (!widgetType)
{
[self sendLocalisedError:@"widget_integration_unable_to_create" toEvent:eventData];
return;
}
widgetEventContent[@"type"] = widgetType;
widgetEventContent[@"url"] = widgetUrl;
if (widgetName)
{
widgetEventContent[@"name"] = widgetName;
}
if (widgetData)
{
widgetEventContent[@"data"] = widgetData;
}
}
__weak __typeof__(self) weakSelf = self;
[room sendStateEventOfType:kWidgetEventTypeString
content:widgetEventContent
stateKey:widget_id
success:^(NSString *eventId) {
typeof(self) self = weakSelf;
if (self)
{
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toEvent:eventData];
}
}
failure:^(NSError *error) {
typeof(self) self = weakSelf;
if (self)
{
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toEvent:eventData];
}
}];
}
}
- (void)getWidgets:(NSDictionary*)eventData
{
MXRoom *room = [self roomCheckWithEvent:eventData];
if (room)
{
NSArray<Widget*> *widgets = [[WidgetManager sharedManager] widgetsInRoom:room];
NSMutableArray<NSDictionary*> *widgetStateEvents = [NSMutableArray arrayWithCapacity:widgets.count];
for (Widget *widget in widgets)
{
[widgetStateEvents addObject:widget.widgetEvent.JSONDictionary];
}
[self sendNSObjectResponse:widgetStateEvents toEvent:eventData];
}
}
- (void)canSendEvent:(NSDictionary*)eventData
{
NSString *eventType;
BOOL isState = NO;
MXRoom *room = [self roomCheckWithEvent:eventData];
if (room)
{
if (room.state.membership != MXMembershipJoin)
{
[self sendLocalisedError:@"widget_integration_must_be_in_room" toEvent:eventData];
return;
}
MXJSONModelSetString(eventType, eventData[@"event_type"]);
MXJSONModelSetBoolean(isState, eventData[@"is_state"]);
MXRoomPowerLevels *powerLevels = room.state.powerLevels;
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:mxSession.myUser.userId];
BOOL canSend = NO;
if (isState)
{
canSend = (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:eventType]);
}
else
{
canSend = (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsMessage:eventType]);
}
if (canSend)
{
[self sendBoolResponse:YES toEvent:eventData];
}
else
{
[self sendLocalisedError:@"widget_integration_no_permission_in_room" toEvent:eventData];
}
}
}
- (void)getMembershipState:(NSString*)userId eventData:(NSDictionary*)eventData
{
NSLog(@"[IntegrationManagerVC] membership_state of %@ in room %@ requested.", userId, roomId);
MXRoom *room = [self roomCheckWithEvent:eventData];
if (room)
{
MXRoomMember *member = [room.state memberWithUserId:userId];
[self sendNSObjectResponse:member.originalEvent.content toEvent:eventData];
}
}
- (void)getJoinRules:(NSDictionary*)eventData
{
NSLog(@"[IntegrationManagerVC] join_rules of %@ requested.", roomId);
MXRoom *room = [self roomCheckWithEvent:eventData];
if (room)
{
MXEvent *event = [room.state stateEventsWithType:kMXEventTypeStringRoomJoinRules].lastObject;
[self sendNSObjectResponse:event.JSONDictionary toEvent:eventData];
}
}
- (void)setPlumbingState:(NSDictionary*)eventData
{
NSLog(@"[IntegrationManagerVC] Received request to set plumbing state to status %@ in room %@.", eventData[@"status"], roomId);
MXRoom *room = [self roomCheckWithEvent:eventData];
if (room)
{
NSString *status;
MXJSONModelSetString(status, eventData[@"status"]);
if (status)
{
__weak __typeof__(self) weakSelf = self;
[room sendStateEventOfType:kMXEventTypeStringRoomPlumbing
content:@{
@"status": status
}
stateKey:nil
success:^(NSString *eventId) {
typeof(self) self = weakSelf;
if (self)
{
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toEvent:eventData];
}
}
failure:^(NSError *error) {
typeof(self) self = weakSelf;
if (self)
{
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toEvent:eventData];
}
}];
}
else
{
NSLog(@"[IntegrationManagerVC] setPlumbingState. Error: Plumbing state status should be a string.");
}
}
}
- (void)getBotOptions:(NSString*)userId eventData:(NSDictionary*)eventData
{
NSLog(@"[IntegrationManagerVC] Received request to get options for bot %@ in room %@", userId, roomId);
MXRoom *room = [self roomCheckWithEvent:eventData];
if (room)
{
NSString *stateKey = [NSString stringWithFormat:@"_%@", userId];
NSArray<MXEvent*> *stateEvents = [room.state stateEventsWithType:kMXEventTypeStringRoomBotOptions];
MXEvent *botOptionsEvent;
for (MXEvent *stateEvent in stateEvents)
{
if ([stateEvent.stateKey isEqualToString:stateKey])
{
if (!botOptionsEvent || stateEvent.ageLocalTs > botOptionsEvent.ageLocalTs)
{
botOptionsEvent = stateEvent;
}
}
}
[self sendNSObjectResponse:botOptionsEvent.JSONDictionary toEvent:eventData];
}
}
- (void)setBotOptions:(NSString*)userId eventData:(NSDictionary*)eventData
{
NSLog(@"[IntegrationManagerVC] Received request to set options for bot %@ in room %@", userId, roomId);
MXRoom *room = [self roomCheckWithEvent:eventData];
if (room)
{
NSDictionary *content;
MXJSONModelSetDictionary(content, eventData[@"content"]);
if (content)
{
__weak __typeof__(self) weakSelf = self;
NSString *stateKey = [NSString stringWithFormat:@"_%@", userId];
[room sendStateEventOfType:kMXEventTypeStringRoomBotOptions
content:content
stateKey:stateKey
success:^(NSString *eventId) {
typeof(self) self = weakSelf;
if (self)
{
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toEvent:eventData];
}
}
failure:^(NSError *error) {
typeof(self) self = weakSelf;
if (self)
{
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toEvent:eventData];
}
}];
}
else
{
NSLog(@"[IntegrationManagerVC] setBotOptions. Error: options should be a dict.");
}
}
}
- (void)setBotPower:(NSString*)userId eventData:(NSDictionary*)eventData
{
NSLog(@"[IntegrationManagerVC] Received request to set power level to %@ for bot %@ in room %@.", eventData[@"level"], userId, roomId);
MXRoom *room = [self roomCheckWithEvent:eventData];
if (room)
{
NSInteger level = -1;
MXJSONModelSetInteger(level, eventData[@"level"]);
if (level >= 0)
{
__weak __typeof__(self) weakSelf = self;
[room setPowerLevelOfUserWithUserID:userId powerLevel:level success:^{
typeof(self) self = weakSelf;
if (self)
{
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toEvent:eventData];
}
} failure:^(NSError *error) {
typeof(self) self = weakSelf;
if (self)
{
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toEvent:eventData];
}
}];
}
else
{
NSLog(@"[IntegrationManagerVC] setBotPower. Power level must be positive integer.");
[self sendLocalisedError:@"widget_integration_positive_power_level" toEvent:eventData];
}
}
}
- (void)getMembershipCount:(NSDictionary*)eventData
{
MXRoom *room = [self roomCheckWithEvent:eventData];
if (room)
{
NSUInteger membershipCount = room.state.joinedMembers.count;
[self sendIntegerResponse:membershipCount toEvent:eventData];
}
}
@end
@@ -0,0 +1,33 @@
/*
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 "WebViewViewController.h"
#import "WidgetManager.h"
/**
`WidgetViewController` displays widget within a webview.
*/
@interface WidgetViewController : WebViewViewController
/**
Init 'WidgetViewController' instance with a widget.
@param widget the widget to open.
*/
- (instancetype)initForWidget:(Widget*)widget;
@end
@@ -0,0 +1,48 @@
/*
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 "WidgetViewController.h"
@interface WidgetViewController ()
{
Widget *widget;
}
@end
@implementation WidgetViewController
- (instancetype)initForWidget:(Widget*)theWidget
{
self = [super initWithURL:theWidget.url];
if (self)
{
widget = theWidget;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
webView.scalesPageToFit = NO;
webView.scrollView.bounces = NO;
self.navigationItem.title = widget.name;
}
@end