AvatarManager.swift 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. //
  2. // SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
  3. // SPDX-License-Identifier: GPL-3.0-or-later
  4. //
  5. import UIKit
  6. import SDWebImage
  7. @objcMembers class AvatarManager: NSObject {
  8. public static let shared = AvatarManager()
  9. private let avatarDefaultSize = CGRect(x: 0, y: 0, width: 32, height: 32)
  10. // MARK: - Conversation avatars
  11. public func getAvatar(for room: NCRoom, with style: UIUserInterfaceStyle, completionBlock: @escaping (_ image: UIImage?) -> Void) -> SDWebImageCombinedOperation? {
  12. if room.accountId != nil, NCDatabaseManager.sharedInstance().serverHasTalkCapability(kCapabilityConversationAvatars, forAccountId: room.accountId) {
  13. // Server supports conversation avatars -> try to get the avatar using this API
  14. return NCAPIController.sharedInstance().getAvatarFor(room, with: style) { image, _ in
  15. completionBlock(image)
  16. }
  17. } else {
  18. // Server does not support conversation avatars -> use the legacy way to obtain an avatar
  19. return self.getFallbackAvatar(for: room, with: style, completionBlock: completionBlock)
  20. }
  21. }
  22. public func getGroupAvatar(with style: UIUserInterfaceStyle) -> UIImage? {
  23. let traitCollection = UITraitCollection(userInterfaceStyle: style)
  24. return UIImage(named: "group-avatar", in: nil, compatibleWith: traitCollection)
  25. }
  26. public func getMailAvatar(with style: UIUserInterfaceStyle) -> UIImage? {
  27. let traitCollection = UITraitCollection(userInterfaceStyle: style)
  28. return UIImage(named: "mail-avatar", in: nil, compatibleWith: traitCollection)
  29. }
  30. private func getFallbackAvatar(for room: NCRoom,
  31. with style: UIUserInterfaceStyle,
  32. completionBlock: @escaping (_ image: UIImage?) -> Void) -> SDWebImageCombinedOperation? {
  33. let traitCollection = UITraitCollection(userInterfaceStyle: style)
  34. if room.objectType == NCRoomObjectTypeFile {
  35. completionBlock(UIImage(named: "file-avatar", in: nil, compatibleWith: traitCollection))
  36. } else if room.objectType == NCRoomObjectTypeSharePassword {
  37. completionBlock(UIImage(named: "password-avatar", in: nil, compatibleWith: traitCollection))
  38. } else {
  39. switch room.type {
  40. case .oneToOne:
  41. let account = NCDatabaseManager.sharedInstance().talkAccount(forAccountId: room.accountId)
  42. return self.getUserAvatar(forId: room.name, withStyle: style, usingAccount: account, completionBlock: completionBlock)
  43. case .formerOneToOne:
  44. completionBlock(UIImage(named: "user-avatar", in: nil, compatibleWith: traitCollection))
  45. case .public:
  46. completionBlock(UIImage(named: "public-avatar", in: nil, compatibleWith: traitCollection))
  47. case .group:
  48. completionBlock(UIImage(named: "group-avatar", in: nil, compatibleWith: traitCollection))
  49. case .changelog:
  50. completionBlock(UIImage(named: "changelog-avatar", in: nil, compatibleWith: traitCollection))
  51. default:
  52. completionBlock(nil)
  53. }
  54. }
  55. return nil
  56. }
  57. // MARK: - Actor avatars
  58. // swiftlint:disable:next function_parameter_count
  59. public func getActorAvatar(forId actorId: String?, withType actorType: String?, withDisplayName actorDisplayName: String?, withRoomToken roomToken: String?, withStyle style: UIUserInterfaceStyle, usingAccount account: TalkAccount?, completionBlock: @escaping (_ image: UIImage?) -> Void) -> SDWebImageCombinedOperation? {
  60. if let actorId {
  61. if actorType == "bots" {
  62. return getBotsAvatar(forId: actorId, withStyle: style, completionBlock: completionBlock)
  63. } else if actorType == "guests" {
  64. return getGuestsAvatar(withDisplayName: actorDisplayName ?? "", completionBlock: completionBlock)
  65. } else if actorType == "users" {
  66. return getUserAvatar(forId: actorId, withStyle: style, usingAccount: account, completionBlock: completionBlock)
  67. } else if actorType == "federated_users" {
  68. return getFederatedUserAvatar(forId: actorId, withRoomToken: roomToken, withStyle: style, usingAccount: account, completionBlock: completionBlock)
  69. } else if actorType == "deleted_users" {
  70. return getDeletedUserAvatar(completionBlock: completionBlock)
  71. }
  72. }
  73. var image: UIImage?
  74. if actorType == NCAttendeeTypeEmail {
  75. image = self.getMailAvatar(with: style)
  76. } else if actorType == NCAttendeeTypeGroup || actorType == NCAttendeeTypeCircle {
  77. image = self.getGroupAvatar(with: style)
  78. } else {
  79. image = NCUtils.getImage(withString: "?", withBackgroundColor: .systemGray3, withBounds: self.avatarDefaultSize, isCircular: true)
  80. }
  81. completionBlock(image)
  82. return nil
  83. }
  84. private func getBotsAvatar(forId actorId: String, withStyle style: UIUserInterfaceStyle, completionBlock: @escaping (_ image: UIImage?) -> Void) -> SDWebImageCombinedOperation? {
  85. if actorId == "changelog" {
  86. let traitCollection = UITraitCollection(userInterfaceStyle: style)
  87. completionBlock(UIImage(named: "changelog-avatar", in: nil, compatibleWith: traitCollection))
  88. } else {
  89. let image = NCUtils.getImage(withString: ">", withBackgroundColor: .systemGray3, withBounds: self.avatarDefaultSize, isCircular: true)
  90. completionBlock(image)
  91. }
  92. return nil
  93. }
  94. private func getGuestsAvatar(withDisplayName actorDisplayName: String, completionBlock: @escaping (_ image: UIImage?) -> Void) -> SDWebImageCombinedOperation? {
  95. let name = actorDisplayName.isEmpty ? "?" : actorDisplayName
  96. let image = NCUtils.getImage(withString: name, withBackgroundColor: .systemGray3, withBounds: self.avatarDefaultSize, isCircular: true)
  97. completionBlock(image)
  98. return nil
  99. }
  100. private func getDeletedUserAvatar(completionBlock: @escaping (_ image: UIImage?) -> Void) -> SDWebImageCombinedOperation? {
  101. let image = NCUtils.getImage(withString: "X", withBackgroundColor: .systemGray3, withBounds: self.avatarDefaultSize, isCircular: true)
  102. completionBlock(image)
  103. return nil
  104. }
  105. private func getUserAvatar(forId actorId: String, withStyle style: UIUserInterfaceStyle, usingAccount account: TalkAccount?, completionBlock: @escaping (_ image: UIImage?) -> Void) -> SDWebImageCombinedOperation? {
  106. let account = account ?? NCDatabaseManager.sharedInstance().activeAccount()
  107. return NCAPIController.sharedInstance().getUserAvatar(forUser: actorId, using: account, with: style) { image, _ in
  108. if image != nil {
  109. completionBlock(image)
  110. } else {
  111. NSLog("Unable to get avatar for user %@", actorId)
  112. let traitCollection = UITraitCollection(userInterfaceStyle: style)
  113. completionBlock(UIImage(named: "user-avatar", in: nil, compatibleWith: traitCollection))
  114. }
  115. }
  116. }
  117. private func getFederatedUserAvatar(forId actorId: String, withRoomToken roomToken: String?, withStyle style: UIUserInterfaceStyle, usingAccount account: TalkAccount?, completionBlock: @escaping (_ image: UIImage?) -> Void) -> SDWebImageCombinedOperation? {
  118. let account = account ?? NCDatabaseManager.sharedInstance().activeAccount()
  119. return NCAPIController.sharedInstance().getFederatedUserAvatar(forUser: actorId, inRoom: roomToken, using: account, with: style) { image, _ in
  120. if image != nil {
  121. completionBlock(image)
  122. } else {
  123. NSLog("Unable to get federated avatar for user %@", actorId)
  124. let traitCollection = UITraitCollection(userInterfaceStyle: style)
  125. completionBlock(UIImage(named: "user-avatar", in: nil, compatibleWith: traitCollection))
  126. }
  127. }
  128. }
  129. // MARK: - Utils
  130. public func createRenderedImage(image: UIImage) -> UIImage? {
  131. return self.createRenderedImage(image: image, width: 120, height: 120)
  132. }
  133. private func createRenderedImage(image: UIImage, width: Int, height: Int) -> UIImage? {
  134. UIGraphicsBeginImageContextWithOptions(.init(width: width, height: height), false, 0.0)
  135. image.draw(in: .init(x: 0, y: 0, width: width, height: height))
  136. let newImage = UIGraphicsGetImageFromCurrentImageContext()
  137. UIGraphicsEndImageContext()
  138. return newImage
  139. }
  140. }