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 var pollForNewMessagesTimer: Timer? = nil
private var pollForDeletedMessagesTimer: Timer? = nil
private var pollForModerationPermissionTimer: Timer? = nil
private var pollForModeratorsTimer: Timer? = nil
private var hasStarted = false
private let userHexEncodedPublicKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey
// MARK: Settings
private let pollForNewMessagesInterval: TimeInterval = 4
private let pollForDeletedMessagesInterval: TimeInterval = 20
private let pollForModerationPermissionInterval: TimeInterval = 10 * 60
private let pollForModeratorsInterval: TimeInterval = 10 * 60
// MARK: Lifecycle
@objc(initForGroup:)
@ -24,18 +24,18 @@ public final class LokiGroupChatPoller : NSObject {
if hasStarted { return }
pollForNewMessagesTimer = Timer.scheduledTimer(withTimeInterval: pollForNewMessagesInterval, repeats: true) { [weak self] _ in self?.pollForNewMessages() }
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
pollForNewMessages()
pollForDeletedMessages()
pollForModerationPermission()
pollForModerators()
hasStarted = true
}
@objc public func stop() {
pollForNewMessagesTimer?.invalidate()
pollForDeletedMessagesTimer?.invalidate()
pollForModerationPermissionTimer?.invalidate()
pollForModeratorsTimer?.invalidate()
hasStarted = false
}
@ -140,13 +140,7 @@ public final class LokiGroupChatPoller : NSObject {
}
}
private func pollForModerationPermission() {
let group = self.group
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)
}
}
private func pollForModerators() {
let _ = LokiGroupChatAPI.getModerators(for: group.serverID, on: group.server)
}
}

@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) OWSMessageBubbleView *messageBubbleView;
@property (nonatomic) NSLayoutConstraint *messageBubbleViewBottomConstraint;
@property (nonatomic) AvatarImageView *avatarView;
@property (nonatomic) UIImageView *moderatorIconView;
@property (nonatomic, nullable) LKFriendRequestView *friendRequestView;
@property (nonatomic, nullable) UIImageView *sendFailureBadgeView;
@ -61,6 +62,11 @@ NS_ASSUME_NONNULL_BEGIN
[self.avatarView autoSetDimension:ALDimensionWidth 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.contentView.userInteractionEnabled = YES;
@ -227,6 +233,11 @@ NS_ASSUME_NONNULL_BEGIN
[self.messageBubbleView autoPinLeadingToTrailingEdgeOfView:self.avatarView offset:8],
[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];
self.avatarView.image = authorAvatarImage;
[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
selector:@selector(otherUsersProfileDidChange:)
@ -385,6 +405,9 @@ NS_ASSUME_NONNULL_BEGIN
self.avatarView.image = nil;
[self.avatarView removeFromSuperview];
self.moderatorIconView.image = nil;
[self.moderatorIconView removeFromSuperview];
[self.sendFailureBadgeView removeFromSuperview];
self.sendFailureBadgeView = nil;

@ -1236,6 +1236,10 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
return NO;
}
- (NSString *)userHexEncodedPublicKey {
return OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey;
}
- (BOOL)userCanDeleteGroupMessage
{
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
if (interationType == OWSInteractionType_IncomingMessage) {
__block BOOL isModerator;
[[self primaryStorage].dbReadWriteConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
isModerator = [[self primaryStorage] isModeratorForGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer in:transaction];
}];
BOOL isModerator = [LKGroupChatAPI isUserModerator:self.userHexEncodedPublicKey forGroup:LKGroupChatAPI.publicChatServerID onServer: LKGroupChatAPI.publicChatServer];
if (!isModerator) return false;
}

@ -4,6 +4,8 @@ import PromiseKit
public final class LokiGroupChatAPI : NSObject {
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
private static let fallbackBatchCount = 40
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> {
return getAuthToken(for: server).then { token -> Promise<Bool> in
let url = URL(string: "\(server)/loki/v1/user_info")!
let request = TSRequest(url: url)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ]
return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in
guard let json = rawResponse as? JSON, let data = json["data"] as? JSON else {
print("[Loki] Couldn't parse moderation permission for group chat with ID: \(group) on server: \(server) from: \(rawResponse).")
throw Error.parsingFailed
}
return data["moderator_status"] as? Bool ?? false
public static func getModerators(for group: UInt64, on server: String) -> Promise<Set<String>> {
let url = URL(string: "\(server)/loki/v1/channel/\(group)/get_moderators")!
let request = TSRequest(url: url)
return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in
guard let json = rawResponse as? JSON, let moderators = json["moderators"] as? [String] else {
print("[Loki] Couldn't parse moderators for group chat with ID: \(group) on server: \(server) from: \(rawResponse).")
throw Error.parsingFailed
}
let moderatorAsSet = Set(moderators);
if self.moderators.keys.contains(server) {
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)
@objc(getMessagesForGroup:onServer:)
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;
- (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

@ -176,14 +176,4 @@
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

Loading…
Cancel
Save