2017-08-04 11:05:07 +00:00
|
|
|
/*
|
|
|
|
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 "WidgetManager.h"
|
|
|
|
|
|
|
|
#pragma mark - Contants
|
|
|
|
|
|
|
|
NSString *const kWidgetEventTypeString = @"im.vector.modular.widgets";
|
|
|
|
NSString *const kWidgetTypeJitsi = @"jitsi";
|
|
|
|
|
2017-08-09 15:31:15 +00:00
|
|
|
NSString *const kMXKWidgetManagerDidUpdateWidgetNotification = @"kMXKWidgetManagerDidUpdateWidgetNotification";
|
|
|
|
|
2017-08-17 10:17:07 +00:00
|
|
|
NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
|
|
|
|
2017-08-04 11:05:07 +00:00
|
|
|
@interface WidgetManager ()
|
|
|
|
{
|
2017-08-09 16:06:33 +00:00
|
|
|
// MXSession kind of hash -> Listener for matrix events for widgets.
|
2017-08-16 09:49:01 +00:00
|
|
|
// There is one per matrix session
|
2017-08-04 11:05:07 +00:00
|
|
|
NSMutableDictionary<NSString*, id> *widgetEventListener;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation WidgetManager
|
|
|
|
|
|
|
|
+ (instancetype)sharedManager
|
|
|
|
{
|
|
|
|
static WidgetManager *sharedManager = nil;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
sharedManager = [[WidgetManager alloc] init];
|
|
|
|
});
|
|
|
|
|
|
|
|
return sharedManager;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (instancetype)init
|
|
|
|
{
|
|
|
|
self = [super init];
|
|
|
|
if (self)
|
|
|
|
{
|
|
|
|
widgetEventListener = [NSMutableDictionary dictionary];
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray<Widget *> *)widgetsInRoom:(MXRoom *)room
|
|
|
|
{
|
2017-08-16 09:49:01 +00:00
|
|
|
return [self widgetsOfTypes:nil inRoom:room];
|
|
|
|
}
|
2017-08-10 13:43:25 +00:00
|
|
|
|
2017-08-16 09:49:01 +00:00
|
|
|
- (NSArray<Widget *> *)widgetsOfTypes:(NSArray<NSString *> *)widgetTypes inRoom:(MXRoom *)room
|
|
|
|
{
|
2017-08-09 14:48:05 +00:00
|
|
|
// Widget id -> widget
|
|
|
|
NSMutableDictionary <NSString*, Widget *> *widgets = [NSMutableDictionary dictionary];
|
2017-08-04 11:05:07 +00:00
|
|
|
|
2017-08-09 14:48:05 +00:00
|
|
|
// Get all im.vector.modular.widgets state events in the room
|
2017-08-09 15:59:10 +00:00
|
|
|
NSMutableArray<MXEvent*> *widgetEvents = [NSMutableArray arrayWithArray:[room.state stateEventsWithType:kWidgetEventTypeString]];
|
2017-08-04 11:05:07 +00:00
|
|
|
|
2017-08-09 14:48:05 +00:00
|
|
|
// There can be several im.vector.modular.widgets state events for a same widget but
|
|
|
|
// only the last one must be considered.
|
2017-08-09 15:59:10 +00:00
|
|
|
|
|
|
|
// Order widgetEvents with the last event first
|
|
|
|
[widgetEvents sortUsingComparator:^NSComparisonResult(MXEvent *event1, MXEvent *event2) {
|
|
|
|
|
|
|
|
NSComparisonResult result = NSOrderedAscending;
|
|
|
|
if (event2.originServerTs > event1.originServerTs)
|
|
|
|
{
|
|
|
|
result = NSOrderedDescending;
|
|
|
|
}
|
|
|
|
else if (event2.originServerTs == event1.originServerTs)
|
|
|
|
{
|
|
|
|
result = NSOrderedSame;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}];
|
|
|
|
|
2017-08-16 09:49:01 +00:00
|
|
|
// Create each widget from its lastest im.vector.modular.widgets state event
|
2017-08-09 15:59:10 +00:00
|
|
|
for (MXEvent *widgetEvent in widgetEvents)
|
2017-08-04 11:05:07 +00:00
|
|
|
{
|
2017-08-16 09:49:01 +00:00
|
|
|
// Filter widget types if required
|
|
|
|
if (widgetTypes)
|
|
|
|
{
|
|
|
|
NSString *widgetType;
|
|
|
|
MXJSONModelSetString(widgetType, widgetEvent.content[@"type"]);
|
|
|
|
|
2017-08-16 15:05:14 +00:00
|
|
|
if (widgetType && NSNotFound == [widgetTypes indexOfObject:widgetType])
|
2017-08-16 09:49:01 +00:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-09 15:31:15 +00:00
|
|
|
// widgetEvent.stateKey = widget id
|
2017-08-09 14:48:05 +00:00
|
|
|
if (!widgets[widgetEvent.stateKey])
|
|
|
|
{
|
|
|
|
Widget *widget = [[Widget alloc] initWithWidgetEvent:widgetEvent inMatrixSession:room.mxSession];
|
|
|
|
if (widget)
|
|
|
|
{
|
|
|
|
widgets[widget.widgetId] = widget;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-08-04 11:05:07 +00:00
|
|
|
|
2017-08-09 14:48:05 +00:00
|
|
|
// Return active widgets only
|
|
|
|
NSMutableArray<Widget *> *activeWidgets = [NSMutableArray array];
|
|
|
|
for (Widget *widget in widgets.allValues)
|
|
|
|
{
|
2017-08-04 11:05:07 +00:00
|
|
|
if (widget.isActive)
|
|
|
|
{
|
2017-08-09 14:48:05 +00:00
|
|
|
[activeWidgets addObject:widget];
|
2017-08-04 11:05:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-09 14:48:05 +00:00
|
|
|
return activeWidgets;
|
2017-08-04 11:05:07 +00:00
|
|
|
}
|
|
|
|
|
2017-08-17 10:17:07 +00:00
|
|
|
- (MXHTTPOperation *)closeWidget:(NSString *)widgetId inRoom:(MXRoom *)room success:(void (^)())success failure:(void (^)(NSError *))failure
|
|
|
|
{
|
|
|
|
NSError *permissionError = [self checkWidgetPermissionInRoom:room];
|
|
|
|
if (permissionError)
|
|
|
|
{
|
|
|
|
if (failure)
|
|
|
|
{
|
|
|
|
failure(permissionError);
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send a state event with an empty content to disable the widget
|
|
|
|
return [room sendStateEventOfType:kWidgetEventTypeString
|
|
|
|
content:@{}
|
|
|
|
stateKey:widgetId
|
|
|
|
success:^(NSString *eventId)
|
|
|
|
{
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
success();
|
|
|
|
}
|
|
|
|
} failure:failure];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Check user's power for widgets management in a room.
|
|
|
|
|
|
|
|
@param room the room to check.
|
|
|
|
@return an NSError if the user cannot act on widgets in this room. Else, nil.
|
|
|
|
*/
|
|
|
|
- (NSError *)checkWidgetPermissionInRoom:(MXRoom *)room
|
|
|
|
{
|
|
|
|
NSError *error;
|
|
|
|
|
|
|
|
// Check user's power in the room
|
|
|
|
MXRoomPowerLevels *powerLevels = room.state.powerLevels;
|
|
|
|
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:room.mxSession.myUser.userId];
|
|
|
|
|
|
|
|
// The user must be able to send state events to manage widgets
|
|
|
|
if (oneSelfPowerLevel < powerLevels.stateDefault)
|
|
|
|
{
|
|
|
|
error = [NSError errorWithDomain:WidgetManagerErrorDomain
|
|
|
|
code:WidgetManagerErrorCodeNotEnoughPower
|
|
|
|
userInfo:@{
|
|
|
|
NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"widget_no_power_to_manage", @"Vector", nil)
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2017-08-04 11:05:07 +00:00
|
|
|
- (void)addMatrixSession:(MXSession *)mxSession
|
|
|
|
{
|
2017-08-10 13:43:25 +00:00
|
|
|
__weak __typeof__(self) weakSelf = self;
|
|
|
|
|
2017-08-04 11:05:07 +00:00
|
|
|
id listener = [mxSession listenToEventsOfTypes:@[kWidgetEventTypeString] onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) {
|
|
|
|
|
2017-08-10 13:43:25 +00:00
|
|
|
typeof(self) self = weakSelf;
|
|
|
|
|
2017-08-16 09:49:01 +00:00
|
|
|
if (self && direction == MXTimelineDirectionForwards)
|
2017-08-04 11:05:07 +00:00
|
|
|
{
|
2017-08-09 15:31:15 +00:00
|
|
|
Widget *widget = [[Widget alloc] initWithWidgetEvent:event inMatrixSession:mxSession];
|
|
|
|
if (widget)
|
|
|
|
{
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKWidgetManagerDidUpdateWidgetNotification object:widget];
|
|
|
|
}
|
2017-08-04 11:05:07 +00:00
|
|
|
}
|
|
|
|
}];
|
|
|
|
|
2017-08-09 16:06:33 +00:00
|
|
|
NSString *hash = [NSString stringWithFormat:@"%p", mxSession];
|
|
|
|
widgetEventListener[hash] = listener;
|
2017-08-04 11:05:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)removeMatrixSession:(MXSession *)mxSession
|
|
|
|
{
|
2017-08-09 16:06:33 +00:00
|
|
|
// mxSession.myUser.userId and mxSession.matrixRestClient.credentials.userId may be nil here
|
|
|
|
// So, use a kind of hash value instead
|
|
|
|
NSString *hash = [NSString stringWithFormat:@"%p", mxSession];
|
|
|
|
id listener = widgetEventListener[hash];
|
2017-08-04 11:05:07 +00:00
|
|
|
|
|
|
|
[mxSession removeListener:listener];
|
|
|
|
|
2017-08-09 16:06:33 +00:00
|
|
|
[widgetEventListener removeObjectForKey:hash];
|
2017-08-04 11:05:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|