From af39b35da712fd24c20acf9640705700bd6e6bfc Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 12 Sep 2019 15:06:20 +1000 Subject: [PATCH] Clean & implement moderator tags UI --- .../Crown.imageset/Contents.json | 12 +++++ .../Images.xcassets/Crown.imageset/crown.pdf | Bin 0 -> 1802 bytes Signal/src/Loki/LokiGroupChatPoller.swift | 2 +- .../ConversationView/Cells/OWSMessageCell.m | 23 ++++++++ .../ConversationView/ConversationViewItem.m | 5 +- .../src/Loki/API/LokiGroupChatAPI.swift | 51 +++++------------- 6 files changed, 51 insertions(+), 42 deletions(-) create mode 100644 Signal/Images.xcassets/Crown.imageset/Contents.json create mode 100644 Signal/Images.xcassets/Crown.imageset/crown.pdf diff --git a/Signal/Images.xcassets/Crown.imageset/Contents.json b/Signal/Images.xcassets/Crown.imageset/Contents.json new file mode 100644 index 000000000..97010b1ad --- /dev/null +++ b/Signal/Images.xcassets/Crown.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "crown.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/Crown.imageset/crown.pdf b/Signal/Images.xcassets/Crown.imageset/crown.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5e5c424ca7b0782bf0bb5c82a1d06ed4690c386c GIT binary patch literal 1802 zcmY!laB(;DX8Cu`1g=e5W#2a!&tua<6o0wxL74p`Wi0|9#Bhq`x-cX!DUP-k<*0eXsu~ z`}g~QndNdGHF~yRqW-qO7kPEizIOFOv0H7^oV&QPFGxr6Trp1I+o}Fse}49Y76!rm zpzoIL^J-SKKa6plf^tcl`NhgaJ6~d zcTMd{-(!Q2fP%E2FPE}SFz2}x`cffA%O+v|^}@javmIA05}UZ_Qu396!fPo(9hEZE z1>2N@(k?i$?x;)=vgo(o%ETR)uypFw+6E4Ghdw3N%+$w||88t}&(ZqF_0BnW<*C(- zD;DXl{1MEMl{xKBOqBEEBT2hvC&usAanliIPcpi<^FgVPx}l$DN5+cGM7@Py&0bx~ zV$WL$MpQEV4#UK!#WwQj^2+77EeU7T>6TeTw+)B zR^9B_cum?dmq}i@`ltN-_8+%r{L)(D+QdTgvu{=biP{qVw%SG7t1nk&s^3ze^XxitT^?)yv+^m1w(Y8=H3e%cXlad>FSmwWozBr?>L)epOX0T(5L{)s%3-X*tzj z+T^Xu%vajGJye{mxXJ(0&u0M)H3lC%t~ejcdbjJqD-j>3*Did=c3ms2k?o$gV4`KS zU$er5S#4X>--@&@`!ikDU9Ry=ZSw8|vXcuV6sO!x=wqdRVt-a%kA>Wecz0T=H`7h0g4@%B9Bn%AXair+j_#f17-@ z|E%rrFDL%H-DvGDzi(Hd{C8Up-8W{>Uw7~M_Uzs? zJ3A^ED(ELC0!d&o0F(fdKm;|%2yTvoen@3Os)Bw%VtT5As)8Zd z+Fe^ex3^` zYBXG|j0_Ad4J-|f3`|VTj7+06k*qUEvJT>2=fsl4ocwgKL7-SEhQfiZ+8W?+J$&cMJBnEKGw837|6P0ZL5 z=z26UQv(ZxSV>W0W=?7mxW)?3tV#ts859pe`S~RZAdf>5i)UV1z5*!N!SPsJl2}v% R_MxGvnE{uos;j>n7XbR6sZ9U? literal 0 HcmV?d00001 diff --git a/Signal/src/Loki/LokiGroupChatPoller.swift b/Signal/src/Loki/LokiGroupChatPoller.swift index 4a0b5108f..78fb92019 100644 --- a/Signal/src/Loki/LokiGroupChatPoller.swift +++ b/Signal/src/Loki/LokiGroupChatPoller.swift @@ -141,6 +141,6 @@ public final class LokiGroupChatPoller : NSObject { } private func pollForModerators() { - let _ = LokiGroupChatAPI.getModerators(for: self.group.serverID, on: self.group.server) + let _ = LokiGroupChatAPI.getModerators(for: group.serverID, on: group.server) } } diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index b71d39867..1203f9216 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -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; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 319bde986..650ac105b 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -1236,7 +1236,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) return NO; } -- (NSString *)ourHexEncodedPublicKey { +- (NSString *)userHexEncodedPublicKey { return OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey; } @@ -1258,8 +1258,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) // Only allow deletion on incoming messages if the user has moderation permission if (interationType == OWSInteractionType_IncomingMessage) { - BOOL isModerator = [LKGroupChatAPI isUserModerator:self.ourHexEncodedPublicKey forGroup:LKGroupChatAPI.publicChatServerID onServer: LKGroupChatAPI.publicChatServer]; - + BOOL isModerator = [LKGroupChatAPI isUserModerator:self.userHexEncodedPublicKey forGroup:LKGroupChatAPI.publicChatServerID onServer: LKGroupChatAPI.publicChatServer]; if (!isModerator) return false; } diff --git a/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift b/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift index be01d346f..94abf6588 100644 --- a/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift @@ -4,23 +4,17 @@ import PromiseKit public final class LokiGroupChatAPI : NSObject { private static let storage = OWSPrimaryStorage.shared() + private static var moderators: [String:[UInt64:Set]] = [:] // Server URL to (channel ID to set of moderator IDs) + // MARK: Settings private static let fallbackBatchCount = 40 private static let maxRetryCount: UInt = 4 // MARK: Public Chat - @objc public static let publicChatServer = "https://chat.lokinet.org" + @objc public static let publicChatServer = "https://chat-dev.lokinet.org" @objc public static let publicChatMessageType = "network.loki.messenger.publicChat" @objc public static let publicChatServerID: UInt64 = 1 - // Mark: Moderators - typealias ModeratorArray = Set - typealias ChannelDictionary = [UInt64 : ModeratorArray] - typealias ServerMapping = [String : ChannelDictionary] - - // A mapping from server to channel to moderator - private static var moderators = ServerMapping() - // MARK: Convenience private static var userDisplayName: String { return SSKEnvironment.shared.contactsManager.displayName(forPhoneIdentifier: userHexEncodedPublicKey) ?? "Anonymous" @@ -244,26 +238,6 @@ public final class LokiGroupChatAPI : NSObject { } } - public static func userHasModerationPermission(for group: UInt64, on server: String) -> Promise { - return getAuthToken(for: server).then { token -> Promise 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 - } - } - } - - @objc (isUserModerator:forGroup:onServer:) - public static func isUserModerator(user hexEncodedPublicString: String, for group: UInt64, on server: String) -> Bool { - return self.moderators[server]?[group]?.contains(hexEncodedPublicString) ?? false - } - public static func getModerators(for group: UInt64, on server: String) -> Promise> { let url = URL(string: "\(server)/loki/v1/channel/\(group)/get_moderators")! let request = TSRequest(url: url) @@ -272,20 +246,21 @@ public final class LokiGroupChatAPI : NSObject { print("[Loki] Couldn't parse moderators for group chat with ID: \(group) on server: \(server) from: \(rawResponse).") throw Error.parsingFailed } - - let moderatorSet = Set(moderators); - - // Update our cache - if (self.moderators.keys.contains(server)) { - self.moderators[server]![group] = moderatorSet + let moderatorAsSet = Set(moderators); + if self.moderators.keys.contains(server) { + self.moderators[server]![group] = moderatorAsSet } else { - self.moderators[server] = [group : moderatorSet] + self.moderators[server] = [ group : moderatorAsSet ] } - - return moderatorSet + 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 {