Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
Weblate 2018-05-31 15:15:02 +00:00
commit 376ad02d5d
22 changed files with 1161 additions and 140 deletions

View file

@ -42,4 +42,6 @@ Joey Watts <joey.watts.96 at gmail.com>
Arash Tabrizian <a.tabriziyan at gmail.com>
* PR #1828 Fix issue #1793 Confirmation popup when leaving room
* PR #1824 Fix issue #1816 Support specifying kick and ban msgs
Doug Earnshaw <pixlwave at users.noreply.github.com>
* PR #1865 Fix timezone interval

View file

@ -97,6 +97,7 @@
32E84FA11F6BD32700CA0B89 /* apps-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 32E84F9E1F6BD32700CA0B89 /* apps-icon.png */; };
32E84FA21F6BD32700CA0B89 /* apps-icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 32E84F9F1F6BD32700CA0B89 /* apps-icon@2x.png */; };
32E84FA31F6BD32700CA0B89 /* apps-icon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 32E84FA01F6BD32700CA0B89 /* apps-icon@3x.png */; };
32EF474920B6EE990031695C /* StickerPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32EF474820B6EE990031695C /* StickerPickerViewController.m */; };
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 */; };
@ -109,6 +110,8 @@
92726A4B1F58737A004AD26F /* SiriIntents.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 92726A431F58737A004AD26F /* SiriIntents.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
92726A511F587410004AD26F /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92726A501F587410004AD26F /* Intents.framework */; };
9D686B069F967C4D4BBC610F /* Pods_RiotPods_SiriIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB50EEBE8214352B9EBD6394 /* Pods_RiotPods_SiriIntents.framework */; };
B19A173920B7F94800DF0BB0 /* DeactivateAccountViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B19A173820B7F94800DF0BB0 /* DeactivateAccountViewController.m */; };
B19A173B20B7F96700DF0BB0 /* DeactivateAccountViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B19A173A20B7F96700DF0BB0 /* DeactivateAccountViewController.storyboard */; };
DDDE2AB95F865F2292B1D315 /* Pods_RiotPods_RiotShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 23D7292481328A48B8D5D4ED /* Pods_RiotPods_RiotShareExtension.framework */; };
F0131DE51F2200D600CBF707 /* RiotSplitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F0131DE41F2200D600CBF707 /* RiotSplitViewController.m */; };
F0173EB51FCF346800B5F6A3 /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = F0173EAF1FCF346800B5F6A3 /* Vector.strings */; };
@ -766,6 +769,8 @@
32E84F9E1F6BD32700CA0B89 /* apps-icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "apps-icon.png"; sourceTree = "<group>"; };
32E84F9F1F6BD32700CA0B89 /* apps-icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "apps-icon@2x.png"; sourceTree = "<group>"; };
32E84FA01F6BD32700CA0B89 /* apps-icon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "apps-icon@3x.png"; sourceTree = "<group>"; };
32EF474720B6EE990031695C /* StickerPickerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StickerPickerViewController.h; sourceTree = "<group>"; };
32EF474820B6EE990031695C /* StickerPickerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StickerPickerViewController.m; sourceTree = "<group>"; };
32F3AE181F6FF4E600F0F004 /* WidgetViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetViewController.h; sourceTree = "<group>"; };
32F3AE191F6FF4E600F0F004 /* WidgetViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetViewController.m; sourceTree = "<group>"; };
32FD0A3A1EB0CD9B0072B066 /* BugReportViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BugReportViewController.h; sourceTree = "<group>"; };
@ -794,6 +799,9 @@
92726A501F587410004AD26F /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = System/Library/Frameworks/Intents.framework; sourceTree = SDKROOT; };
A5030B7C3C0B6EB83A9257BD /* Pods-RiotPods-Riot.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-Riot.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot.debug.xcconfig"; sourceTree = "<group>"; };
B0FAA1A49F76B0CE15C5CBD8 /* Pods_RiotPods_Riot.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RiotPods_Riot.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B19A173720B7F94800DF0BB0 /* DeactivateAccountViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeactivateAccountViewController.h; sourceTree = "<group>"; };
B19A173820B7F94800DF0BB0 /* DeactivateAccountViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DeactivateAccountViewController.m; sourceTree = "<group>"; };
B19A173A20B7F96700DF0BB0 /* DeactivateAccountViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = DeactivateAccountViewController.storyboard; sourceTree = "<group>"; };
C195C53961EA28E6900AEB68 /* Pods-Riot.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Riot.release.xcconfig"; path = "Pods/Target Support Files/Pods-Riot/Pods-Riot.release.xcconfig"; sourceTree = "<group>"; };
C5258DFF261AA3AB228A3F11 /* Pods-RiotPods-RiotShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-RiotShareExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RiotPods-RiotShareExtension/Pods-RiotPods-RiotShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
DB50EEBE8214352B9EBD6394 /* Pods_RiotPods_SiriIntents.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RiotPods_SiriIntents.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -1562,6 +1570,8 @@
3233F7301F31F4BF006ACA81 /* JitsiViewController.xib */,
32C2356D1F7B871800E38FC5 /* WidgetPickerViewController.h */,
32C2356E1F7B871800E38FC5 /* WidgetPickerViewController.m */,
32EF474720B6EE990031695C /* StickerPickerViewController.h */,
32EF474820B6EE990031695C /* StickerPickerViewController.m */,
);
path = Widgets;
sourceTree = "<group>";
@ -1717,6 +1727,16 @@
path = SiriIntents;
sourceTree = "<group>";
};
B19A173520B6F89900DF0BB0 /* DeactivateAccount */ = {
isa = PBXGroup;
children = (
B19A173720B7F94800DF0BB0 /* DeactivateAccountViewController.h */,
B19A173820B7F94800DF0BB0 /* DeactivateAccountViewController.m */,
B19A173A20B7F96700DF0BB0 /* DeactivateAccountViewController.storyboard */,
);
path = DeactivateAccount;
sourceTree = "<group>";
};
F0173EAE1FCF346800B5F6A3 /* vi.lproj */ = {
isa = PBXGroup;
children = (
@ -2283,6 +2303,7 @@
24B5103F1EFA88CC004C6AD2 /* ReadReceiptsViewController.xib */,
F0B4CBA31F418D0B008E99C5 /* WebViewViewController.h */,
F0B4CBA41F418D0B008E99C5 /* WebViewViewController.m */,
B19A173520B6F89900DF0BB0 /* DeactivateAccount */,
);
path = ViewController;
sourceTree = "<group>";
@ -3151,6 +3172,7 @@
F083BDDA1E7009ED00A9B29C /* typing.png in Resources */,
F083BE831E7009ED00A9B29C /* RecentTableViewCell.xib in Resources */,
F046DC731FE1786500E3DAF0 /* GroupHomeViewController.xib in Resources */,
B19A173B20B7F96700DF0BB0 /* DeactivateAccountViewController.storyboard in Resources */,
F0E5D9141FF6FF3F00560D7F /* GroupRoomTableViewCell.xib in Resources */,
F083BDB71E7009ED00A9B29C /* remove_icon@2x.png in Resources */,
F083BDD31E7009ED00A9B29C /* settings_icon@3x.png in Resources */,
@ -3510,6 +3532,7 @@
F083BE661E7009ED00A9B29C /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.m in Sources */,
F083BE141E7009ED00A9B29C /* HomeViewController.m in Sources */,
F083BDFB1E7009ED00A9B29C /* RoomSearchDataSource.m in Sources */,
32EF474920B6EE990031695C /* StickerPickerViewController.m in Sources */,
3233F73C1F3306A7006ACA81 /* WidgetManager.m in Sources */,
F0B8D0D31FDFECB200F34524 /* GroupTableViewCell.m in Sources */,
F083BE281E7009ED00A9B29C /* StartChatViewController.m in Sources */,
@ -3630,6 +3653,7 @@
F0E05A021E963103004B83FB /* FavouritesViewController.m in Sources */,
F083BE941E7009ED00A9B29C /* FilesSearchTableViewCell.m in Sources */,
F083BE921E7009ED00A9B29C /* SimpleRoomTitleView.m in Sources */,
B19A173920B7F94800DF0BB0 /* DeactivateAccountViewController.m in Sources */,
F083BE981E7009ED00A9B29C /* MessagesSearchResultTextMsgBubbleCell.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View file

@ -113,6 +113,14 @@ extern NSString *const kAppDelegateNetworkStatusDidChangeNotification;
*/
- (void)logoutWithConfirmation:(BOOL)askConfirmation completion:(void (^)(BOOL isLoggedOut))completion;
/**
Log out all the accounts without confirmation.
Show the authentication screen on successful logout.
@param completion the block to execute at the end of the operation.
*/
- (void)logoutWithCompletion:(void (^)(BOOL isLoggedOut))completion;
#pragma mark - Matrix Accounts handling
- (void)selectMatrixAccount:(void (^)(MXKAccount *selectedAccount))onSelection;

View file

@ -2667,6 +2667,16 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
[topVC startActivityIndicator];
}
[self logoutWithCompletion:^(BOOL isLoggedOut) {
if (completion)
{
completion (YES);
}
}];
}
- (void)logoutWithCompletion:(void (^)(BOOL isLoggedOut))completion
{
self.pushRegistry = nil;
isPushRegistered = NO;

View file

@ -267,6 +267,8 @@
"room_event_action_view_encryption" = "Encryption Information";
"room_warning_about_encryption" = "End-to-end encryption is in beta and may not be reliable.\n\nYou should not yet trust it to secure data.\n\nDevices will not yet be able to decrypt history from before they joined the room.\n\nEncrypted messages will not be visible on clients that do not yet implement encryption.";
"room_event_failed_to_send" = "Failed to send";
"room_action_send_photo_or_video" = "Send photo or video";
"room_action_send_sticker" = "Send sticker";
// Unknown devices
"unknown_devices_alert_title" = "Room contains unknown devices";
@ -316,6 +318,7 @@
"settings_flair" = "Show flair where allowed";
"settings_devices" = "DEVICES";
"settings_cryptography" = "CRYPTOGRAPHY";
"settings_deactivate_account" = "DEACTIVATE ACCOUNT";
"settings_sign_out" = "Sign Out";
"settings_sign_out_confirmation" = "Are you sure?";
@ -396,6 +399,8 @@
"settings_crypto_export" = "Export keys";
"settings_crypto_blacklist_unverified_devices" = "Encrypt to verified devices only";
"settings_deactivate_my_account" = "Deactivate my account";
// Room Details
"room_details_title" = "Room Details";
"room_details_people" = "Members";
@ -568,6 +573,8 @@
// Widget
"widget_no_power_to_manage" = "You need permission to manage widgets in this room";
"widget_creation_failure" = "Widget creation has failed";
"widget_sticker_picker_no_stickerpacks_alert" = "You don't currently have any stickerpacks enabled.";
"widget_sticker_picker_no_stickerpacks_alert_add_now" = "Add some now?";
// Widget Integration Manager
"widget_integration_need_to_be_able_to_invite" = "You need to be able to invite users to do that.";
@ -596,3 +603,22 @@
// GDPR
"gdpr_consent_not_given_alert_message" = "To continue using the %@ homeserver you must review and agree to the terms and conditions.";
"gdpr_consent_not_given_alert_review_now_action" = "Review now";
// Deactivate account
"deactivate_account_title" = "Deactivate Account";
"deactivate_account_informations_part1" = "This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. ";
"deactivate_account_informations_part2_emphasize" = "This action is irreversible.";
"deactivate_account_informations_part3" = "\n\nDeactivating your account ";
"deactivate_account_informations_part4_emphasize" = "does not by default cause us to forget messages you have sent. ";
"deactivate_account_informations_part5" = "If you would like us to forget your messages, please tick the box below\n\nMessage visibility in Matrix is similar to email. Our forgetting your messages means that messages you have sent will not be shared with any new or unregistered users, but registered users who already have access to these messages will still have access to their copy.";
"deactivate_account_forget_messages_information_part1" = "Please forget all messages I have sent when my account is deactivated (";
"deactivate_account_forget_messages_information_part2_emphasize" = "Warning";
"deactivate_account_forget_messages_information_part3" = ": this will cause future users to see an incomplete view of conversations)";
"deactivate_account_validate_action" = "Deactivate account";
"deactivate_account_password_alert_title" = "Deactivate Account";
"deactivate_account_password_alert_message" = "To continue, please enter your password";

View file

@ -31,15 +31,6 @@ window.riotIOS.events = {};
// Listen to messages posted by the widget
window.riotIOS.onMessage = function(event) {
// Do not SPAM ObjC with event already managed
if (riotIOS.events[event.data._id]) {
return;
}
if (!event.origin) { // stupid chrome
event.origin = event.originalEvent.origin;
}
// Use an internal "_id" field for matching onMessage events and requests
// _id was originally used by the Modular API. Keep it
if (!event.data._id) {
@ -52,6 +43,15 @@ window.riotIOS.onMessage = function(event) {
if (!event.data._id) {
event.data._id = Date.now() + "-" + Math.random().toString(36);
}
// Do not SPAM ObjC with event already managed
if (riotIOS.events[event.data._id]) {
return;
}
if (!event.origin) { // stupid chrome
event.origin = event.originalEvent.origin;
}
// Keep this event for future usage
riotIOS.events[event.data._id] = event;

View file

@ -35,6 +35,7 @@ FOUNDATION_EXPORT NSString *const kWidgetModularEventTypeString;
Known types widgets.
*/
FOUNDATION_EXPORT NSString *const kWidgetTypeJitsi;
FOUNDATION_EXPORT NSString *const kWidgetTypeStickerPicker;
/**
Posted when a widget has been created, updated or disabled.
@ -101,6 +102,15 @@ WidgetManagerErrorCode;
*/
- (NSArray<Widget*> *)userWidgets:(MXSession*)mxSession;
/**
List all widgets of a given type of an account.
@param mxSession the session of the user account.
@param widgetTypes the types of widget to search. Nil means all types.
@return a list of widgets.
*/
- (NSArray<Widget*> *)userWidgets:(MXSession*)mxSession ofTypes:(NSArray<NSString*>*)widgetTypes;
/**
Add a modular widget to a room.

View file

@ -23,6 +23,7 @@
NSString *const kWidgetMatrixEventTypeString = @"m.widget";
NSString *const kWidgetModularEventTypeString = @"im.vector.modular.widgets";
NSString *const kWidgetTypeJitsi = @"jitsi";
NSString *const kWidgetTypeStickerPicker = @"m.stickerpicker";
NSString *const kWidgetManagerDidUpdateWidgetNotification = @"kWidgetManagerDidUpdateWidgetNotification";
@ -174,14 +175,22 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
- (NSArray<Widget*> *)userWidgets:(MXSession*)mxSession
{
// Disable user widgets (sticker picker) for now
return nil;
return [self userWidgets:mxSession ofTypes:nil];
}
- (NSArray<Widget*> *)userWidgets:(MXSession*)mxSession ofTypes:(NSArray<NSString*>*)widgetTypes
{
// Get all widgets in the user account data
NSMutableArray<Widget *> *userWidgets = [NSMutableArray array];
for (NSDictionary *widgetEventContent in [mxSession.accountData accountDataForEventType:@"m.widgets"].allValues)
for (NSDictionary *widgetEventContent in [mxSession.accountData accountDataForEventType:kMXAccountDataTypeUserWidgets].allValues)
{
// Patch: Modular uses a malformed key: "stateKey" instead of "state_key"
if (![widgetEventContent isKindOfClass:NSDictionary.class])
{
NSLog(@"[WidgetManager] userWidgets: ERROR: invalid user widget format: %@", widgetEventContent);
continue;
}
// Patch: Modular used a malformed key: "stateKey" instead of "state_key"
// TODO: To remove once fixed server side
NSDictionary *widgetEventContentFixed = widgetEventContent;
if (!widgetEventContent[@"state_key"] && widgetEventContent[@"stateKey"])
@ -192,7 +201,8 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
}
MXEvent *widgetEvent = [MXEvent modelFromJSON:widgetEventContentFixed];
if (widgetEvent)
if (widgetEvent
&& (!widgetTypes || [widgetTypes containsObject:widgetEvent.content[@"type"]]))
{
Widget *widget = [[Widget alloc] initWithWidgetEvent:widgetEvent inMatrixSession:mxSession];
if (widget)

View file

@ -0,0 +1,45 @@
/*
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 UIKit;
@import MatrixKit;
#pragma mark - Types
@class DeactivateAccountViewController;
#pragma mark - Protocol
@protocol DeactivateAccountViewControllerDelegate <NSObject>
- (void)deactivateAccountViewControllerDidCancel:(DeactivateAccountViewController*)deactivateAccountViewController;
- (void)deactivateAccountViewControllerDidDeactivateWithSuccess:(DeactivateAccountViewController*)deactivateAccountViewController;
@end
#pragma mark - Interface
@interface DeactivateAccountViewController : MXKViewController
#pragma mark - Properties
@property (nonatomic, weak) id<DeactivateAccountViewControllerDelegate> delegate;
#pragma mark - Class Methods
+ (DeactivateAccountViewController*)instantiateWithMatrixSession:(MXSession*)matrixSession;
@end

View file

@ -0,0 +1,327 @@
/*
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 "DeactivateAccountViewController.h"
#import "RiotDesignValues.h"
#import "AppDelegate.h"
#pragma mark - Defines & Constants
static CGFloat const kButtonCornerRadius = 5.0;
static CGFloat const kTextFontSize = 15.0;
#pragma mark - Private Interface
@interface DeactivateAccountViewController ()
#pragma mark - Outlets
@property (weak, nonatomic) IBOutlet UILabel *deactivateAccountInfosLabel;
@property (weak, nonatomic) IBOutlet UILabel *forgetMessagesInfoLabel;
@property (weak, nonatomic) IBOutlet UIButton *forgetMessageButton;
@property (weak, nonatomic) IBOutlet UIButton *deactivateAcccountButton;
#pragma mark - Private Properties
@property (strong, nonatomic) NSDictionary *normalStringAttributes;
@property (strong, nonatomic) NSDictionary *emphasizeStringAttributes;
@property (strong, nonatomic) MXKErrorAlertPresentation *errorPresentation;
@property (weak, nonatomic) id <NSObject> themeDidChangeNotificationObserver;
@end
#pragma mark - Implementation
@implementation DeactivateAccountViewController
#pragma mark - Setup & Teardown
+ (DeactivateAccountViewController*)instantiateWithMatrixSession:(MXSession*)matrixSession
{
DeactivateAccountViewController* viewController = [[UIStoryboard storyboardWithName:NSStringFromClass([DeactivateAccountViewController class]) bundle:[NSBundle mainBundle]] instantiateInitialViewController];
[viewController addMatrixSession:matrixSession];
return viewController;
}
- (void)destroy
{
id<NSObject> notificationObserver = self.themeDidChangeNotificationObserver;
if (notificationObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:notificationObserver];
}
[super destroy];
}
#pragma mark - View life cycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = NSLocalizedStringFromTable(@"deactivate_account_title", @"Vector", nil);
self.errorPresentation = [[MXKErrorAlertPresentation alloc] init];
[self setupStringAttributes];
[self setupViews];
[self userInterfaceThemeDidChange];
[self registerThemeNotification];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"DeactivateAccount"];
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self.deactivateAcccountButton.layer setCornerRadius:kButtonCornerRadius];
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return kRiotDesignStatusBarStyle;
}
#pragma mark - Private
- (void)registerThemeNotification
{
self.themeDidChangeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
[self userInterfaceThemeDidChange];
}];
}
- (void)userInterfaceThemeDidChange
{
self.view.backgroundColor = kRiotPrimaryBgColor;
self.defaultBarTintColor = kRiotSecondaryBgColor;
self.activityIndicator.backgroundColor = kRiotOverlayColor;
}
- (void)setupStringAttributes
{
self.normalStringAttributes = @{
NSFontAttributeName: [UIFont systemFontOfSize:kTextFontSize],
NSForegroundColorAttributeName: kRiotPrimaryTextColor
};
self.emphasizeStringAttributes = @{
NSFontAttributeName: [UIFont systemFontOfSize:kTextFontSize weight:UIFontWeightBold],
NSForegroundColorAttributeName: kRiotPrimaryTextColor
};
}
- (void)setupViews
{
[self setupNavigationBar];
[self setupDeactivateAcccountButton];
[self setupDeactivateAccountInfosLabel];
[self setupForgetMessagesInfoLabel];
}
- (void)setupNavigationBar
{
self.navigationController.navigationBar.titleTextAttributes = @{ NSForegroundColorAttributeName: kRiotColorRed };
UIBarButtonItem *cancelBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedStringFromTable(@"cancel", @"Vector", nil) style:UIBarButtonItemStylePlain target:self action:@selector(cancelButtonAction:)];
self.navigationItem.rightBarButtonItem = cancelBarButtonItem;
}
- (void)setupDeactivateAcccountButton
{
// Adjust button font size for small devices
self.deactivateAcccountButton.titleLabel.adjustsFontSizeToFitWidth = YES;
self.deactivateAcccountButton.titleLabel.minimumScaleFactor = 0.5;
self.deactivateAcccountButton.titleLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
self.deactivateAcccountButton.layer.masksToBounds = YES;
self.deactivateAcccountButton.backgroundColor = kRiotColorGreen;
[self.deactivateAcccountButton setTitle:NSLocalizedStringFromTable(@"deactivate_account_validate_action", @"Vector", nil) forState:UIControlStateNormal];
[self.deactivateAcccountButton setTitleColor:kRiotColorSilver forState:UIControlStateDisabled];
}
- (void)setupDeactivateAccountInfosLabel
{
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"deactivate_account_informations_part1", @"Vector", nil) attributes:self.normalStringAttributes]];
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"deactivate_account_informations_part2_emphasize", @"Vector", nil) attributes:self.emphasizeStringAttributes]];
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"deactivate_account_informations_part3", @"Vector", nil) attributes:self.normalStringAttributes]];
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"deactivate_account_informations_part4_emphasize", @"Vector", nil) attributes:self.emphasizeStringAttributes]];
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"deactivate_account_informations_part5", @"Vector", nil) attributes:self.normalStringAttributes]];
[self.deactivateAccountInfosLabel setAttributedText:attributedString];
}
- (void)setupForgetMessagesInfoLabel
{
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"deactivate_account_forget_messages_information_part1", @"Vector", nil) attributes:self.normalStringAttributes]];
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"deactivate_account_forget_messages_information_part2_emphasize", @"Vector", nil) attributes:self.emphasizeStringAttributes]];
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"deactivate_account_forget_messages_information_part3", @"Vector", nil) attributes:self.normalStringAttributes]];
[self.forgetMessagesInfoLabel setAttributedText:attributedString];
}
- (void)enableUserActions:(BOOL)enableUserActions
{
self.navigationItem.rightBarButtonItem.enabled = enableUserActions;
self.forgetMessageButton.enabled = enableUserActions;
self.deactivateAcccountButton.enabled = enableUserActions;
}
- (void)presentPasswordRequiredAlertWithSubmitHandler:(void (^)(NSString *password))submitHandler
cancelHandler:(dispatch_block_t)cancelHandler
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"deactivate_account_password_alert_title", @"Vector", nil)
message:NSLocalizedStringFromTable(@"deactivate_account_password_alert_message", @"Vector", nil) preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
textField.placeholder = nil;
textField.keyboardType = UIKeyboardTypeDefault;
}];
[alert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (cancelHandler)
{
cancelHandler();
}
}]];
__weak typeof(self) weakSelf = self;
[alert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"submit"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
UITextField *textField = alert.textFields.firstObject;
typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf)
{
NSString *password = textField.text;
if (submitHandler)
{
submitHandler(password);
}
}
}]];
[self presentViewController:alert animated:YES completion:nil];
}
- (void)deactivateAccountWithUserId:(NSString*)userId
andPassword:(NSString*)password
eraseAllMessages:(BOOL)eraseAllMessages
{
if (password && userId)
{
[self enableUserActions:NO];
[self startActivityIndicator];
// This assumes that the homeserver requires password UI auth
// for this endpoint. In reality it could be any UI auth.
__weak typeof(self) weakSelf = self;
NSDictionary *authParameters = @{@"user": userId,
@"password": password,
@"type": kMXLoginFlowTypePassword};
[self.mainSession deactivateAccountWithAuthParameters:authParameters eraseAccount:eraseAllMessages success:^{
NSLog(@"[SettingsViewController] Deactivate account with success");
[weakSelf stopActivityIndicator];
[weakSelf enableUserActions:YES];
} failure:^(NSError *error) {
NSLog(@"[SettingsViewController] Failed to deactivate account");
typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf)
{
[strongSelf stopActivityIndicator];
[strongSelf enableUserActions:YES];
[strongSelf.errorPresentation presentErrorFromViewController:strongSelf forError:error animated:YES handler:nil];
}
}];
}
else
{
NSLog(@"[SettingsViewController] Failed to deactivate account");
[self.errorPresentation presentGenericErrorFromViewController:self animated:YES handler:nil];
}
}
#pragma mark - Actions
- (void)cancelButtonAction:(id)sender
{
[self.delegate deactivateAccountViewControllerDidCancel:self];
}
- (IBAction)forgetMessagesButtonAction:(UIButton*)sender
{
self.forgetMessageButton.selected = !self.forgetMessageButton.selected;
}
- (IBAction)deactivateAccountButtonAction:(id)sender
{
__weak typeof(self) weakSelf = self;
[self presentPasswordRequiredAlertWithSubmitHandler:^(NSString *password) {
typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf)
{
NSString *userId = strongSelf.mainSession.myUser.userId;
[strongSelf deactivateAccountWithUserId:userId andPassword:password eraseAllMessages:strongSelf.forgetMessageButton.isEnabled];
}
} cancelHandler:nil];
}
@end

View file

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="IHf-2R-Nmh">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Deactivate Account View Controller-->
<scene sceneID="qDi-ox-hHh">
<objects>
<viewController automaticallyAdjustsScrollViewInsets="NO" id="IHf-2R-Nmh" customClass="DeactivateAccountViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="QVw-lB-bra">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="OSw-nj-Wdk">
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="r6I-6D-FLD">
<rect key="frame" x="0.0" y="0.0" width="375" height="641"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="justified" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uaL-Wi-UcQ">
<rect key="frame" x="20" y="15" width="335" height="426.5"/>
<string key="text">This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. This action is irreversible.
Deactivating your account does not by default cause us to forget messages you have sent. If you would like us to forget your messages, please tick the box below.
Message visibility in Matrix is similar to email. Our forgetting your messages means that messages you have sent will not be shared with any new or unregistered users, but registered users who already have access to these messages will still have access to their copy.
</string>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="UBr-xR-a5z">
<rect key="frame" x="10" y="449.5" width="44" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="3Bn-lP-a2k"/>
<constraint firstAttribute="width" secondItem="UBr-xR-a5z" secondAttribute="height" multiplier="1:1" id="6Nd-IG-Wda"/>
</constraints>
<state key="normal" image="selection_untick.png"/>
<state key="selected" image="selection_tick.png"/>
<connections>
<action selector="forgetMessagesButtonAction:" destination="IHf-2R-Nmh" eventType="touchUpInside" id="gbq-6b-IU4"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2AU-Ro-4rr">
<rect key="frame" x="54" y="459.5" width="301" height="101.5"/>
<string key="text">Please forget all messages I have sent when my account is deactivated (Warning: this will cause future users to see an incomplete view of conversations)</string>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7fz-Dy-5pc">
<rect key="frame" x="83" y="586" width="208" height="30"/>
<color key="backgroundColor" red="0.028153735480000001" green="0.82494870580000002" blue="0.051896891280000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCLoginButton"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="GpW-8Z-aDc"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="100" id="gMI-XP-i84"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
<inset key="contentEdgeInsets" minX="30" minY="0.0" maxX="30" maxY="0.0"/>
<state key="normal" title="Deactivate Account">
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="deactivateAccountButtonAction:" destination="IHf-2R-Nmh" eventType="touchUpInside" id="ZgL-dj-3sa"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="2AU-Ro-4rr" firstAttribute="leading" secondItem="UBr-xR-a5z" secondAttribute="trailing" id="2dA-G3-YmX"/>
<constraint firstAttribute="trailing" secondItem="uaL-Wi-UcQ" secondAttribute="trailing" constant="20" id="3VC-8f-t4r"/>
<constraint firstItem="7fz-Dy-5pc" firstAttribute="centerX" secondItem="r6I-6D-FLD" secondAttribute="centerX" id="ArL-9U-gev"/>
<constraint firstItem="7fz-Dy-5pc" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="r6I-6D-FLD" secondAttribute="leading" constant="20" id="GdW-9q-ham"/>
<constraint firstItem="UBr-xR-a5z" firstAttribute="leading" secondItem="r6I-6D-FLD" secondAttribute="leading" constant="10" id="HEu-UC-ZqJ"/>
<constraint firstItem="2AU-Ro-4rr" firstAttribute="top" secondItem="UBr-xR-a5z" secondAttribute="top" constant="10" id="Je8-yX-RL7"/>
<constraint firstItem="7fz-Dy-5pc" firstAttribute="top" secondItem="2AU-Ro-4rr" secondAttribute="bottom" constant="25" id="Rie-CJ-sjU"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="7fz-Dy-5pc" secondAttribute="trailing" constant="20" id="aBJ-Qh-98C"/>
<constraint firstItem="uaL-Wi-UcQ" firstAttribute="top" secondItem="r6I-6D-FLD" secondAttribute="top" constant="15" id="gNW-9W-tWy"/>
<constraint firstItem="uaL-Wi-UcQ" firstAttribute="leading" secondItem="r6I-6D-FLD" secondAttribute="leading" constant="20" id="hkc-Mh-1eX"/>
<constraint firstAttribute="bottom" secondItem="7fz-Dy-5pc" secondAttribute="bottom" constant="25" id="pcJ-cW-k1P"/>
<constraint firstItem="2AU-Ro-4rr" firstAttribute="trailing" secondItem="uaL-Wi-UcQ" secondAttribute="trailing" id="unG-rW-hIH"/>
<constraint firstItem="UBr-xR-a5z" firstAttribute="top" secondItem="uaL-Wi-UcQ" secondAttribute="bottom" constant="8" id="ygz-iG-BYI"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="r6I-6D-FLD" secondAttribute="trailing" id="Cv8-1O-u62"/>
<constraint firstItem="r6I-6D-FLD" firstAttribute="width" secondItem="OSw-nj-Wdk" secondAttribute="width" id="LbL-Rk-YWQ"/>
<constraint firstItem="r6I-6D-FLD" firstAttribute="top" secondItem="OSw-nj-Wdk" secondAttribute="top" id="bWG-YM-ufd"/>
<constraint firstAttribute="bottom" secondItem="r6I-6D-FLD" secondAttribute="bottom" id="eTa-ak-f7f"/>
<constraint firstItem="r6I-6D-FLD" firstAttribute="leading" secondItem="OSw-nj-Wdk" secondAttribute="leading" id="jDI-GL-r0a"/>
</constraints>
</scrollView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="HwH-O8-jKY" firstAttribute="top" secondItem="OSw-nj-Wdk" secondAttribute="top" id="0VJ-Ig-2ed"/>
<constraint firstItem="HwH-O8-jKY" firstAttribute="leading" secondItem="OSw-nj-Wdk" secondAttribute="leading" id="Hc0-LV-lRt"/>
<constraint firstItem="HwH-O8-jKY" firstAttribute="trailing" secondItem="OSw-nj-Wdk" secondAttribute="trailing" id="slf-Q8-PKr"/>
<constraint firstItem="HwH-O8-jKY" firstAttribute="bottom" secondItem="OSw-nj-Wdk" secondAttribute="bottom" id="vr6-Pc-AP8"/>
</constraints>
<viewLayoutGuide key="safeArea" id="HwH-O8-jKY"/>
</view>
<connections>
<outlet property="deactivateAcccountButton" destination="7fz-Dy-5pc" id="DMS-cL-nZT"/>
<outlet property="deactivateAccountInfosLabel" destination="uaL-Wi-UcQ" id="zDZ-bW-s0Z"/>
<outlet property="forgetMessageButton" destination="UBr-xR-a5z" id="NfP-c4-AxP"/>
<outlet property="forgetMessagesInfoLabel" destination="2AU-Ro-4rr" id="tTS-bh-g58"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dTx-N1-Ytz" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-239.19999999999999" y="103.89805097451276"/>
</scene>
</scenes>
<resources>
<image name="selection_tick.png" width="22" height="22"/>
<image name="selection_untick.png" width="22" height="22"/>
</resources>
</document>

View file

@ -115,6 +115,7 @@
#import "IntegrationManagerViewController.h"
#import "WidgetPickerViewController.h"
#import "StickerPickerViewController.h"
@interface RoomViewController ()
{
@ -2854,6 +2855,80 @@
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
}
#pragma mark - RoomInputToolbarViewDelegate
- (void)roomInputToolbarViewPresentStickerPicker:(MXKRoomInputToolbarView*)toolbarView
{
// Search for the sticker picker widget in the user account
Widget *widget = [[WidgetManager sharedManager] userWidgets:self.roomDataSource.mxSession ofTypes:@[kWidgetTypeStickerPicker]].firstObject;
if (widget)
{
// Display the widget
[widget widgetUrl:^(NSString * _Nonnull widgetUrl) {
StickerPickerViewController *stickerPickerVC = [[StickerPickerViewController alloc] initWithUrl:widgetUrl forWidget:widget];
stickerPickerVC.roomDataSource = self.roomDataSource;
[self.navigationController pushViewController:stickerPickerVC animated:YES];
} failure:^(NSError * _Nonnull error) {
NSLog(@"[RoomVC] Cannot display widget %@", widget);
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
else
{
// The Sticker picker widget is not installed yet. Propose the user to install it
__weak typeof(self) weakSelf = self;
[currentAlert dismissViewControllerAnimated:NO completion:nil];
NSString *alertMessage = [NSString stringWithFormat:@"%@\n%@",
NSLocalizedStringFromTable(@"widget_sticker_picker_no_stickerpacks_alert", @"Vector", nil),
NSLocalizedStringFromTable(@"widget_sticker_picker_no_stickerpacks_alert_add_now", @"Vector", nil)
];
currentAlert = [UIAlertController alertControllerWithTitle:nil message:alertMessage preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"no"]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action)
{
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
}]];
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"yes"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
// Show the sticker picker settings screen
IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc]
initForMXSession:self.roomDataSource.mxSession
inRoom:self.roomDataSource.roomId
screen:[IntegrationManagerViewController screenForWidget:kWidgetTypeStickerPicker]
widgetId:nil];
[self presentViewController:modularVC animated:NO completion:nil];
}
}]];
[currentAlert mxk_setAccessibilityIdentifier:@"RoomVCStickerPickerAlert"];
[self presentViewController:currentAlert animated:YES completion:nil];
}
}
#pragma mark - MXKRoomInputToolbarViewDelegate
- (void)roomInputToolbarView:(MXKRoomInputToolbarView*)toolbarView isTyping:(BOOL)typing
@ -3064,7 +3139,7 @@
// Matrix Apps button
else if (self.navigationItem.rightBarButtonItems.count == 2 && sender == self.navigationItem.rightBarButtonItems[1])
{
if ([self widgetsCount:YES])
if ([self widgetsCount:NO])
{
WidgetPickerViewController *widgetPicker = [[WidgetPickerViewController alloc] initForMXSession:self.roomDataSource.mxSession
inRoom:self.roomDataSource.roomId];
@ -3650,7 +3725,7 @@
inRoom:self.roomDataSource.room].count;
if (includeUserWidgets)
{
widgetsCount = [[WidgetManager sharedManager] userWidgets:self.roomDataSource.room.mxSession].count;
widgetsCount += [[WidgetManager sharedManager] userWidgets:self.roomDataSource.room.mxSession].count;
}
return widgetsCount;

View file

@ -34,6 +34,7 @@
#import "CountryPickerViewController.h"
#import "LanguagePickerViewController.h"
#import "DeactivateAccountViewController.h"
#import "NBPhoneNumberUtil.h"
#import "RageShakeManager.h"
@ -62,6 +63,7 @@ enum
SETTINGS_SECTION_CRYPTOGRAPHY_INDEX,
SETTINGS_SECTION_FLAIR_INDEX,
SETTINGS_SECTION_DEVICES_INDEX,
SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX,
SETTINGS_SECTION_COUNT
};
@ -130,7 +132,7 @@ enum {
typedef void (^blockSettingsViewController_onReadyToDestroy)();
@interface SettingsViewController ()
@interface SettingsViewController () <DeactivateAccountViewControllerDelegate>
{
// Current alert (if any).
UIAlertController *currentAlert;
@ -234,6 +236,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
*/
@property (nonatomic) BOOL newPhoneEditingEnabled;
@property (weak, nonatomic) DeactivateAccountViewController *deactivateAccountViewController;
@end
@implementation SettingsViewController
@ -258,6 +262,9 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
self.navigationItem.title = NSLocalizedStringFromTable(@"settings_title", @"Vector", nil);
// Remove back bar button title when pushing a view controller
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
[self.tableView registerClass:MXKTableViewCellWithLabelAndTextField.class forCellReuseIdentifier:[MXKTableViewCellWithLabelAndTextField defaultReuseIdentifier]];
[self.tableView registerClass:MXKTableViewCellWithLabelAndSwitch.class forCellReuseIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier]];
[self.tableView registerClass:MXKTableViewCellWithLabelAndMXKImageView.class forCellReuseIdentifier:[MXKTableViewCellWithLabelAndMXKImageView defaultReuseIdentifier]];
@ -310,6 +317,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector(onSave:)];
self.navigationItem.rightBarButtonItem.accessibilityIdentifier=@"SettingsVCNavBarSaveButton";
// Observe user interface theme change.
kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
@ -1268,6 +1276,10 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
count = CRYPTOGRAPHY_COUNT;
}
}
else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX)
{
count = 1;
}
return count;
}
@ -2151,6 +2163,32 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
cell = exportKeysBtnCell;
}
}
else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX)
{
MXKTableViewCellWithButton *deactivateAccountBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
if (!deactivateAccountBtnCell)
{
deactivateAccountBtnCell = [[MXKTableViewCellWithButton alloc] init];
}
else
{
// Fix https://github.com/vector-im/riot-ios/issues/1354
deactivateAccountBtnCell.mxkButton.titleLabel.text = nil;
}
NSString *btnTitle = NSLocalizedStringFromTable(@"settings_deactivate_my_account", @"Vector", nil);
[deactivateAccountBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
[deactivateAccountBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted];
[deactivateAccountBtnCell.mxkButton setTintColor:kRiotColorRed];
deactivateAccountBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
[deactivateAccountBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
[deactivateAccountBtnCell.mxkButton addTarget:self action:@selector(deactivateAccountAction) forControlEvents:UIControlEventTouchUpInside];
deactivateAccountBtnCell.mxkButton.accessibilityIdentifier = nil;
cell = deactivateAccountBtnCell;
}
return cell;
}
@ -2228,6 +2266,18 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
return NSLocalizedStringFromTable(@"settings_cryptography", @"Vector", nil);
}
}
else if (section == SETTINGS_SECTION_CRYPTOGRAPHY_INDEX)
{
// Check whether this section is visible
if (self.mainSession.crypto)
{
return NSLocalizedStringFromTable(@"settings_cryptography", @"Vector", nil);
}
}
else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX)
{
return NSLocalizedStringFromTable(@"settings_deactivate_my_account", @"Vector", nil);
}
return nil;
}
@ -3673,6 +3723,20 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
[self presentViewController:themePicker animated:YES completion:nil];
}
- (void)deactivateAccountAction
{
DeactivateAccountViewController *deactivateAccountViewController = [DeactivateAccountViewController instantiateWithMatrixSession:self.mainSession];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:deactivateAccountViewController];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:navigationController animated:YES completion:nil];
deactivateAccountViewController.delegate = self;
self.deactivateAccountViewController = deactivateAccountViewController;
}
#pragma mark - MediaPickerViewController Delegate
- (void)dismissMediaPicker
@ -4033,4 +4097,20 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
[self refreshSettings];
}
#pragma mark - DeactivateAccountViewControllerDelegate
- (void)deactivateAccountViewControllerDidDeactivateWithSuccess:(DeactivateAccountViewController *)deactivateAccountViewController
{
NSLog(@"[SettingsViewController] Deactivate account with success");
[[AppDelegate theDelegate] logoutWithCompletion:^(BOOL isLoggedOut) {
NSLog(@"[SettingsViewController] Complete clear user data after account deactivation");
}];
}
- (void)deactivateAccountViewControllerDidCancel:(DeactivateAccountViewController *)deactivateAccountViewController
{
[deactivateAccountViewController dismissViewControllerAnimated:YES completion:nil];
}
@end

View file

@ -37,4 +37,12 @@ FOUNDATION_EXPORT NSString *const kIntegrationManagerAddIntegrationScreen;
*/
- (instancetype)initForMXSession:(MXSession*)mxSession inRoom:(NSString*)roomId screen:(NSString*)screen widgetId:(NSString*)widgetId;
/**
Get the integration manager settings screen for a given widget type.
@param widgetType the widget type.
@return the screen id for that widget type.
*/
+ (NSString*)screenForWidget:(NSString*)widgetType;
@end

View file

@ -50,6 +50,11 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
return self;
}
+ (NSString*)screenForWidget:(NSString*)widgetType
{
return [NSString stringWithFormat:@"type_%@", widgetType];
}
- (void)destroy
{
[super destroy];
@ -147,8 +152,25 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
if (!roomIdInEvent)
{
[self sendLocalisedError:@"widget_integration_missing_room_id" toRequest:requestId];
return;
// These APIs don't require roomId
// Get and set user widgets (not associated with a specific room)
// If roomId is specified, it must be validated, so room-based widgets agreed
// handled further down.
if ([@"set_widget" isEqualToString:action])
{
[self setWidget:requestId data:requestData];
return;
}
else if ([@"get_widgets" isEqualToString:action])
{
[self getWidgets:requestId data:requestData];
return;
}
else
{
[self sendLocalisedError:@"widget_integration_missing_room_id" toRequest:requestId];
return;
}
}
if (![roomIdInEvent isEqualToString:roomId])
@ -157,6 +179,17 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
return;
}
// Get and set room-based widgets
if ([@"set_widget" isEqualToString:action])
{
[self setWidget:requestId data:requestData];
return;
}
else if ([@"get_widgets" isEqualToString:action])
{
[self getWidgets:requestId data:requestData];
return;
}
// These APIs don't require userId
if ([@"join_rules_state" isEqualToString:action])
@ -174,14 +207,9 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
[self getMembershipCount:requestId data:requestData];
return;
}
else if ([@"set_widget" isEqualToString:action])
else if ([@"get_room_enc_state" isEqualToString:action])
{
[self setWidget:requestId data:requestData];
return;
}
else if ([@"get_widgets" isEqualToString:action])
{
[self getWidgets:requestId data:requestData];
[self getRoomEncState:requestId data:requestData];
return;
}
else if ([@"can_send_event" isEqualToString:action])
@ -281,75 +309,120 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
- (void)setWidget:(NSString*)requestId data:(NSDictionary*)requestData
{
NSLog(@"[IntegrationManagerVC] Received request to set widget in room %@.", roomId);
NSLog(@"[IntegrationManagerVC] Received request to set widget");
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
NSString *widget_id, *widgetType, *widgetUrl;
NSString *widgetName; // optional
NSDictionary *widgetData ; // optional
BOOL userWidget = NO;
if (room)
MXJSONModelSetString(widget_id, requestData[@"widget_id"]);
MXJSONModelSetString(widgetType, requestData[@"type"]);
MXJSONModelSetString(widgetUrl, requestData[@"url"]);
MXJSONModelSetString(widgetName, requestData[@"name"]);
MXJSONModelSetDictionary(widgetData, requestData[@"data"]);
MXJSONModelSetBoolean(userWidget, requestData[@"userWidget"]);
if (!widget_id)
{
NSString *widget_id, *widgetType, *widgetUrl;
NSString *widgetName; // optional
NSDictionary *widgetData ; // optional
[self sendLocalisedError:@"widget_integration_unable_to_create" toRequest:requestId]; // new Error("Missing required widget fields."));
return;
}
MXJSONModelSetString(widget_id, requestData[@"widget_id"]);
MXJSONModelSetString(widgetType, requestData[@"type"]);
MXJSONModelSetString(widgetUrl, requestData[@"url"]);
MXJSONModelSetString(widgetName, requestData[@"name"]);
MXJSONModelSetDictionary(widgetData, requestData[@"data"]);
if (!widgetType)
{
[self sendLocalisedError:@"widget_integration_unable_to_create" toRequest:requestId];
return;
}
if (!widget_id)
NSMutableDictionary *widgetEventContent = [NSMutableDictionary dictionary];
if (widgetUrl)
{
widgetEventContent[@"type"] = widgetType;
widgetEventContent[@"url"] = widgetUrl;
if (widgetName)
{
[self sendLocalisedError:@"widget_integration_unable_to_create" toRequest:requestId]; // new Error("Missing required widget fields."));
return;
widgetEventContent[@"name"] = widgetName;
}
if (widgetData)
{
widgetEventContent[@"data"] = widgetData;
}
}
// else this is a deletion
NSMutableDictionary *widgetEventContent = [NSMutableDictionary dictionary];
__weak __typeof__(self) weakSelf = self;
if (userWidget)
{
// Update the user account data
NSMutableDictionary *userWidgets = [NSMutableDictionary dictionaryWithDictionary:[mxSession.accountData accountDataForEventType:kMXAccountDataTypeUserWidgets]];
// Delete existing widget with ID
[userWidgets removeObjectForKey:widget_id];
// Add new widget / update
if (widgetUrl)
{
if (!widgetType)
{
[self sendLocalisedError:@"widget_integration_unable_to_create" toRequest:requestId];
return;
}
widgetEventContent[@"type"] = widgetType;
widgetEventContent[@"url"] = widgetUrl;
if (widgetName)
{
widgetEventContent[@"name"] = widgetName;
}
if (widgetData)
{
widgetEventContent[@"data"] = widgetData;
}
userWidgets[widget_id] = @{
@"content": widgetEventContent,
@"sender": mxSession.myUser.userId,
@"state_key": widget_id,
@"type": kWidgetMatrixEventTypeString,
@"id": widget_id,
};
}
__weak __typeof__(self) weakSelf = self;
[mxSession setAccountData:userWidgets forType:kMXAccountDataTypeUserWidgets success:^{
// TODO: Move to kWidgetMatrixEventTypeString ("m.widget") type but when?
[room sendStateEventOfType:kWidgetModularEventTypeString
content:widgetEventContent
stateKey:widget_id
success:^(NSString *eventId) {
typeof(self) self = weakSelf;
if (self)
{
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toRequest:requestId];
}
} failure:^(NSError *error) {
typeof(self) self = weakSelf;
if (self)
{
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toRequest:requestId];
typeof(self) self = weakSelf;
if (self)
{
[self sendLocalisedError:@"widget_integration_unable_to_create" toRequest:requestId];
}
}];
}
else
{
// Room widget
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
if (room)
{
// TODO: Move to kWidgetMatrixEventTypeString ("m.widget") type but when?
[room sendStateEventOfType:kWidgetModularEventTypeString
content:widgetEventContent
stateKey:widget_id
success:^(NSString *eventId) {
typeof(self) self = weakSelf;
if (self)
{
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toRequest:requestId];
}
}
}
failure:^(NSError *error) {
failure:^(NSError *error) {
typeof(self) self = weakSelf;
if (self)
{
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toRequest:requestId];
}
}];
typeof(self) self = weakSelf;
if (self)
{
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toRequest:requestId];
}
}];
}
}
}
@ -376,6 +449,15 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
[self sendNSObjectResponse:widgetStateEvents toRequest:requestId];
}
- (void)getRoomEncState:(NSString*)requestId data:(NSDictionary*)requestData
{
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
if (room)
{
[self sendBoolResponse:room.state.isEncrypted toRequest:requestId];
}
}
- (void)canSendEvent:(NSString*)requestId data:(NSDictionary*)requestData
{
NSString *eventType;

View file

@ -0,0 +1,21 @@
/*
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 "WidgetViewController.h"
@interface StickerPickerViewController : WidgetViewController
@end

View file

@ -0,0 +1,60 @@
/*
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 "StickerPickerViewController.h"
#import "IntegrationManagerViewController.h"
@interface StickerPickerViewController ()
@end
@implementation StickerPickerViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.title = NSLocalizedStringFromTable(@"room_action_send_sticker", @"Vector", nil);
// Hide back button title
self.parentViewController.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
UIBarButtonItem *editButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:@selector(onEditButtonPressed)];
[self.navigationItem setRightBarButtonItem: editButton animated:YES];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Make sure the content is up-to-date when we come back from the sticker picker settings screen
[webView reload];
}
- (void)onEditButtonPressed
{
// Show the sticker picker settings screen
IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc]
initForMXSession:self.roomDataSource.mxSession
inRoom:self.roomDataSource.roomId
screen:[IntegrationManagerViewController screenForWidget:kWidgetTypeStickerPicker]
widgetId:self.widget.widgetId];
[self presentViewController:modularVC animated:NO completion:nil];
}
@end

View file

@ -53,15 +53,9 @@
MXRoom *room = [mxSession roomWithRoomId:roomId];
NSArray<Widget*> *roomWidgets = [[WidgetManager sharedManager] widgetsNotOfTypes:@[kWidgetTypeJitsi]
NSArray<Widget*> *widgets = [[WidgetManager sharedManager] widgetsNotOfTypes:@[kWidgetTypeJitsi]
inRoom:room];
NSArray<Widget*> *userWidgets = [[WidgetManager sharedManager] userWidgets:room.mxSession];
NSMutableArray<Widget*> *widgets = [NSMutableArray array];
[widgets addObjectsFromArray:roomWidgets];
[widgets addObjectsFromArray:userWidgets];
// List widgets
for (Widget *widget in widgets)
{

View file

@ -27,6 +27,11 @@
*/
@interface WidgetViewController : WebViewViewController
/**
The displayed widget.
*/
@property (nonatomic, readonly) Widget *widget;
/**
The room data source.
Required if the widget needs to post messages.

View file

@ -1,5 +1,6 @@
/*
Copyright 2017 Vector Creations Ltd
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.
@ -17,17 +18,16 @@
#import "WidgetViewController.h"
#import "AppDelegate.h"
#import "IntegrationManagerViewController.h"
NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse('%@', %@);";
@interface WidgetViewController ()
{
Widget *widget;
}
@end
@implementation WidgetViewController
@synthesize widget;
- (instancetype)initWithUrl:(NSString*)widgetUrl forWidget:(Widget*)theWidget
{
@ -43,7 +43,6 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
{
[super viewDidLoad];
webView.scalesPageToFit = NO;
webView.scrollView.bounces = NO;
// Disable opacity so that the webview background uses the current interface theme
@ -92,16 +91,16 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
[self presentViewController:alert animated:YES completion:nil];
}
#pragma mark - UIWebViewDelegate
#pragma mark - WKNavigationDelegate
-(void)webViewDidFinishLoad:(UIWebView *)theWebView
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
[self enableDebug];
// Setup js code
NSString *path = [[NSBundle mainBundle] pathForResource:@"postMessageAPI" ofType:@"js"];
NSString *js = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[webView stringByEvaluatingJavaScriptFromString:js];
[webView evaluateJavaScript:js completionHandler:nil];
[self stopActivityIndicator];
@ -120,14 +119,16 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
}
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
NSString *urlString = [[request URL] absoluteString];
NSString *urlString = navigationAction.request.URL.absoluteString;
// TODO: We should use the WebKit PostMessage API and the
// `didReceiveScriptMessage` delegate to manage the JS<->Native bridge
if ([urlString hasPrefix:@"js:"])
{
// Listen only to scheme of the JS-UIWebView bridge
NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
// Listen only to the scheme of the JS<->Native bridge
NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByRemovingPercentEncoding];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
@ -152,20 +153,22 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
}
}
return NO;
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
if (navigationType == UIWebViewNavigationTypeLinkClicked )
if (navigationAction.navigationType == WKNavigationTypeLinkActivated)
{
// Open links outside the app
[[UIApplication sharedApplication] openURL:[request URL]];
return NO;
[[UIApplication sharedApplication] openURL:navigationAction.request.URL];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
return YES;
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
// Filter out the users's scalar token
NSString *errorDescription = error.description;
@ -216,6 +219,37 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
// Consider we are done with the sticker picker widget
[self withdrawViewControllerAnimated:YES completion:nil];
}
else if ([@"integration_manager_open" isEqualToString:action])
{
NSDictionary *widgetData;
NSString *integType, *integId;
MXJSONModelSetDictionary(widgetData, requestData[@"widgetData"]);
if (widgetData)
{
MXJSONModelSetString(integType, widgetData[@"integType"]);
MXJSONModelSetString(integId, widgetData[@"integId"]);
}
if (integType && integId)
{
// Open the integration manager requested page
IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc]
initForMXSession:self.roomDataSource.mxSession
inRoom:self.roomDataSource.roomId
screen:[IntegrationManagerViewController screenForWidget:integType]
widgetId:integId];
[self presentViewController:modularVC animated:NO completion:nil];
}
else
{
NSLog(@"[WidgetVC] onPostMessageRequest: ERROR: Invalid content for integration_manager_open: %@", requestData);
}
}
else
{
NSLog(@"[WidgetVC] onPostMessageRequest: ERROR: Unsupported action: %@: %@", action, requestData);
}
}
- (void)sendBoolResponse:(BOOL)response toRequest:(NSString*)requestId
@ -225,7 +259,7 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
requestId,
response ? @"true" : @"false"];
[webView stringByEvaluatingJavaScriptFromString:js];
[webView evaluateJavaScript:js completionHandler:nil];
}
- (void)sendIntegerResponse:(NSUInteger)response toRequest:(NSString*)requestId
@ -234,7 +268,7 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
requestId,
@(response)];
[webView stringByEvaluatingJavaScriptFromString:js];
[webView evaluateJavaScript:js completionHandler:nil];
}
- (void)sendNSObjectResponse:(NSObject*)response toRequest:(NSString*)requestId
@ -260,7 +294,7 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
requestId,
jsString];
[webView stringByEvaluatingJavaScriptFromString:js];
[webView evaluateJavaScript:js completionHandler:nil];
}
- (void)sendError:(NSString*)message toRequest:(NSString*)requestId

View file

@ -18,12 +18,28 @@
#import "MediaPickerViewController.h"
@protocol RoomInputToolbarViewDelegate <MXKRoomInputToolbarViewDelegate>
/**
Tells the delegate that the user wants to display the sticker picker.
@param toolbarView the room input toolbar view.
*/
- (void)roomInputToolbarViewPresentStickerPicker:(MXKRoomInputToolbarView*)toolbarView;
@end
/**
`RoomInputToolbarView` instance is a view used to handle all kinds of available inputs
for a room (message composer, attachments selection...).
*/
@interface RoomInputToolbarView : MXKRoomInputToolbarViewWithHPGrowingText <MediaPickerViewControllerDelegate>
/**
The delegate notified when inputs are ready.
*/
@property (nonatomic) id<RoomInputToolbarViewDelegate> delegate;
@property (weak, nonatomic) IBOutlet UIView *mainToolbarView;
@property (weak, nonatomic) IBOutlet UIView *separatorView;

View file

@ -29,17 +29,21 @@
#import <MobileCoreServices/MobileCoreServices.h>
#import "WidgetManager.h"
#import "IntegrationManagerViewController.h"
@interface RoomInputToolbarView()
{
MediaPickerViewController *mediaPicker;
// The call type selection (voice or video)
UIAlertController *callActionSheet;
// The intermediate action sheet
UIAlertController *actionSheet;
}
@end
@implementation RoomInputToolbarView
@dynamic delegate;
+ (UINib *)nib
{
@ -228,24 +232,53 @@
// Check whether media attachment is supported
if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:presentViewController:)])
{
// MediaPickerViewController is based on the Photos framework. So it is available only for iOS 8 and later.
Class PHAsset_class = NSClassFromString(@"PHAsset");
if (PHAsset_class)
{
mediaPicker = [MediaPickerViewController mediaPickerViewController];
mediaPicker.mediaTypes = @[(NSString *)kUTTypeImage, (NSString *)kUTTypeMovie];
mediaPicker.delegate = self;
UINavigationController *navigationController = [UINavigationController new];
[navigationController pushViewController:mediaPicker animated:NO];
[self.delegate roomInputToolbarView:self presentViewController:navigationController];
}
else
{
// We use UIImagePickerController by default for iOS < 8
self.leftInputToolbarButton = self.attachMediaButton;
[super onTouchUpInside:self.leftInputToolbarButton];
}
// Ask the user the kind of the call: voice or video?
actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
__weak typeof(self) weakSelf = self;
[actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_action_send_photo_or_video", @"Vector", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->actionSheet = nil;
[self showMediaPicker];
}
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_action_send_sticker", @"Vector", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->actionSheet = nil;
[self.delegate roomInputToolbarViewPresentStickerPicker:self];
}
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->actionSheet = nil;
}
}]];
[actionSheet popoverPresentationController].sourceView = self.voiceCallButton;
[actionSheet popoverPresentationController].sourceRect = self.voiceCallButton.bounds;
[self.window.rootViewController presentViewController:actionSheet animated:YES completion:nil];
}
else
{
@ -257,52 +290,52 @@
if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:placeCallWithVideo:)])
{
// Ask the user the kind of the call: voice or video?
callActionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
__weak typeof(self) weakSelf = self;
[callActionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"voice", @"Vector", nil)
[actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"voice", @"Vector", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->callActionSheet = nil;
self->actionSheet = nil;
[self.delegate roomInputToolbarView:self placeCallWithVideo:NO];
}
}]];
[callActionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"video", @"Vector", nil)
[actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"video", @"Vector", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->callActionSheet = nil;
self->actionSheet = nil;
[self.delegate roomInputToolbarView:self placeCallWithVideo:YES];
}
}]];
[callActionSheet addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
[actionSheet addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->callActionSheet = nil;
self->actionSheet = nil;
}
}]];
[callActionSheet popoverPresentationController].sourceView = self.voiceCallButton;
[callActionSheet popoverPresentationController].sourceRect = self.voiceCallButton.bounds;
[self.window.rootViewController presentViewController:callActionSheet animated:YES completion:nil];
[actionSheet popoverPresentationController].sourceView = self.voiceCallButton;
[actionSheet popoverPresentationController].sourceRect = self.voiceCallButton.bounds;
[self.window.rootViewController presentViewController:actionSheet animated:YES completion:nil];
}
}
else if (button == self.hangupCallButton)
@ -316,15 +349,36 @@
[super onTouchUpInside:button];
}
- (void)showMediaPicker
{
// MediaPickerViewController is based on the Photos framework. So it is available only for iOS 8 and later.
Class PHAsset_class = NSClassFromString(@"PHAsset");
if (PHAsset_class)
{
mediaPicker = [MediaPickerViewController mediaPickerViewController];
mediaPicker.mediaTypes = @[(NSString *)kUTTypeImage, (NSString *)kUTTypeMovie];
mediaPicker.delegate = self;
UINavigationController *navigationController = [UINavigationController new];
[navigationController pushViewController:mediaPicker animated:NO];
[self.delegate roomInputToolbarView:self presentViewController:navigationController];
}
else
{
// We use UIImagePickerController by default for iOS < 8
self.leftInputToolbarButton = self.attachMediaButton;
[super onTouchUpInside:self.leftInputToolbarButton];
}
}
- (void)destroy
{
[self dismissMediaPicker];
if (callActionSheet)
if (actionSheet)
{
[callActionSheet dismissViewControllerAnimated:NO completion:nil];
callActionSheet = nil;
[actionSheet dismissViewControllerAnimated:NO completion:nil];
actionSheet = nil;
}
[super destroy];