Merge pull request #50 from loki-project/moderator

Moderator Tags
pull/51/head
gmbnt 6 years ago committed by GitHub
commit 57891b1f2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "crown.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

@ -4,14 +4,14 @@ public final class LokiGroupChatPoller : NSObject {
private let group: LokiGroupChat private let group: LokiGroupChat
private var pollForNewMessagesTimer: Timer? = nil private var pollForNewMessagesTimer: Timer? = nil
private var pollForDeletedMessagesTimer: Timer? = nil private var pollForDeletedMessagesTimer: Timer? = nil
private var pollForModerationPermissionTimer: Timer? = nil private var pollForModeratorsTimer: Timer? = nil
private var hasStarted = false private var hasStarted = false
private let userHexEncodedPublicKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey private let userHexEncodedPublicKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey
// MARK: Settings // MARK: Settings
private let pollForNewMessagesInterval: TimeInterval = 4 private let pollForNewMessagesInterval: TimeInterval = 4
private let pollForDeletedMessagesInterval: TimeInterval = 20 private let pollForDeletedMessagesInterval: TimeInterval = 20
private let pollForModerationPermissionInterval: TimeInterval = 10 * 60 private let pollForModeratorsInterval: TimeInterval = 10 * 60
// MARK: Lifecycle // MARK: Lifecycle
@objc(initForGroup:) @objc(initForGroup:)
@ -24,18 +24,18 @@ public final class LokiGroupChatPoller : NSObject {
if hasStarted { return } if hasStarted { return }
pollForNewMessagesTimer = Timer.scheduledTimer(withTimeInterval: pollForNewMessagesInterval, repeats: true) { [weak self] _ in self?.pollForNewMessages() } pollForNewMessagesTimer = Timer.scheduledTimer(withTimeInterval: pollForNewMessagesInterval, repeats: true) { [weak self] _ in self?.pollForNewMessages() }
pollForDeletedMessagesTimer = Timer.scheduledTimer(withTimeInterval: pollForDeletedMessagesInterval, repeats: true) { [weak self] _ in self?.pollForDeletedMessages() } pollForDeletedMessagesTimer = Timer.scheduledTimer(withTimeInterval: pollForDeletedMessagesInterval, repeats: true) { [weak self] _ in self?.pollForDeletedMessages() }
pollForModerationPermissionTimer = Timer.scheduledTimer(withTimeInterval: pollForModerationPermissionInterval, repeats: true) { [weak self] _ in self?.pollForModerationPermission() } pollForModeratorsTimer = Timer.scheduledTimer(withTimeInterval: pollForModeratorsInterval, repeats: true) { [weak self] _ in self?.pollForModerators() }
// Perform initial updates // Perform initial updates
pollForNewMessages() pollForNewMessages()
pollForDeletedMessages() pollForDeletedMessages()
pollForModerationPermission() pollForModerators()
hasStarted = true hasStarted = true
} }
@objc public func stop() { @objc public func stop() {
pollForNewMessagesTimer?.invalidate() pollForNewMessagesTimer?.invalidate()
pollForDeletedMessagesTimer?.invalidate() pollForDeletedMessagesTimer?.invalidate()
pollForModerationPermissionTimer?.invalidate() pollForModeratorsTimer?.invalidate()
hasStarted = false hasStarted = false
} }
@ -140,13 +140,7 @@ public final class LokiGroupChatPoller : NSObject {
} }
} }
private func pollForModerationPermission() { private func pollForModerators() {
let group = self.group let _ = LokiGroupChatAPI.getModerators(for: group.serverID, on: group.server)
let _ = LokiGroupChatAPI.userHasModerationPermission(for: group.serverID, on: group.server).done { isModerator in
let storage = OWSPrimaryStorage.shared()
storage.dbReadWriteConnection.readWrite { transaction in
storage.setIsModerator(isModerator, for: UInt(group.serverID), on: group.server, in: transaction)
}
}
} }
} }

@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) OWSMessageBubbleView *messageBubbleView; @property (nonatomic) OWSMessageBubbleView *messageBubbleView;
@property (nonatomic) NSLayoutConstraint *messageBubbleViewBottomConstraint; @property (nonatomic) NSLayoutConstraint *messageBubbleViewBottomConstraint;
@property (nonatomic) AvatarImageView *avatarView; @property (nonatomic) AvatarImageView *avatarView;
@property (nonatomic) UIImageView *moderatorIconView;
@property (nonatomic, nullable) LKFriendRequestView *friendRequestView; @property (nonatomic, nullable) LKFriendRequestView *friendRequestView;
@property (nonatomic, nullable) UIImageView *sendFailureBadgeView; @property (nonatomic, nullable) UIImageView *sendFailureBadgeView;
@ -61,6 +62,11 @@ NS_ASSUME_NONNULL_BEGIN
[self.avatarView autoSetDimension:ALDimensionWidth toSize:self.avatarSize]; [self.avatarView autoSetDimension:ALDimensionWidth toSize:self.avatarSize];
[self.avatarView autoSetDimension:ALDimensionHeight toSize:self.avatarSize]; [self.avatarView autoSetDimension:ALDimensionHeight toSize:self.avatarSize];
self.moderatorIconView = [[UIImageView alloc] init];
[self.moderatorIconView autoSetDimension:ALDimensionWidth toSize:20.f];
[self.moderatorIconView autoSetDimension:ALDimensionHeight toSize:20.f];
self.moderatorIconView.hidden = YES;
self.messageBubbleViewBottomConstraint = [self.messageBubbleView autoPinBottomToSuperviewMarginWithInset:0]; self.messageBubbleViewBottomConstraint = [self.messageBubbleView autoPinBottomToSuperviewMarginWithInset:0];
self.contentView.userInteractionEnabled = YES; self.contentView.userInteractionEnabled = YES;
@ -227,6 +233,11 @@ NS_ASSUME_NONNULL_BEGIN
[self.messageBubbleView autoPinLeadingToTrailingEdgeOfView:self.avatarView offset:8], [self.messageBubbleView autoPinLeadingToTrailingEdgeOfView:self.avatarView offset:8],
[self.messageBubbleView autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:self.avatarView], [self.messageBubbleView autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:self.avatarView],
]]; ]];
[self.viewConstraints addObjectsFromArray:@[
[self.moderatorIconView autoPinEdge:ALEdgeTrailing toEdge:ALEdgeTrailing ofView:self.avatarView],
[self.moderatorIconView autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:self.avatarView withOffset:3.5]
]];
} }
} }
@ -285,6 +296,15 @@ NS_ASSUME_NONNULL_BEGIN
diameter:self.avatarSize] build]; diameter:self.avatarSize] build];
self.avatarView.image = authorAvatarImage; self.avatarView.image = authorAvatarImage;
[self.contentView addSubview:self.avatarView]; [self.contentView addSubview:self.avatarView];
if (self.viewItem.isGroupThread && !self.viewItem.isRSSFeed) {
BOOL isModerator = [LKGroupChatAPI isUserModerator:incomingMessage.authorId forGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer];
UIImage *moderatorIcon = [UIImage imageNamed:@"Crown"];
self.moderatorIconView.image = moderatorIcon;
self.moderatorIconView.hidden = !isModerator;
}
[self.contentView addSubview:self.moderatorIconView];
[[NSNotificationCenter defaultCenter] addObserver:self [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(otherUsersProfileDidChange:) selector:@selector(otherUsersProfileDidChange:)
@ -385,6 +405,9 @@ NS_ASSUME_NONNULL_BEGIN
self.avatarView.image = nil; self.avatarView.image = nil;
[self.avatarView removeFromSuperview]; [self.avatarView removeFromSuperview];
self.moderatorIconView.image = nil;
[self.moderatorIconView removeFromSuperview];
[self.sendFailureBadgeView removeFromSuperview]; [self.sendFailureBadgeView removeFromSuperview];
self.sendFailureBadgeView = nil; self.sendFailureBadgeView = nil;

@ -1236,6 +1236,10 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
return NO; return NO;
} }
- (NSString *)userHexEncodedPublicKey {
return OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey;
}
- (BOOL)userCanDeleteGroupMessage - (BOOL)userCanDeleteGroupMessage
{ {
if (!self.isGroupThread) return false; if (!self.isGroupThread) return false;
@ -1254,10 +1258,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
// Only allow deletion on incoming messages if the user has moderation permission // Only allow deletion on incoming messages if the user has moderation permission
if (interationType == OWSInteractionType_IncomingMessage) { if (interationType == OWSInteractionType_IncomingMessage) {
__block BOOL isModerator; BOOL isModerator = [LKGroupChatAPI isUserModerator:self.userHexEncodedPublicKey forGroup:LKGroupChatAPI.publicChatServerID onServer: LKGroupChatAPI.publicChatServer];
[[self primaryStorage].dbReadWriteConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
isModerator = [[self primaryStorage] isModeratorForGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer in:transaction];
}];
if (!isModerator) return false; if (!isModerator) return false;
} }

@ -4,6 +4,8 @@ import PromiseKit
public final class LokiGroupChatAPI : NSObject { public final class LokiGroupChatAPI : NSObject {
private static let storage = OWSPrimaryStorage.shared() private static let storage = OWSPrimaryStorage.shared()
private static var moderators: [String:[UInt64:Set<String>]] = [:] // Server URL to (channel ID to set of moderator IDs)
// MARK: Settings // MARK: Settings
private static let fallbackBatchCount = 40 private static let fallbackBatchCount = 40
private static let maxRetryCount: UInt = 4 private static let maxRetryCount: UInt = 4
@ -236,21 +238,29 @@ public final class LokiGroupChatAPI : NSObject {
} }
} }
public static func userHasModerationPermission(for group: UInt64, on server: String) -> Promise<Bool> { public static func getModerators(for group: UInt64, on server: String) -> Promise<Set<String>> {
return getAuthToken(for: server).then { token -> Promise<Bool> in let url = URL(string: "\(server)/loki/v1/channel/\(group)/get_moderators")!
let url = URL(string: "\(server)/loki/v1/user_info")! let request = TSRequest(url: url)
let request = TSRequest(url: url) return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ] guard let json = rawResponse as? JSON, let moderators = json["moderators"] as? [String] else {
return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in print("[Loki] Couldn't parse moderators for group chat with ID: \(group) on server: \(server) from: \(rawResponse).")
guard let json = rawResponse as? JSON, let data = json["data"] as? JSON else { throw Error.parsingFailed
print("[Loki] Couldn't parse moderation permission for group chat with ID: \(group) on server: \(server) from: \(rawResponse).") }
throw Error.parsingFailed let moderatorAsSet = Set(moderators);
} if self.moderators.keys.contains(server) {
return data["moderator_status"] as? Bool ?? false self.moderators[server]![group] = moderatorAsSet
} else {
self.moderators[server] = [ group : moderatorAsSet ]
} }
return moderatorAsSet
} }
} }
@objc (isUserModerator:forGroup:onServer:)
public static func isUserModerator(_ hexEncodedPublicString: String, for group: UInt64, on server: String) -> Bool {
return moderators[server]?[group]?.contains(hexEncodedPublicString) ?? false
}
// MARK: Public API (Obj-C) // MARK: Public API (Obj-C)
@objc(getMessagesForGroup:onServer:) @objc(getMessagesForGroup:onServer:)
public static func objc_getMessages(for group: UInt64, on server: String) -> AnyPromise { public static func objc_getMessages(for group: UInt64, on server: String) -> AnyPromise {

@ -99,8 +99,6 @@ NS_ASSUME_NONNULL_BEGIN
- (void)setIDForMessageWithServerID:(NSUInteger)serverID to:(NSString *)messageID in:(YapDatabaseReadWriteTransaction *)transaction; - (void)setIDForMessageWithServerID:(NSUInteger)serverID to:(NSString *)messageID in:(YapDatabaseReadWriteTransaction *)transaction;
- (NSString *_Nullable)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction; - (NSString *_Nullable)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction;
- (void)setIsModerator:(BOOL)isModerator forGroup:(NSUInteger)group onServer:(NSString *)server in:(YapDatabaseReadWriteTransaction *)transaction NS_SWIFT_NAME(setIsModerator(_:for:on:in:));
- (BOOL)isModeratorForGroup:(NSUInteger)group onServer:(NSString *)server in:(YapDatabaseReadTransaction *)transaction NS_SWIFT_NAME(isModerator(for:on:in:));
@end @end

@ -176,14 +176,4 @@
return [transaction objectForKey:key inCollection:LKMessageIDCollection]; return [transaction objectForKey:key inCollection:LKMessageIDCollection];
} }
- (void)setIsModerator:(BOOL)isModerator forGroup:(NSUInteger)group onServer:(NSString *)server in:(YapDatabaseReadWriteTransaction *)transaction {
NSString *key = [NSString stringWithFormat:@"%@.%@", server, @(group)];
[transaction setBool:isModerator forKey:key inCollection:LKModerationPermissionCollection];
}
- (BOOL)isModeratorForGroup:(NSUInteger)group onServer:(NSString *)server in:(YapDatabaseReadTransaction *)transaction NS_SWIFT_NAME(isModerator(for:on:in:)) {
NSString *key = [NSString stringWithFormat:@"%@.%@", server, @(group)];
return [transaction boolForKey:key inCollection:LKModerationPermissionCollection defaultValue:false];
}
@end @end

Loading…
Cancel
Save