From ce6575ce38e083aeb225b7dd3034c3312b029a6f Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 18 Oct 2022 10:09:16 +0200 Subject: [PATCH 1/5] Add private var for avatar menu --- .../Home/AllChats/AllChatsCoordinator.swift | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift b/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift index 932a52333f..9dc5745053 100644 --- a/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift +++ b/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift @@ -331,8 +331,8 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol { private func createLeftButtonItem(for viewController: UIViewController) { createAvatarButtonItem(for: viewController) } - - private func createAvatarButtonItem(for viewController: UIViewController) { + + private var avatarMenu: UIMenu { var actions: [UIMenuElement] = [] actions.append(UIAction(title: VectorL10n.allChatsUserMenuSettings, image: UIImage(systemName: "gearshape")) { [weak self] action in @@ -358,21 +358,23 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol { } ])) - let menu = UIMenu(options: .displayInline, children: actions) - + return UIMenu(options: .displayInline, children: actions) + } + + private func createAvatarButtonItem(for viewController: UIViewController) { let view = UIView(frame: CGRect(x: 0, y: 0, width: 36, height: 36)) view.backgroundColor = .clear - let button: UIButton = UIButton(frame: view.bounds.inset(by: UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7))) + let button: UIButton = UIButton(frame: view.bounds.inset(by: .init(top: 7, left: 7, bottom: 7, right: 7))) button.setImage(Asset.Images.tabPeople.image, for: .normal) - button.menu = menu + button.menu = avatarMenu button.showsMenuAsPrimaryAction = true button.autoresizingMask = [.flexibleHeight, .flexibleWidth] button.accessibilityLabel = VectorL10n.allChatsUserMenuAccessibilityLabel view.addSubview(button) self.avatarMenuButton = button - let avatarView = UserAvatarView(frame: view.bounds.inset(by: UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7))) + let avatarView = UserAvatarView(frame: view.bounds.inset(by: .init(top: 7, left: 7, bottom: 7, right: 7))) avatarView.isUserInteractionEnabled = false avatarView.update(theme: ThemeService.shared().theme) avatarView.autoresizingMask = [.flexibleHeight, .flexibleWidth] From e4314400c65ab51e4b916a38c5643c07be5d4800 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 18 Oct 2022 11:07:50 +0200 Subject: [PATCH 2/5] Cleanup createAvatarButtonItem method --- Riot/Modules/Home/AllChats/AllChatsCoordinator.swift | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift b/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift index 9dc5745053..d9a62af4f9 100644 --- a/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift +++ b/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift @@ -365,7 +365,8 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol { let view = UIView(frame: CGRect(x: 0, y: 0, width: 36, height: 36)) view.backgroundColor = .clear - let button: UIButton = UIButton(frame: view.bounds.inset(by: .init(top: 7, left: 7, bottom: 7, right: 7))) + let avatarInsets: UIEdgeInsets = .init(top: 7, left: 7, bottom: 7, right: 7) + let button: UIButton = .init(frame: view.bounds.inset(by: avatarInsets)) button.setImage(Asset.Images.tabPeople.image, for: .normal) button.menu = avatarMenu button.showsMenuAsPrimaryAction = true @@ -374,18 +375,13 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol { view.addSubview(button) self.avatarMenuButton = button - let avatarView = UserAvatarView(frame: view.bounds.inset(by: .init(top: 7, left: 7, bottom: 7, right: 7))) + let avatarView = UserAvatarView(frame: view.bounds.inset(by: avatarInsets)) avatarView.isUserInteractionEnabled = false avatarView.update(theme: ThemeService.shared().theme) avatarView.autoresizingMask = [.flexibleHeight, .flexibleWidth] view.addSubview(avatarView) self.avatarMenuView = avatarView - - if let avatar = userAvatarViewData(from: currentMatrixSession) { - avatarView.fill(with: avatar) - button.setImage(nil, for: .normal) - } - + updateAvatarButtonItem() viewController.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: view) } From b178b2dcb6e7c0d1e1a8f93062cab4488784031f Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 18 Oct 2022 15:12:04 +0200 Subject: [PATCH 3/5] Add multiple fallbacks in AvatarViewDataProtocol --- Riot/Modules/Common/Avatar/AvatarView.swift | 31 +++++++++---------- .../Common/Avatar/AvatarViewData.swift | 19 ++++++++++-- .../Avatar/AvatarViewDataProtocol.swift | 22 +++++++++++-- .../RoomNotificationSettingsAvatarView.swift | 2 +- .../Views/Avatar/RoomAvatarViewData.swift | 4 +-- .../Room/DirectoryRoomTableViewCellVM.swift | 14 +-------- .../User/Avatar/UserAvatarViewData.swift | 4 +-- 7 files changed, 57 insertions(+), 39 deletions(-) diff --git a/Riot/Modules/Common/Avatar/AvatarView.swift b/Riot/Modules/Common/Avatar/AvatarView.swift index e222d5af94..7cee21d40c 100644 --- a/Riot/Modules/Common/Avatar/AvatarView.swift +++ b/Riot/Modules/Common/Avatar/AvatarView.swift @@ -106,19 +106,9 @@ class AvatarView: UIView, Themable { return } - let defaultAvatarImage: UIImage? - var defaultAvatarImageContentMode: UIView.ContentMode = .scaleAspectFill + let (defaultAvatarImage, defaultAvatarImageContentMode) = viewData.fallbackImageParameters() ?? (nil, .scaleAspectFill) + updateAvatarImageView(image: defaultAvatarImage, contentMode: defaultAvatarImageContentMode) - switch viewData.fallbackImage { - case .matrixItem(let matrixItemId, let matrixItemDisplayName): - defaultAvatarImage = AvatarGenerator.generateAvatar(forMatrixItem: matrixItemId, withDisplayName: matrixItemDisplayName) - case .image(let image, let contentMode): - defaultAvatarImage = image - defaultAvatarImageContentMode = contentMode ?? .scaleAspectFill - case .none: - defaultAvatarImage = nil - } - if let avatarUrl = viewData.avatarUrl { avatarImageView.setImageURI(avatarUrl, withType: nil, @@ -127,12 +117,9 @@ class AvatarView: UIView, Themable { with: MXThumbnailingMethodScale, previewImage: defaultAvatarImage, mediaManager: viewData.mediaManager) - avatarImageView.contentMode = .scaleAspectFill - avatarImageView.imageView?.contentMode = .scaleAspectFill + updateAvatarContentMode(contentMode: .scaleAspectFill) } else { - avatarImageView.image = defaultAvatarImage - avatarImageView.contentMode = defaultAvatarImageContentMode - avatarImageView.imageView?.contentMode = defaultAvatarImageContentMode + updateAvatarImageView(image: defaultAvatarImage, contentMode: defaultAvatarImageContentMode) } } @@ -148,6 +135,16 @@ class AvatarView: UIView, Themable { gestureRecognizer.minimumPressDuration = 0 self.addGestureRecognizer(gestureRecognizer) } + + private func updateAvatarImageView(image: UIImage?, contentMode: UIView.ContentMode) { + avatarImageView?.image = image + updateAvatarContentMode(contentMode: contentMode) + } + + private func updateAvatarContentMode(contentMode: UIView.ContentMode) { + avatarImageView?.contentMode = contentMode + avatarImageView?.imageView.contentMode = contentMode + } // MARK: - Actions diff --git a/Riot/Modules/Common/Avatar/AvatarViewData.swift b/Riot/Modules/Common/Avatar/AvatarViewData.swift index ef5cbb89c8..88eb47a078 100644 --- a/Riot/Modules/Common/Avatar/AvatarViewData.swift +++ b/Riot/Modules/Common/Avatar/AvatarViewData.swift @@ -29,6 +29,21 @@ struct AvatarViewData: AvatarViewDataProtocol { /// Matrix media handler if exists var mediaManager: MXMediaManager? - /// Fallback image used when avatarUrl is nil - var fallbackImage: AvatarFallbackImage? + /// Fallback images used when avatarUrl is nil + var fallbackImages: [AvatarFallbackImage]? +} + +extension AvatarViewData { + init(matrixItemId: String, + displayName: String? = nil, + avatarUrl: String? = nil, + mediaManager: MXMediaManager? = nil, + fallbackImage: AvatarFallbackImage?) { + + self.matrixItemId = matrixItemId + self.displayName = displayName + self.avatarUrl = avatarUrl + self.mediaManager = mediaManager + self.fallbackImages = fallbackImage.map { [$0] } + } } diff --git a/Riot/Modules/Common/Avatar/AvatarViewDataProtocol.swift b/Riot/Modules/Common/Avatar/AvatarViewDataProtocol.swift index 9b677e5813..f3410783bf 100644 --- a/Riot/Modules/Common/Avatar/AvatarViewDataProtocol.swift +++ b/Riot/Modules/Common/Avatar/AvatarViewDataProtocol.swift @@ -41,6 +41,24 @@ protocol AvatarViewDataProtocol: AvatarProtocol { /// Matrix media handler var mediaManager: MXMediaManager? { get } - /// Fallback image used when avatarUrl is nil - var fallbackImage: AvatarFallbackImage? { get } + /// Fallback images used when avatarUrl is nil + var fallbackImages: [AvatarFallbackImage]? { get } +} + +extension AvatarViewDataProtocol { + func fallbackImageParameters() -> (UIImage?, UIView.ContentMode)? { + fallbackImages? + .lazy + .map { fallbackImage in + switch fallbackImage { + case .matrixItem(let matrixItemId, let matrixItemDisplayName): + return (AvatarGenerator.generateAvatar(forMatrixItem: matrixItemId, withDisplayName: matrixItemDisplayName), .scaleAspectFill) + case .image(let image, let contentMode): + return (image, contentMode ?? .scaleAspectFill) + } + } + .first { (image, contentMode) in + image != nil + } + } } diff --git a/Riot/Modules/Room/NotificationSettings/UIKit/RoomNotificationSettingsAvatarView.swift b/Riot/Modules/Room/NotificationSettings/UIKit/RoomNotificationSettingsAvatarView.swift index 744bfa2b64..caab08a869 100644 --- a/Riot/Modules/Room/NotificationSettings/UIKit/RoomNotificationSettingsAvatarView.swift +++ b/Riot/Modules/Room/NotificationSettings/UIKit/RoomNotificationSettingsAvatarView.swift @@ -25,7 +25,7 @@ class RoomNotificationSettingsAvatarView: UIView { func configure(viewData: AvatarViewDataProtocol) { avatarView.fill(with: viewData) - switch viewData.fallbackImage { + switch viewData.fallbackImages?.first { case .matrixItem(_, let matrixItemDisplayName): nameLabel.text = matrixItemDisplayName default: diff --git a/Riot/Modules/Room/Views/Avatar/RoomAvatarViewData.swift b/Riot/Modules/Room/Views/Avatar/RoomAvatarViewData.swift index 1af0a99aa3..7f8df4e18a 100644 --- a/Riot/Modules/Room/Views/Avatar/RoomAvatarViewData.swift +++ b/Riot/Modules/Room/Views/Avatar/RoomAvatarViewData.swift @@ -26,7 +26,7 @@ struct RoomAvatarViewData: AvatarViewDataProtocol { return roomId } - var fallbackImage: AvatarFallbackImage? { - return .matrixItem(matrixItemId, displayName) + var fallbackImages: [AvatarFallbackImage]? { + [.matrixItem(matrixItemId, displayName)] } } diff --git a/Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCellVM.swift b/Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCellVM.swift index 099102014f..27659b9be9 100644 --- a/Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCellVM.swift +++ b/Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCellVM.swift @@ -27,19 +27,7 @@ struct DirectoryRoomTableViewCellVM { // TODO: Use AvatarView subclass in the cell view func setAvatar(in avatarImageView: MXKImageView) { - - let defaultAvatarImage: UIImage? - var defaultAvatarImageContentMode: UIView.ContentMode = .scaleAspectFill - - switch self.avatarViewData.fallbackImage { - case .matrixItem(let matrixItemId, let matrixItemDisplayName): - defaultAvatarImage = AvatarGenerator.generateAvatar(forMatrixItem: matrixItemId, withDisplayName: matrixItemDisplayName) - case .image(let image, let contentMode): - defaultAvatarImage = image - defaultAvatarImageContentMode = contentMode ?? .scaleAspectFill - case .none: - defaultAvatarImage = nil - } + let (defaultAvatarImage, defaultAvatarImageContentMode) = avatarViewData.fallbackImageParameters() ?? (nil, .scaleAspectFill) if let avatarUrl = self.avatarViewData.avatarUrl { avatarImageView.enableInMemoryCache = true diff --git a/Riot/Modules/User/Avatar/UserAvatarViewData.swift b/Riot/Modules/User/Avatar/UserAvatarViewData.swift index 2f9dea0f08..2dad83e98b 100644 --- a/Riot/Modules/User/Avatar/UserAvatarViewData.swift +++ b/Riot/Modules/User/Avatar/UserAvatarViewData.swift @@ -26,7 +26,7 @@ struct UserAvatarViewData: AvatarViewDataProtocol { return userId } - var fallbackImage: AvatarFallbackImage? { - return .matrixItem(matrixItemId, displayName) + var fallbackImages: [AvatarFallbackImage]? { + [.matrixItem(matrixItemId, displayName), .image(Asset.Images.tabPeople.image, .scaleAspectFill)] } } From 05c5a55c79eec42f13eb5a6162e66abd2c860993 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 18 Oct 2022 15:59:51 +0200 Subject: [PATCH 4/5] Add changelog.d file --- changelog.d/6847.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6847.bugfix diff --git a/changelog.d/6847.bugfix b/changelog.d/6847.bugfix new file mode 100644 index 0000000000..3e8dcd7a15 --- /dev/null +++ b/changelog.d/6847.bugfix @@ -0,0 +1 @@ +Updates the avatar image loading logics. From 5043ece53b8ce7be27f332abeaf3930e7a0d4179 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 18 Oct 2022 16:04:15 +0200 Subject: [PATCH 5/5] Remove space --- Riot/Modules/Home/AllChats/AllChatsCoordinator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift b/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift index d9a62af4f9..1070db4e57 100644 --- a/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift +++ b/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift @@ -331,7 +331,7 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol { private func createLeftButtonItem(for viewController: UIViewController) { createAvatarButtonItem(for: viewController) } - + private var avatarMenu: UIMenu { var actions: [UIMenuElement] = []