element-ios/Riot/Categories/MXRoom+Riot.m

676 lines
23 KiB
Objective-C

/*
Copyright 2015 OpenMarket Ltd
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.
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 "MXRoom+Riot.h"
#import "AvatarGenerator.h"
#import "MatrixKit.h"
#import "GeneratedInterface-Swift.h"
#import <objc/runtime.h>
@implementation MXRoom (Riot)
#pragma mark - Room tags
- (void)setRoomTag:(NSString*)tag completion:(void (^)(void))completion
{
NSString* oldTag = nil;
if (self.accountData.tags && self.accountData.tags.count)
{
oldTag = self.accountData.tags.allKeys[0];
}
// support only kMXRoomTagFavourite or kMXRoomTagLowPriority tags by now
if (![tag isEqualToString:kMXRoomTagFavourite] && ![tag isEqualToString:kMXRoomTagLowPriority])
{
tag = nil;
}
NSString* tagOrder = [self.mxSession tagOrderToBeAtIndex:0 from:NSNotFound withTag:tag];
MXLogDebug(@"[MXRoom+Riot] Update the room %@ tag from %@ to %@ with tag order %@", self.roomId, oldTag, tag, tagOrder);
[self replaceTag:oldTag
byTag:tag
withOrder:tagOrder
success: ^{
if (completion)
{
completion();
}
} failure:^(NSError *error) {
MXLogDebug(@"[MXRoom+Riot] Failed to update the tag %@ of room (%@)", tag, self.roomId);
NSString *userId = self.mxSession.myUser.userId;
// Notify user
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification
object:error
userInfo:userId ? @{kMXKErrorUserIdKey: userId} : nil];
if (completion)
{
completion();
}
}];
}
#pragma mark - Room notification mode
- (BOOL)isMute
{
// Check whether an override rule has been defined with the roomm id as rule id.
// This kind of rule is created to mute the room
MXPushRule* rule = [self getOverrideRoomPushRule];
if (rule && rule.enabled)
{
// Support for MSC3987: The dont_notify push rule action is deprecated.
if (rule.actions.count == 0)
{
return true;
}
// Support deprecated dont_notify push rule action for compatibility purposes.
for (MXPushRuleAction *ruleAction in rule.actions)
{
if (ruleAction.actionType == MXPushRuleActionTypeDontNotify)
{
for (MXPushRuleCondition *ruleCondition in rule.conditions)
{
if (ruleCondition.kindType == MXPushRuleConditionTypeEventMatch)
{
NSString *key;
NSString *pattern;
MXJSONModelSetString(key, ruleCondition.parameters[@"key"]);
MXJSONModelSetString(pattern, ruleCondition.parameters[@"pattern"]);
if (key && pattern && [key isEqualToString:@"room_id"] && [pattern isEqualToString:self.roomId])
{
return true;
}
}
}
}
}
}
return NO;
}
- (BOOL)isMentionsOnly
{
// Check push rules at room level
MXPushRule *rule = [self getRoomPushRule];
if (rule && rule.enabled)
{
// Support for MSC3987: The dont_notify push rule action is deprecated.
if (rule.actions.count == 0)
{
return true;
}
// Support deprecated dont_notify push rule action for compatibility purposes.
for (MXPushRuleAction *ruleAction in rule.actions)
{
if (ruleAction.actionType == MXPushRuleActionTypeDontNotify)
{
return true;
}
}
}
return NO;
}
- (void)mute:(void (^)(void))completion
{
// Check the current notification mode
if (self.isMute)
{
if (completion)
{
completion();
}
return;
}
// Check whether a rule at room level must be removed first
if (self.isMentionsOnly)
{
MXPushRule* rule = [self getRoomPushRule];
[self removePushRule:rule completion:^{
[self mute:completion];
}];
return;
}
// The user does not want to have push at all
MXPushRule* rule = [self getOverrideRoomPushRule];
// Check if no rule is already defined.
if (!rule)
{
// Add a new one
[self addPushRuleToMute:completion];
}
else
{
// Check whether there is no pending update for this room
if (self.notificationCenterDidUpdateObserver)
{
MXLogDebug(@"[MXRoom+Riot] Request in progress: ignore push rule update");
if (completion)
{
completion();
}
return;
}
// check if the user did not define one
BOOL hasDontNotifyRule = NO;
// Support for MSC3987: The dont_notify push rule action is deprecated.
if (rule.actions.count == 0)
{
hasDontNotifyRule = YES;
}
else
{
// Support deprecated dont_notify push rule action for compatibility purposes.
for (MXPushRuleAction *ruleAction in rule.actions)
{
if (ruleAction.actionType == MXPushRuleActionTypeDontNotify)
{
hasDontNotifyRule = YES;
break;
}
}
}
// if the user defined one, use it
if (hasDontNotifyRule)
{
[self enablePushRule:rule completion:completion];
}
else
{
// If the user has defined a room rule, the rule is deleted before adding new one.
[self removePushRule:rule completion:^{
// Add new rule to disable notification
[self addPushRuleToMute:completion];
}];
}
}
}
- (void)mentionsOnly:(void (^)(void))completion
{
// Check the current notification mode
if (self.isMentionsOnly)
{
if (completion)
{
completion();
}
return;
}
// Check whether an override rule must be removed first
if (self.mute)
{
MXPushRule* rule = [self getOverrideRoomPushRule];
[self removePushRule:rule completion:^{
[self mentionsOnly:completion];
}];
return;
}
// The user wants to have push only for highlighted notifications
MXPushRule* rule = [self getRoomPushRule];
// Check if no rule is already defined.
if (!rule)
{
// Add a new one
[self addPushRuleToMentionsOnly:completion];
}
else
{
// Check whether there is no pending update for this room
if (self.notificationCenterDidUpdateObserver)
{
MXLogDebug(@"[MXRoom+Riot] Request in progress: ignore push rule update");
if (completion)
{
completion();
}
return;
}
// check if the user did not define one
BOOL hasDontNotifyRule = NO;
// Support for MSC3987: The dont_notify push rule action is deprecated.
if (rule.actions.count == 0)
{
hasDontNotifyRule = YES;
}
else
{
// Support deprecated dont_notify push rule action for compatibility purposes.
for (MXPushRuleAction *ruleAction in rule.actions)
{
if (ruleAction.actionType == MXPushRuleActionTypeDontNotify)
{
hasDontNotifyRule = YES;
break;
}
}
}
// if the user defined one, use it
if (hasDontNotifyRule)
{
[self enablePushRule:rule completion:completion];
}
else
{
// If the user has defined a room rule, the rule is deleted before adding new one.
[self removePushRule:rule completion:^{
// Add new rule to disable notification
[self addPushRuleToMentionsOnly:completion];
}];
}
}
}
- (void)allMessages:(void (^)(void))completion
{
// Check the current notification mode
if (!self.isMentionsOnly && !self.isMute)
{
// Nothing to do
if (completion)
{
completion();
}
return;
}
// Check whether an override rule must be removed first
if (self.isMute)
{
MXPushRule* rule = [self getOverrideRoomPushRule];
[self removePushRule:rule completion:^{
// Check the push rule at room level now
[self allMessages:completion];
}];
return;
}
// Check whether a rule at room level must be removed
if (self.isMentionsOnly)
{
MXPushRule* rule = [self getRoomPushRule];
[self removePushRule:rule completion:^{
// let the other notification rules manage the pushes.
[self removePushRule:rule completion:completion];
}];
}
}
- (void)encryptionTrustLevelForUserId:(NSString*)userId onComplete:(void (^)(UserEncryptionTrustLevel userEncryptionTrustLevel))onComplete
{
if (self.mxSession.crypto)
{
[self.mxSession.crypto trustLevelSummaryForUserIds:@[userId] forceDownload:NO success:^(MXUsersTrustLevelSummary *usersTrustLevelSummary) {
MXCrossSigningInfo *crossSigningInfo = [self.mxSession.crypto.crossSigning crossSigningKeysForUser:userId];
EncryptionTrustLevel *encryption = [[EncryptionTrustLevel alloc] init];
UserEncryptionTrustLevel userEncryptionTrustLevel = [encryption userTrustLevelWithCrossSigning:crossSigningInfo
trustedDevicesProgress:usersTrustLevelSummary.trustedDevicesProgress];
onComplete(userEncryptionTrustLevel);
} failure:^(NSError *error) {
MXLogErrorDetails(@"[MXRoom+Riot] Error fetching trust level summary", error);
onComplete(UserEncryptionTrustLevelUnknown);
}];
}
else
{
onComplete(UserEncryptionTrustLevelNone);
}
}
#pragma mark -
- (MXPushRule*)getRoomPushRule
{
NSArray* rules = self.mxSession.notificationCenter.rules.global.room;
// sanity checks
if (rules)
{
for(MXPushRule* rule in rules)
{
// the rule id is the room Id
// it is the server trick to avoid duplicated rule on the same room.
if ([rule.ruleId isEqualToString:self.roomId])
{
return rule;
}
}
}
return nil;
}
- (MXPushRule*)getOverrideRoomPushRule
{
NSArray* rules = self.mxSession.notificationCenter.rules.global.override;
// sanity checks
if (rules)
{
for(MXPushRule* rule in rules)
{
// the rule id is the room Id
// it is the server trick to avoid duplicated rule on the same room.
if ([rule.ruleId isEqualToString:self.roomId])
{
return rule;
}
}
}
return nil;
}
- (void)addPushRuleToMentionsOnly:(void (^)(void))completion
{
MXNotificationCenter* notificationCenter = self.mxSession.notificationCenter;
// Define notificationCenter observers if a completion block is defined.
if (completion)
{
__weak typeof(self) weakSelf = self;
self.notificationCenterDidUpdateObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXNotificationCenterDidUpdateRules object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// Check whether the rule has been added
BOOL isAdded = ([self getRoomPushRule] != nil);
if (isAdded)
{
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf.notificationCenterDidUpdateObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidUpdateObserver];
strongSelf.notificationCenterDidUpdateObserver = nil;
}
if (strongSelf.notificationCenterDidFailObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidFailObserver];
strongSelf.notificationCenterDidFailObserver = nil;
}
completion();
}
}];
self.notificationCenterDidFailObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXNotificationCenterDidFailRulesUpdate object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf.notificationCenterDidUpdateObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidUpdateObserver];
strongSelf.notificationCenterDidUpdateObserver = nil;
}
if (strongSelf.notificationCenterDidFailObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidFailObserver];
strongSelf.notificationCenterDidFailObserver = nil;
}
completion();
}];
}
[notificationCenter addRoomRule:self.roomId
notify:NO
sound:NO
highlight:NO];
}
- (void)addPushRuleToMute:(void (^)(void))completion
{
MXNotificationCenter* notificationCenter = self.mxSession.notificationCenter;
// Define notificationCenter observers if a completion block is defined.
if (completion)
{
__weak typeof(self) weakSelf = self;
self.notificationCenterDidUpdateObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXNotificationCenterDidUpdateRules object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// Check whether the rule has been added
BOOL isAdded = ([self getOverrideRoomPushRule] != nil);
if (isAdded)
{
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf.notificationCenterDidUpdateObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidUpdateObserver];
strongSelf.notificationCenterDidUpdateObserver = nil;
}
if (strongSelf.notificationCenterDidFailObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidFailObserver];
strongSelf.notificationCenterDidFailObserver = nil;
}
completion();
}
}];
self.notificationCenterDidFailObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXNotificationCenterDidFailRulesUpdate object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf.notificationCenterDidUpdateObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidUpdateObserver];
strongSelf.notificationCenterDidUpdateObserver = nil;
}
if (strongSelf.notificationCenterDidFailObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidFailObserver];
strongSelf.notificationCenterDidFailObserver = nil;
}
completion();
}];
}
[notificationCenter addOverrideRuleWithId:self.roomId
conditions:@[@{@"kind":@"event_match", @"key":@"room_id", @"pattern":self.roomId}]
notify:NO
sound:NO
highlight:NO];
}
- (void)removePushRule:(MXPushRule *)rule completion:(void (^)(void))completion
{
MXNotificationCenter* notificationCenter = self.mxSession.notificationCenter;
// Define notificationCenter observers if a completion block is defined.
if (completion)
{
__weak typeof(self) weakSelf = self;
self.notificationCenterDidUpdateObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXNotificationCenterDidUpdateRules object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// Check whether the rule has been removed
BOOL isRemoved = ([notificationCenter ruleById:rule.ruleId] == nil);
if (isRemoved)
{
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf.notificationCenterDidUpdateObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidUpdateObserver];
strongSelf.notificationCenterDidUpdateObserver = nil;
}
if (strongSelf.notificationCenterDidFailObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidFailObserver];
strongSelf.notificationCenterDidFailObserver = nil;
}
completion();
}
}];
self.notificationCenterDidFailObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXNotificationCenterDidFailRulesUpdate object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf.notificationCenterDidUpdateObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidUpdateObserver];
strongSelf.notificationCenterDidUpdateObserver = nil;
}
if (strongSelf.notificationCenterDidFailObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidFailObserver];
strongSelf.notificationCenterDidFailObserver = nil;
}
completion();
}];
}
[notificationCenter removeRule:rule];
}
- (void)enablePushRule:(MXPushRule *)rule completion:(void (^)(void))completion
{
MXNotificationCenter* notificationCenter = self.mxSession.notificationCenter;
// Define notificationCenter observers if a completion block is defined.
if (completion)
{
__weak typeof(self) weakSelf = self;
self.notificationCenterDidUpdateObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXNotificationCenterDidUpdateRules object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// No way to check whether this notification concerns the push rule. Consider the change is applied.
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf.notificationCenterDidUpdateObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidUpdateObserver];
strongSelf.notificationCenterDidUpdateObserver = nil;
}
if (strongSelf.notificationCenterDidFailObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidFailObserver];
strongSelf.notificationCenterDidFailObserver = nil;
}
completion();
}];
self.notificationCenterDidFailObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXNotificationCenterDidFailRulesUpdate object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf.notificationCenterDidUpdateObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidUpdateObserver];
strongSelf.notificationCenterDidUpdateObserver = nil;
}
if (strongSelf.notificationCenterDidFailObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf.notificationCenterDidFailObserver];
strongSelf.notificationCenterDidFailObserver = nil;
}
completion();
}];
}
[notificationCenter enableRule:rule isEnabled:YES completion:nil];
}
- (void)setNotificationCenterDidFailObserver:(id)anObserver
{
objc_setAssociatedObject(self, @selector(notificationCenterDidFailObserver), anObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)notificationCenterDidFailObserver
{
return objc_getAssociatedObject(self, @selector(notificationCenterDidFailObserver));
}
- (void)setNotificationCenterDidUpdateObserver:(id)anObserver
{
objc_setAssociatedObject(self, @selector(notificationCenterDidUpdateObserver), anObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)notificationCenterDidUpdateObserver
{
return objc_getAssociatedObject(self, @selector(notificationCenterDidUpdateObserver));
}
@end