Files
session-ios/SessionMessagingKit/Utilities/ProfilePictureView+Convenience.swift

266 lines
12 KiB
Swift

// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import SessionUIKit
import SessionUtilitiesKit
public extension ProfilePictureView {
func update(
publicKey: String,
threadVariant: SessionThread.Variant,
displayPictureUrl: String?,
profile: Profile?,
profileIcon: Info.ProfileIcon = .none,
additionalProfile: Profile? = nil,
additionalProfileIcon: Info.ProfileIcon = .none,
using dependencies: Dependencies
) {
let (info, additionalInfo): (front: Info?, back: Info?) = Info.generateInfoFrom(
size: self.size,
publicKey: publicKey,
threadVariant: threadVariant,
displayPictureUrl: displayPictureUrl,
profile: profile,
profileIcon: profileIcon,
additionalProfile: additionalProfile,
additionalProfileIcon: additionalProfileIcon,
using: dependencies
)
guard let info: Info = info else { return }
update(info, additionalInfo: additionalInfo)
}
}
public extension ProfilePictureView.Info {
static func generateInfoFrom(
size: Size,
publicKey: String,
threadVariant: SessionThread.Variant,
displayPictureUrl: String?,
profile: Profile?,
profileIcon: ProfileIcon = .none,
additionalProfile: Profile? = nil,
additionalProfileIcon: ProfileIcon = .none,
using dependencies: Dependencies
) -> (front: ProfilePictureView.Info?, back: ProfilePictureView.Info?) {
let explicitPath: String? = try? dependencies[singleton: .displayPictureManager].path(
for: displayPictureUrl
)
let explicitPathFileExists: Bool = (explicitPath.map { dependencies[singleton: .fileManager].fileExists(atPath: $0) } ?? false)
switch (explicitPath, explicitPathFileExists, publicKey.isEmpty, threadVariant) {
// TODO: Deal with this case later when implement group related Pro features
case (.some(let path), true, _, .legacyGroup), (.some(let path), true, _, .group): fallthrough
case (.some(let path), true, _, .community):
/// If we are given an explicit `displayPictureUrl` then only use that
return (ProfilePictureView.Info(
source: .url(URL(fileURLWithPath: path)),
canAnimate: true,
icon: profileIcon
), nil)
case (.some(let path), true, _, _):
/// If we are given an explicit `displayPictureUrl` then only use that
return (
ProfilePictureView.Info(
source: .url(URL(fileURLWithPath: path)),
canAnimate: ProfilePictureView.canProfileAnimate(profile, using: dependencies),
icon: profileIcon
),
nil
)
case (_, _, _, .community):
return (
ProfilePictureView.Info(
source: {
switch size {
case .navigation, .message: return .image("SessionWhite16", #imageLiteral(resourceName: "SessionWhite16"))
case .list: return .image("SessionWhite24", #imageLiteral(resourceName: "SessionWhite24"))
case .hero, .modal, .expanded: return .image("SessionWhite40", #imageLiteral(resourceName: "SessionWhite40"))
}
}(),
canAnimate: true,
inset: {
let padding: CGFloat
switch size {
case .navigation, .message: padding = 7
case .list: padding = 12
case .hero: padding = 28
case .modal: padding = 24
case .expanded: padding = 50
}
return UIEdgeInsets(
top: padding,
left: padding,
bottom: padding,
right: padding
)
}(),
icon: profileIcon,
forcedBackgroundColor: .theme(.classicDark, color: .borderSeparator)
),
nil
)
case (_, _, true, _): return (nil, nil)
case (_, _, _, .legacyGroup), (_, _, _, .group):
let source: ImageDataManager.DataSource = {
guard
let path: String = try? dependencies[singleton: .displayPictureManager]
.path(for: profile?.displayPictureUrl),
dependencies[singleton: .fileManager].fileExists(atPath: path)
else {
return .placeholderIcon(
seed: (profile?.id ?? publicKey),
text: (profile?.displayName(for: threadVariant))
.defaulting(to: publicKey),
size: (additionalProfile != nil ?
size.multiImageSize :
size.viewSize
)
)
}
return ImageDataManager.DataSource.url(URL(fileURLWithPath: path))
}()
return (
ProfilePictureView.Info(
source: source,
canAnimate: ProfilePictureView.canProfileAnimate(profile, using: dependencies),
icon: profileIcon
),
additionalProfile
.map { other in
let source: ImageDataManager.DataSource = {
guard
let path: String = try? dependencies[singleton: .displayPictureManager]
.path(for: other.displayPictureUrl),
dependencies[singleton: .fileManager].fileExists(atPath: path)
else {
return .placeholderIcon(
seed: other.id,
text: other.displayName(for: threadVariant),
size: size.multiImageSize
)
}
return ImageDataManager.DataSource.url(URL(fileURLWithPath: path))
}()
return ProfilePictureView.Info(
source: source,
canAnimate: ProfilePictureView.canProfileAnimate(other, using: dependencies),
icon: additionalProfileIcon
)
}
.defaulting(
to: ProfilePictureView.Info(
source: .image("ic_user_round_fill", UIImage(named: "ic_user_round_fill")),
canAnimate: false,
renderingMode: .alwaysTemplate,
themeTintColor: .white,
inset: UIEdgeInsets(
top: 4,
left: 4,
bottom: -6,
right: 4
),
icon: additionalProfileIcon
)
)
)
case (_, _, _, .contact):
let source: ImageDataManager.DataSource = {
guard
let path: String = try? dependencies[singleton: .displayPictureManager]
.path(for: profile?.displayPictureUrl),
dependencies[singleton: .fileManager].fileExists(atPath: path)
else {
return .placeholderIcon(
seed: publicKey,
text: (profile?.displayName(for: threadVariant))
.defaulting(to: publicKey),
size: size.viewSize
)
}
return ImageDataManager.DataSource.url(URL(fileURLWithPath: path))
}()
return (
ProfilePictureView.Info(
source: source,
canAnimate: ProfilePictureView.canProfileAnimate(profile, using: dependencies),
icon: profileIcon),
nil
)
}
}
}
public extension ProfilePictureView {
// TODO: [PRO] Need to properly wire this up (it won't observe the changes, the parent screen will be responsible for updating the profile data and reloading the UI if the pro state changes)
static func canProfileAnimate(_ profile: Profile?, using dependencies: Dependencies) -> Bool {
guard dependencies[feature: .sessionProEnabled] else { return true }
switch profile {
case .none: return false
case .some(let profile) where profile.id == dependencies[cache: .general].sessionId.hexString:
if case .active = dependencies[singleton: .sessionProState].sessionProStateSubject.value {
return true
} else {
return false
}
case .some(let profile):
return dependencies.mutate(cache: .libSession, { $0.validateProProof(for: profile) })
}
}
}
public extension ProfilePictureSwiftUI {
init?(
size: ProfilePictureView.Info.Size,
publicKey: String,
threadVariant: SessionThread.Variant,
displayPictureUrl: String?,
profile: Profile?,
profileIcon: ProfilePictureView.Info.ProfileIcon = .none,
additionalProfile: Profile? = nil,
additionalProfileIcon: ProfilePictureView.Info.ProfileIcon = .none,
using dependencies: Dependencies
) {
let (info, additionalInfo) = ProfilePictureView.Info.generateInfoFrom(
size: size,
publicKey: publicKey,
threadVariant: threadVariant,
displayPictureUrl: displayPictureUrl,
profile: profile,
profileIcon: profileIcon,
additionalProfile: additionalProfile,
additionalProfileIcon: additionalProfileIcon,
using: dependencies
)
switch info {
case .none: return nil
case .some(let info):
self.init(
size: size,
info: info,
additionalInfo: additionalInfo,
dataManager: dependencies[singleton: .imageDataManager]
)
}
}
}