session-ios/SignalUtilitiesKit/Profile Pictures/ProfilePictureView.swift

204 lines
8.0 KiB
Swift
Raw Normal View History

// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import GRDB
Fixed a number of reported bugs, some cleanup, added animated profile support Added support for animated profile images (no ability to crop/resize) Updated the message trimming to only remove messages if the open group has 2000 messages or more Updated the message trimming setting to default to be on Updated the ContextMenu to fade out the snapshot as well (looked buggy if the device had even minor lag) Updated the ProfileManager to delete and re-download invalid avatar images (and updated the conversation screen to reload when avatars complete downloading) Updated the message request notification logic so it will show notifications when receiving a new message request as long as the user has read all the old ones (previously the user had to accept/reject all the old ones) Fixed a bug where the "trim open group messages" toggle was accessing UI off the main thread Fixed a bug where the "Chats" settings screen had a close button instead of a back button Fixed a bug where the 'viewsToMove' for the reply UI was inconsistent in some places Fixed an issue where the ProfileManager was doing all of it's validation (and writing to disk) within the database write closure which would block database writes unnecessarily Fixed a bug where a message request wouldn't be identified as such just because it wasn't visible in the conversations list Fixed a bug where opening a message request notification would result in the message request being in the wrong state (also wouldn't insert the 'MessageRequestsViewController' into the hierarchy) Fixed a bug where the avatar image wouldn't appear beside incoming closed group message in some situations cases Removed an error log that was essentially just spam Remove the logic to delete old profile images when calling save on a Profile (wouldn't get called if the row was modified directly and duplicates GarbageCollection logic) Remove the logic to send a notification when calling save on a Profile (wouldn't get called if the row was modified directly) Tweaked the message trimming description to be more accurate Cleaned up some duplicate logic used to determine if a notification should be shown Cleaned up some onion request logic (was passing the version info in some cases when not needed) Moved the push notification notify API call into the PushNotificationAPI class for consistency
2022-07-08 09:53:48 +02:00
import YYImage
2020-11-09 06:03:59 +01:00
import SessionUIKit
import SessionMessagingKit
2019-11-28 06:42:07 +01:00
2019-12-10 04:27:38 +01:00
@objc(LKProfilePictureView)
public final class ProfilePictureView: UIView {
2021-02-26 04:42:46 +01:00
private var hasTappableProfilePicture: Bool = false
@objc public var size: CGFloat = 0 // Not an implicitly unwrapped optional due to Obj-C limitations
2021-02-26 04:42:46 +01:00
// Constraints
2019-11-28 06:42:07 +01:00
private var imageViewWidthConstraint: NSLayoutConstraint!
private var imageViewHeightConstraint: NSLayoutConstraint!
2020-09-14 03:31:45 +02:00
private var additionalImageViewWidthConstraint: NSLayoutConstraint!
private var additionalImageViewHeightConstraint: NSLayoutConstraint!
2019-11-28 06:42:07 +01:00
// MARK: - Components
2019-11-28 06:42:07 +01:00
private lazy var imageView = getImageView()
private lazy var additionalImageView = getImageView()
// MARK: - Lifecycle
2020-01-20 03:20:27 +01:00
public override init(frame: CGRect) {
2019-11-28 06:42:07 +01:00
super.init(frame: frame)
2019-12-02 01:58:15 +01:00
setUpViewHierarchy()
2019-11-28 06:42:07 +01:00
}
2020-01-20 03:20:27 +01:00
public required init?(coder: NSCoder) {
2019-11-28 06:42:07 +01:00
super.init(coder: coder)
2019-12-02 01:58:15 +01:00
setUpViewHierarchy()
2019-11-28 06:42:07 +01:00
}
2019-12-02 01:58:15 +01:00
private func setUpViewHierarchy() {
2019-11-28 06:42:07 +01:00
// Set up image view
addSubview(imageView)
imageView.pin(.leading, to: .leading, of: self)
imageView.pin(.top, to: .top, of: self)
2020-03-17 06:18:53 +01:00
let imageViewSize = CGFloat(Values.mediumProfilePictureSize)
2020-01-22 03:34:49 +01:00
imageViewWidthConstraint = imageView.set(.width, to: imageViewSize)
imageViewHeightConstraint = imageView.set(.height, to: imageViewSize)
2019-11-28 06:42:07 +01:00
// Set up additional image view
addSubview(additionalImageView)
additionalImageView.pin(.trailing, to: .trailing, of: self)
additionalImageView.pin(.bottom, to: .bottom, of: self)
2020-03-17 06:18:53 +01:00
let additionalImageViewSize = CGFloat(Values.smallProfilePictureSize)
2020-09-14 03:31:45 +02:00
additionalImageViewWidthConstraint = additionalImageView.set(.width, to: additionalImageViewSize)
additionalImageViewHeightConstraint = additionalImageView.set(.height, to: additionalImageViewSize)
2019-11-28 06:42:07 +01:00
additionalImageView.layer.cornerRadius = additionalImageViewSize / 2
}
// FIXME: Remove this once we refactor the ConversationVC to Swift (use the HomeViewModel approach)
@objc(updateForThreadId:)
public func update(forThreadId threadId: String?) {
guard
let threadId: String = threadId,
let viewModel: SessionThreadViewModel = Storage.shared.read({ db -> SessionThreadViewModel? in
let userPublicKey: String = getUserHexEncodedPublicKey(db)
return try SessionThreadViewModel
.conversationSettingsProfileQuery(threadId: threadId, userPublicKey: userPublicKey)
.fetchOne(db)
})
else { return }
update(
publicKey: viewModel.threadId,
profile: viewModel.profile,
additionalProfile: viewModel.additionalProfile,
threadVariant: viewModel.threadVariant,
openGroupProfilePicture: viewModel.openGroupProfilePictureData.map { UIImage(data: $0) },
useFallbackPicture: (
viewModel.threadVariant == .openGroup &&
viewModel.openGroupProfilePictureData == nil
),
showMultiAvatarForClosedGroup: true
)
}
public func update(
publicKey: String = "",
profile: Profile? = nil,
additionalProfile: Profile? = nil,
threadVariant: SessionThread.Variant,
openGroupProfilePicture: UIImage? = nil,
useFallbackPicture: Bool = false,
showMultiAvatarForClosedGroup: Bool = false
) {
2020-06-15 05:50:56 +02:00
AssertIsOnMainThread()
guard !useFallbackPicture else {
switch self.size {
case Values.smallProfilePictureSize..<Values.mediumProfilePictureSize: imageView.image = #imageLiteral(resourceName: "SessionWhite16")
case Values.mediumProfilePictureSize..<Values.largeProfilePictureSize: imageView.image = #imageLiteral(resourceName: "SessionWhite24")
default: imageView.image = #imageLiteral(resourceName: "SessionWhite40")
}
imageView.contentMode = .center
imageView.backgroundColor = UIColor(rgbHex: 0x353535)
imageView.layer.cornerRadius = (self.size / 2)
imageViewWidthConstraint.constant = self.size
imageViewHeightConstraint.constant = self.size
additionalImageView.isHidden = true
additionalImageView.image = nil
additionalImageView.layer.cornerRadius = (self.size / 2)
return
}
guard !publicKey.isEmpty || openGroupProfilePicture != nil else { return }
func getProfilePicture(of size: CGFloat, for publicKey: String, profile: Profile?) -> (image: UIImage, isTappable: Bool) {
Fixed a number of reported bugs, some cleanup, added animated profile support Added support for animated profile images (no ability to crop/resize) Updated the message trimming to only remove messages if the open group has 2000 messages or more Updated the message trimming setting to default to be on Updated the ContextMenu to fade out the snapshot as well (looked buggy if the device had even minor lag) Updated the ProfileManager to delete and re-download invalid avatar images (and updated the conversation screen to reload when avatars complete downloading) Updated the message request notification logic so it will show notifications when receiving a new message request as long as the user has read all the old ones (previously the user had to accept/reject all the old ones) Fixed a bug where the "trim open group messages" toggle was accessing UI off the main thread Fixed a bug where the "Chats" settings screen had a close button instead of a back button Fixed a bug where the 'viewsToMove' for the reply UI was inconsistent in some places Fixed an issue where the ProfileManager was doing all of it's validation (and writing to disk) within the database write closure which would block database writes unnecessarily Fixed a bug where a message request wouldn't be identified as such just because it wasn't visible in the conversations list Fixed a bug where opening a message request notification would result in the message request being in the wrong state (also wouldn't insert the 'MessageRequestsViewController' into the hierarchy) Fixed a bug where the avatar image wouldn't appear beside incoming closed group message in some situations cases Removed an error log that was essentially just spam Remove the logic to delete old profile images when calling save on a Profile (wouldn't get called if the row was modified directly and duplicates GarbageCollection logic) Remove the logic to send a notification when calling save on a Profile (wouldn't get called if the row was modified directly) Tweaked the message trimming description to be more accurate Cleaned up some duplicate logic used to determine if a notification should be shown Cleaned up some onion request logic (was passing the version info in some cases when not needed) Moved the push notification notify API call into the PushNotificationAPI class for consistency
2022-07-08 09:53:48 +02:00
if let profile: Profile = profile, let profileData: Data = ProfileManager.profileAvatar(profile: profile), let image: YYImage = YYImage(data: profileData) {
return (image, true)
2020-09-14 03:31:45 +02:00
}
return (
Identicon.generatePlaceholderIcon(
seed: publicKey,
text: (profile?.displayName(for: threadVariant))
.defaulting(to: publicKey),
size: size
),
false
)
2019-11-29 06:30:01 +01:00
}
// Calulate the sizes (and set the additional image content)
let targetSize: CGFloat
switch (threadVariant, showMultiAvatarForClosedGroup) {
case (.closedGroup, true):
if self.size == 40 {
targetSize = 32
}
else if self.size == Values.largeProfilePictureSize {
targetSize = 56
}
else {
targetSize = Values.smallProfilePictureSize
}
imageViewWidthConstraint.constant = targetSize
imageViewHeightConstraint.constant = targetSize
additionalImageViewWidthConstraint.constant = targetSize
additionalImageViewHeightConstraint.constant = targetSize
additionalImageView.isHidden = false
if let additionalProfile: Profile = additionalProfile {
additionalImageView.image = getProfilePicture(
of: targetSize,
for: additionalProfile.id,
profile: additionalProfile
).image
}
default:
targetSize = self.size
imageViewWidthConstraint.constant = targetSize
imageViewHeightConstraint.constant = targetSize
additionalImageView.isHidden = true
additionalImageView.image = nil
2019-11-28 06:42:07 +01:00
}
// Set the image
if let openGroupProfilePicture: UIImage = openGroupProfilePicture {
imageView.image = openGroupProfilePicture
hasTappableProfilePicture = true
}
else {
let (image, isTappable): (UIImage, Bool) = getProfilePicture(
of: targetSize,
for: publicKey,
profile: profile
)
imageView.image = image
hasTappableProfilePicture = isTappable
}
Fixed a number of reported bugs, some cleanup, added animated profile support Added support for animated profile images (no ability to crop/resize) Updated the message trimming to only remove messages if the open group has 2000 messages or more Updated the message trimming setting to default to be on Updated the ContextMenu to fade out the snapshot as well (looked buggy if the device had even minor lag) Updated the ProfileManager to delete and re-download invalid avatar images (and updated the conversation screen to reload when avatars complete downloading) Updated the message request notification logic so it will show notifications when receiving a new message request as long as the user has read all the old ones (previously the user had to accept/reject all the old ones) Fixed a bug where the "trim open group messages" toggle was accessing UI off the main thread Fixed a bug where the "Chats" settings screen had a close button instead of a back button Fixed a bug where the 'viewsToMove' for the reply UI was inconsistent in some places Fixed an issue where the ProfileManager was doing all of it's validation (and writing to disk) within the database write closure which would block database writes unnecessarily Fixed a bug where a message request wouldn't be identified as such just because it wasn't visible in the conversations list Fixed a bug where opening a message request notification would result in the message request being in the wrong state (also wouldn't insert the 'MessageRequestsViewController' into the hierarchy) Fixed a bug where the avatar image wouldn't appear beside incoming closed group message in some situations cases Removed an error log that was essentially just spam Remove the logic to delete old profile images when calling save on a Profile (wouldn't get called if the row was modified directly and duplicates GarbageCollection logic) Remove the logic to send a notification when calling save on a Profile (wouldn't get called if the row was modified directly) Tweaked the message trimming description to be more accurate Cleaned up some duplicate logic used to determine if a notification should be shown Cleaned up some onion request logic (was passing the version info in some cases when not needed) Moved the push notification notify API call into the PushNotificationAPI class for consistency
2022-07-08 09:53:48 +02:00
imageView.contentMode = .scaleAspectFill
imageView.backgroundColor = Colors.unimportant
imageView.layer.cornerRadius = (targetSize / 2)
additionalImageView.layer.cornerRadius = (targetSize / 2)
2019-11-28 06:42:07 +01:00
}
// MARK: - Convenience
Fixed a number of reported bugs, some cleanup, added animated profile support Added support for animated profile images (no ability to crop/resize) Updated the message trimming to only remove messages if the open group has 2000 messages or more Updated the message trimming setting to default to be on Updated the ContextMenu to fade out the snapshot as well (looked buggy if the device had even minor lag) Updated the ProfileManager to delete and re-download invalid avatar images (and updated the conversation screen to reload when avatars complete downloading) Updated the message request notification logic so it will show notifications when receiving a new message request as long as the user has read all the old ones (previously the user had to accept/reject all the old ones) Fixed a bug where the "trim open group messages" toggle was accessing UI off the main thread Fixed a bug where the "Chats" settings screen had a close button instead of a back button Fixed a bug where the 'viewsToMove' for the reply UI was inconsistent in some places Fixed an issue where the ProfileManager was doing all of it's validation (and writing to disk) within the database write closure which would block database writes unnecessarily Fixed a bug where a message request wouldn't be identified as such just because it wasn't visible in the conversations list Fixed a bug where opening a message request notification would result in the message request being in the wrong state (also wouldn't insert the 'MessageRequestsViewController' into the hierarchy) Fixed a bug where the avatar image wouldn't appear beside incoming closed group message in some situations cases Removed an error log that was essentially just spam Remove the logic to delete old profile images when calling save on a Profile (wouldn't get called if the row was modified directly and duplicates GarbageCollection logic) Remove the logic to send a notification when calling save on a Profile (wouldn't get called if the row was modified directly) Tweaked the message trimming description to be more accurate Cleaned up some duplicate logic used to determine if a notification should be shown Cleaned up some onion request logic (was passing the version info in some cases when not needed) Moved the push notification notify API call into the PushNotificationAPI class for consistency
2022-07-08 09:53:48 +02:00
private func getImageView() -> YYAnimatedImageView {
let result = YYAnimatedImageView()
2019-11-28 06:42:07 +01:00
result.layer.masksToBounds = true
2020-03-17 06:18:53 +01:00
result.backgroundColor = Colors.unimportant
Fixed a number of reported bugs, some cleanup, added animated profile support Added support for animated profile images (no ability to crop/resize) Updated the message trimming to only remove messages if the open group has 2000 messages or more Updated the message trimming setting to default to be on Updated the ContextMenu to fade out the snapshot as well (looked buggy if the device had even minor lag) Updated the ProfileManager to delete and re-download invalid avatar images (and updated the conversation screen to reload when avatars complete downloading) Updated the message request notification logic so it will show notifications when receiving a new message request as long as the user has read all the old ones (previously the user had to accept/reject all the old ones) Fixed a bug where the "trim open group messages" toggle was accessing UI off the main thread Fixed a bug where the "Chats" settings screen had a close button instead of a back button Fixed a bug where the 'viewsToMove' for the reply UI was inconsistent in some places Fixed an issue where the ProfileManager was doing all of it's validation (and writing to disk) within the database write closure which would block database writes unnecessarily Fixed a bug where a message request wouldn't be identified as such just because it wasn't visible in the conversations list Fixed a bug where opening a message request notification would result in the message request being in the wrong state (also wouldn't insert the 'MessageRequestsViewController' into the hierarchy) Fixed a bug where the avatar image wouldn't appear beside incoming closed group message in some situations cases Removed an error log that was essentially just spam Remove the logic to delete old profile images when calling save on a Profile (wouldn't get called if the row was modified directly and duplicates GarbageCollection logic) Remove the logic to send a notification when calling save on a Profile (wouldn't get called if the row was modified directly) Tweaked the message trimming description to be more accurate Cleaned up some duplicate logic used to determine if a notification should be shown Cleaned up some onion request logic (was passing the version info in some cases when not needed) Moved the push notification notify API call into the PushNotificationAPI class for consistency
2022-07-08 09:53:48 +02:00
result.contentMode = .scaleAspectFill
2019-11-28 06:42:07 +01:00
return result
}
@objc public func getProfilePicture() -> UIImage? {
return (hasTappableProfilePicture ? imageView.image : nil)
}
2019-11-28 06:42:07 +01:00
}