mirror of https://github.com/oxen-io/session-ios
Updated the profile picture modal
Moved the ProfilePictureView into SessionUIKit Fixed a couple of minor ProfilePictureView bugspull/859/head
parent
2d792e4e3e
commit
cf2e198a64
@ -0,0 +1,211 @@
|
|||||||
|
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SessionUIKit
|
||||||
|
|
||||||
|
public extension ProfilePictureView {
|
||||||
|
// FIXME: Remove this in the UserConfig branch
|
||||||
|
func update(
|
||||||
|
publicKey: String = "",
|
||||||
|
profile: Profile? = nil,
|
||||||
|
icon: ProfileIcon = .none,
|
||||||
|
additionalProfile: Profile? = nil,
|
||||||
|
additionalIcon: ProfileIcon = .none,
|
||||||
|
threadVariant: SessionThread.Variant,
|
||||||
|
openGroupProfilePictureData: Data? = nil,
|
||||||
|
useFallbackPicture: Bool = false,
|
||||||
|
showMultiAvatarForClosedGroup: Bool = false
|
||||||
|
) {
|
||||||
|
guard !useFallbackPicture else {
|
||||||
|
let placeholderImage: UIImage = {
|
||||||
|
switch self.size {
|
||||||
|
case .navigation, .message: return #imageLiteral(resourceName: "SessionWhite16")
|
||||||
|
case .list: return #imageLiteral(resourceName: "SessionWhite24")
|
||||||
|
case .hero: return #imageLiteral(resourceName: "SessionWhite40")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return update(
|
||||||
|
Info(
|
||||||
|
imageData: placeholderImage.pngData(),
|
||||||
|
inset: UIEdgeInsets(
|
||||||
|
top: 12,
|
||||||
|
left: 12,
|
||||||
|
bottom: 12,
|
||||||
|
right: 12
|
||||||
|
),
|
||||||
|
forcedBackgroundColor: .theme(.classicDark, color: .borderSeparator)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
guard openGroupProfilePictureData == nil else {
|
||||||
|
return update(Info(imageData: openGroupProfilePictureData))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (threadVariant, showMultiAvatarForClosedGroup) {
|
||||||
|
case (.closedGroup, true):
|
||||||
|
update(
|
||||||
|
Info(
|
||||||
|
imageData: (
|
||||||
|
profile.map { ProfileManager.profileAvatar(profile: $0) } ??
|
||||||
|
PlaceholderIcon.generate(
|
||||||
|
seed: publicKey,
|
||||||
|
text: (profile?.displayName(for: threadVariant))
|
||||||
|
.defaulting(to: publicKey),
|
||||||
|
size: (additionalProfile != nil ?
|
||||||
|
self.size.multiImageSize :
|
||||||
|
self.size.viewSize
|
||||||
|
)
|
||||||
|
).pngData()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
additionalInfo: additionalProfile
|
||||||
|
.map { otherProfile in
|
||||||
|
Info(
|
||||||
|
imageData: (
|
||||||
|
ProfileManager.profileAvatar(profile: otherProfile) ??
|
||||||
|
PlaceholderIcon.generate(
|
||||||
|
seed: otherProfile.id,
|
||||||
|
text: otherProfile.displayName(for: threadVariant),
|
||||||
|
size: self.size.multiImageSize
|
||||||
|
).pngData()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.defaulting(
|
||||||
|
to: Info(
|
||||||
|
imageData: UIImage(systemName: "person.fill")?.pngData(),
|
||||||
|
renderingMode: .alwaysTemplate,
|
||||||
|
themeTintColor: .white,
|
||||||
|
inset: UIEdgeInsets(
|
||||||
|
top: 3,
|
||||||
|
left: 0,
|
||||||
|
bottom: -5,
|
||||||
|
right: 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
default:
|
||||||
|
update(
|
||||||
|
Info(
|
||||||
|
imageData: (
|
||||||
|
profile.map { ProfileManager.profileAvatar(profile: $0) } ??
|
||||||
|
PlaceholderIcon.generate(
|
||||||
|
seed: publicKey,
|
||||||
|
text: (profile?.displayName(for: threadVariant))
|
||||||
|
.defaulting(to: publicKey),
|
||||||
|
size: (additionalProfile != nil ?
|
||||||
|
self.size.multiImageSize :
|
||||||
|
self.size.viewSize
|
||||||
|
)
|
||||||
|
).pngData()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(
|
||||||
|
publicKey: String,
|
||||||
|
threadVariant: SessionThread.Variant,
|
||||||
|
customImageData: Data?,
|
||||||
|
profile: Profile?,
|
||||||
|
additionalProfile: Profile?
|
||||||
|
) {
|
||||||
|
// If we are given 'customImageData' then only use that
|
||||||
|
guard customImageData == nil else { return update(Info(imageData: customImageData)) }
|
||||||
|
|
||||||
|
// Otherwise there are conversation-type-specific behaviours
|
||||||
|
switch threadVariant {
|
||||||
|
case .openGroup:
|
||||||
|
let placeholderImage: UIImage = {
|
||||||
|
switch self.size {
|
||||||
|
case .navigation, .message: return #imageLiteral(resourceName: "SessionWhite16")
|
||||||
|
case .list: return #imageLiteral(resourceName: "SessionWhite24")
|
||||||
|
case .hero: return #imageLiteral(resourceName: "SessionWhite40")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
update(
|
||||||
|
Info(
|
||||||
|
imageData: placeholderImage.pngData(),
|
||||||
|
inset: UIEdgeInsets(
|
||||||
|
top: 12,
|
||||||
|
left: 12,
|
||||||
|
bottom: 12,
|
||||||
|
right: 12
|
||||||
|
),
|
||||||
|
forcedBackgroundColor: .theme(.classicDark, color: .borderSeparator)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
case .closedGroup: //.legacyGroup, .group:
|
||||||
|
guard !publicKey.isEmpty else { return }
|
||||||
|
// TODO: Test that this doesn't call 'PlaceholderIcon.generate' when the original value exists
|
||||||
|
update(
|
||||||
|
Info(
|
||||||
|
imageData: (
|
||||||
|
profile.map { ProfileManager.profileAvatar(profile: $0) } ??
|
||||||
|
PlaceholderIcon.generate(
|
||||||
|
seed: publicKey,
|
||||||
|
text: (profile?.displayName(for: threadVariant))
|
||||||
|
.defaulting(to: publicKey),
|
||||||
|
size: (additionalProfile != nil ?
|
||||||
|
self.size.multiImageSize :
|
||||||
|
self.size.viewSize
|
||||||
|
)
|
||||||
|
).pngData()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
additionalInfo: additionalProfile
|
||||||
|
.map { otherProfile in
|
||||||
|
Info(
|
||||||
|
imageData: (
|
||||||
|
ProfileManager.profileAvatar(profile: otherProfile) ??
|
||||||
|
PlaceholderIcon.generate(
|
||||||
|
seed: otherProfile.id,
|
||||||
|
text: otherProfile.displayName(for: threadVariant),
|
||||||
|
size: self.size.multiImageSize
|
||||||
|
).pngData()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.defaulting(
|
||||||
|
to: Info(
|
||||||
|
imageData: UIImage(systemName: "person.fill")?.pngData(),
|
||||||
|
renderingMode: .alwaysTemplate,
|
||||||
|
themeTintColor: .white,
|
||||||
|
inset: UIEdgeInsets(
|
||||||
|
top: 3,
|
||||||
|
left: 0,
|
||||||
|
bottom: -5,
|
||||||
|
right: 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
case .contact:
|
||||||
|
guard !publicKey.isEmpty else { return }
|
||||||
|
|
||||||
|
update(
|
||||||
|
Info(
|
||||||
|
imageData: (
|
||||||
|
profile.map { ProfileManager.profileAvatar(profile: $0) } ??
|
||||||
|
PlaceholderIcon.generate(
|
||||||
|
seed: publicKey,
|
||||||
|
text: (profile?.displayName(for: threadVariant))
|
||||||
|
.defaulting(to: publicKey),
|
||||||
|
size: (additionalProfile != nil ?
|
||||||
|
self.size.multiImageSize :
|
||||||
|
self.size.viewSize
|
||||||
|
)
|
||||||
|
).pngData()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,57 +0,0 @@
|
|||||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import SessionUIKit
|
|
||||||
import SessionUtilitiesKit
|
|
||||||
|
|
||||||
@objc(LKIdenticon)
|
|
||||||
public final class Identicon: NSObject {
|
|
||||||
private static let placeholderCache: Atomic<NSCache<NSString, UIImage>> = {
|
|
||||||
let result = NSCache<NSString, UIImage>()
|
|
||||||
result.countLimit = 50
|
|
||||||
|
|
||||||
return Atomic(result)
|
|
||||||
}()
|
|
||||||
|
|
||||||
@objc public static func generatePlaceholderIcon(seed: String, text: String, size: CGFloat) -> UIImage {
|
|
||||||
let icon = PlaceholderIcon(seed: seed)
|
|
||||||
|
|
||||||
var content: String = (text.hasSuffix("\(String(seed.suffix(4))))") ?
|
|
||||||
(text.split(separator: "(")
|
|
||||||
.first
|
|
||||||
.map { String($0) })
|
|
||||||
.defaulting(to: text) :
|
|
||||||
text
|
|
||||||
)
|
|
||||||
|
|
||||||
if content.count > 2 && SessionId.Prefix(from: content) != nil {
|
|
||||||
content.removeFirst(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
let initials: String = content
|
|
||||||
.split(separator: " ")
|
|
||||||
.compactMap { word in word.first.map { String($0) } }
|
|
||||||
.joined()
|
|
||||||
let cacheKey: String = "\(content)-\(Int(floor(size)))"
|
|
||||||
|
|
||||||
if let cachedIcon: UIImage = placeholderCache.wrappedValue.object(forKey: cacheKey as NSString) {
|
|
||||||
return cachedIcon
|
|
||||||
}
|
|
||||||
|
|
||||||
let layer = icon.generateLayer(
|
|
||||||
with: size,
|
|
||||||
text: (initials.count >= 2 ?
|
|
||||||
initials.substring(to: 2).uppercased() :
|
|
||||||
content.substring(to: 2).uppercased()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
let rect = CGRect(origin: CGPoint.zero, size: layer.frame.size)
|
|
||||||
let renderer = UIGraphicsImageRenderer(size: rect.size)
|
|
||||||
let result = renderer.image { layer.render(in: $0.cgContext) }
|
|
||||||
|
|
||||||
placeholderCache.mutate { $0.setObject(result, forKey: cacheKey as NSString) }
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue