From c8af41cd5c0f034e7dba07df06d2bea7f413253e Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Wed, 10 Jun 2020 15:14:16 +1000 Subject: [PATCH] Show real profile picture of open groups --- .../Loki/Components/ConversationCell.swift | 21 ++++--- .../OWSConversationSettingsViewController.m | 10 +++- .../Components/ProfilePictureView.swift | 6 +- .../API/Open Groups/LokiPublicChatAPI.swift | 57 +++++++++++++++++-- .../Database/OWSPrimaryStorage+Loki.swift | 9 +++ 5 files changed, 85 insertions(+), 18 deletions(-) diff --git a/Signal/src/Loki/Components/ConversationCell.swift b/Signal/src/Loki/Components/ConversationCell.swift index d6fdde318..2b75f5850 100644 --- a/Signal/src/Loki/Components/ConversationCell.swift +++ b/Signal/src/Loki/Components/ConversationCell.swift @@ -141,15 +141,20 @@ final class ConversationCell : UITableViewCell { profilePictureView.hexEncodedPublicKey = "" profilePictureView.isRSSFeed = true } else { - var users = MentionsManager.userPublicKeyCache[threadViewModel.threadRecord.uniqueId!] ?? [] - users.remove(getUserHexEncodedPublicKey()) - let randomUsers = users.sorted().prefix(2) // Sort to provide a level of stability - if !randomUsers.isEmpty { - profilePictureView.hexEncodedPublicKey = randomUsers[0] - profilePictureView.additionalHexEncodedPublicKey = randomUsers.count >= 2 ? randomUsers[1] : "" + if let groupAvatarImage = (threadViewModel.threadRecord as? TSGroupThread)?.groupModel.groupImage { + profilePictureView.groupAvatarImage = groupAvatarImage } else { - profilePictureView.hexEncodedPublicKey = "" - profilePictureView.additionalHexEncodedPublicKey = "" + profilePictureView.groupAvatarImage = nil + var users = MentionsManager.userPublicKeyCache[threadViewModel.threadRecord.uniqueId!] ?? [] + users.remove(getUserHexEncodedPublicKey()) + let randomUsers = users.sorted().prefix(2) // Sort to provide a level of stability + if !randomUsers.isEmpty { + profilePictureView.hexEncodedPublicKey = randomUsers[0] + profilePictureView.additionalHexEncodedPublicKey = randomUsers.count >= 2 ? randomUsers[1] : "" + } else { + profilePictureView.hexEncodedPublicKey = "" + profilePictureView.additionalHexEncodedPublicKey = "" + } } profilePictureView.isRSSFeed = (threadViewModel.threadRecord as? TSGroupThread)?.isRSSFeed ?? false } diff --git a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m index 792bf68cf..022225a04 100644 --- a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m +++ b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m @@ -957,8 +957,14 @@ const CGFloat kIconViewLength = 24; [stackView setLayoutMarginsRelativeArrangement:YES]; if (self.isGroupThread) { - profilePictureView.hexEncodedPublicKey = @""; - profilePictureView.isRSSFeed = true; // For now just always show the Session logo + TSGroupThread* groupThread = (TSGroupThread*)self.thread; + if (groupThread.isPublicChat && groupThread.groupModel.groupImage != nil) { + profilePictureView.groupAvatarImage = groupThread.groupModel.groupImage; + profilePictureView.isRSSFeed = false; + } else { + profilePictureView.hexEncodedPublicKey = @""; + profilePictureView.isRSSFeed = true; // For now just always show the Session logo + } } else { profilePictureView.hexEncodedPublicKey = self.thread.contactIdentifier; diff --git a/SignalMessaging/Loki/Redesign/Components/ProfilePictureView.swift b/SignalMessaging/Loki/Redesign/Components/ProfilePictureView.swift index 809fa8f24..6aacb8f21 100644 --- a/SignalMessaging/Loki/Redesign/Components/ProfilePictureView.swift +++ b/SignalMessaging/Loki/Redesign/Components/ProfilePictureView.swift @@ -7,6 +7,7 @@ public final class ProfilePictureView : UIView { @objc public var isRSSFeed = false @objc public var hexEncodedPublicKey: String! @objc public var additionalHexEncodedPublicKey: String? + @objc public var groupAvatarImage: UIImage? // MARK: Components private lazy var imageView = getImageView() @@ -61,8 +62,9 @@ public final class ProfilePictureView : UIView { additionalImageView.isHidden = true additionalImageView.image = nil } - guard hexEncodedPublicKey != nil else { return } // Can happen in rare cases - imageView.image = isRSSFeed ? nil : getProfilePicture(of: size, for: hexEncodedPublicKey) + guard hexEncodedPublicKey != nil || groupAvatarImage != nil else { return } // Can happen in rare cases + + imageView.image = isRSSFeed ? nil : groupAvatarImage != nil ? groupAvatarImage! : getProfilePicture(of: size, for: hexEncodedPublicKey) imageView.backgroundColor = isRSSFeed ? UIColor(rgbHex: 0x353535) : UIColor(rgbHex: 0xD8D8D8) // UIColor(rgbHex: 0xD8D8D8) = Colors.unimportant imageView.layer.cornerRadius = size / 2 imageView.contentMode = isRSSFeed ? .center : .scaleAspectFit diff --git a/SignalServiceKit/src/Loki/API/Open Groups/LokiPublicChatAPI.swift b/SignalServiceKit/src/Loki/API/Open Groups/LokiPublicChatAPI.swift index 37ba968c8..d503f0159 100644 --- a/SignalServiceKit/src/Loki/API/Open Groups/LokiPublicChatAPI.swift +++ b/SignalServiceKit/src/Loki/API/Open Groups/LokiPublicChatAPI.swift @@ -391,18 +391,63 @@ public final class LokiPublicChatAPI : LokiDotNetAPI { let annotation = annotations.first, let info = annotation["value"] as? JSON, let displayName = info["name"] as? String, + let profilePictureURL = info["avatar"] as? String, let countInfo = data["counts"] as? JSON, let memberCount = countInfo["subscribers"] as? Int else { print("[Loki] Couldn't parse info for public chat channel with ID: \(channel) on server: \(server) from: \(rawResponse).") throw LokiDotNetAPIError.parsingFailed } - let profilePictureURL = info["avatar"] as? String - let storage = OWSPrimaryStorage.shared() - storage.dbReadWriteConnection.readWrite { transaction in - storage.setUserCount(memberCount, forPublicChatWithID: "\(server).\(channel)", in: transaction) + let publicChatInfo = LokiPublicChatInfo(displayName: displayName, profilePictureURL: profilePictureURL, memberCount: memberCount) + updateOpenGroupProfileIfNeeded(for: channel, on: server, with: publicChatInfo) + return publicChatInfo + } + } + + static func updateOpenGroupProfileIfNeeded(for channel: UInt64, on server: String, with info: LokiPublicChatInfo) { + let storage = OWSPrimaryStorage.shared() + let publicChatID = "\(server).\(channel)" + storage.dbReadWriteConnection.readWrite { transaction in + //Save user count + storage.setUserCount(info.memberCount, forPublicChatWithID: publicChatID, in: transaction) + + let groupThread = TSGroupThread.getOrCreateThread(withGroupId: publicChatID.data(using: .utf8)!, groupType: .openGroup, transaction: transaction) + + //Update display name if needed + let groupModel = groupThread.groupModel + if groupModel.groupName != info.displayName { + let newGroupModel = TSGroupModel.init(title: info.displayName, memberIds: groupModel.groupMemberIds, image: groupModel.groupImage, groupId: groupModel.groupId, groupType: groupModel.groupType, adminIds: groupModel.groupAdminIds) + groupThread.groupModel = newGroupModel + groupThread.save(with: transaction) + } + + //Download and update profile picture if needed + let oldAvatarURL = storage.getAvatarURL(forPublicChatWithID: publicChatID, in: transaction) + if oldAvatarURL != info.profilePictureURL || groupModel.groupImage == nil { + storage.setAvatarURL(info.profilePictureURL, forPublicChatWithID: publicChatID, in: transaction) + if let avatarURL = info.profilePictureURL { + let configuration = URLSessionConfiguration.default + let manager = AFURLSessionManager.init(sessionConfiguration: configuration) + let url = URL(string: "\(server)\(avatarURL)")! + let request = URLRequest(url: url) + let task = manager.downloadTask(with: request, progress: nil, + destination: {(targetPath: URL, response: URLResponse) -> URL in + let tempFilePath = URL(fileURLWithPath: OWSTemporaryDirectoryAccessibleAfterFirstAuth()).appendingPathComponent(UUID().uuidString) + return tempFilePath + }, + completionHandler: { (response: URLResponse, filePath: URL?, error: Error?) in + if let error = error { + print("[Loki] Couldn't download profile picture for public chat channel with ID: \(channel) on server: \(server)") + return + } + if let path = filePath, let avatarData = try? Data.init(contentsOf: path) { + let attachmentStream = TSAttachmentStream.init(contentType: OWSMimeTypeImageJpeg, byteCount: UInt32(avatarData.count), sourceFilename: nil, caption: nil, albumMessageId: nil) + try! attachmentStream.write(avatarData) + groupThread.updateAvatar(with: attachmentStream) + } + }) + task.resume() + } } - // TODO: Use this to update open group names as needed - return LokiPublicChatInfo(displayName: displayName, profilePictureURL: profilePictureURL, memberCount: memberCount) } } diff --git a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift index c69a996a5..e3ac306b3 100644 --- a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift +++ b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift @@ -151,6 +151,7 @@ public extension OWSPrimaryStorage { // MARK: - Open Groups private static let openGroupUserCountCollection = "LokiPublicChatUserCountCollection" + private static let openGroupAvatarURLCollection = "LokiPublicChatAvatarURLCollection" public func getUserCount(for publicChat: LokiPublicChat, in transaction: YapDatabaseReadTransaction) -> Int? { return transaction.object(forKey: publicChat.id, inCollection: OWSPrimaryStorage.openGroupUserCountCollection) as? Int @@ -159,4 +160,12 @@ public extension OWSPrimaryStorage { public func setUserCount(_ userCount: Int, forPublicChatWithID publicChatID: String, in transaction: YapDatabaseReadWriteTransaction) { transaction.setObject(userCount, forKey: publicChatID, inCollection: OWSPrimaryStorage.openGroupUserCountCollection) } + + public func getAvatarURL(forPublicChatWithID publicChatID: String, in transaction: YapDatabaseReadTransaction) -> String? { + return transaction.object(forKey: publicChatID, inCollection: OWSPrimaryStorage.openGroupAvatarURLCollection) as? String + } + + public func setAvatarURL(_ avatarURL: String?, forPublicChatWithID publicChatID: String, in transaction: YapDatabaseReadWriteTransaction) { + transaction.setObject(avatarURL, forKey: publicChatID, inCollection: OWSPrimaryStorage.openGroupAvatarURLCollection) + } }