Updated the profile picture modal and standardised the ProfilePictureView sizes
Fixed an issue where 'CurrentAppContext().isMainAppAndActive' wasn't called on the main thread Updated the ProfilePictureView to have the updated icon UI
This commit is contained in:
parent
2035d508d9
commit
2d792e4e3e
|
@ -205,9 +205,9 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
|
|||
return
|
||||
}
|
||||
|
||||
guard CurrentAppContext().isMainAppAndActive else { return }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
guard CurrentAppContext().isMainAppAndActive else { return }
|
||||
|
||||
guard let presentingVC = CurrentAppContext().frontmostViewController() else {
|
||||
preconditionFailure() // FIXME: Handle more gracefully
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import UIKit
|
|||
import WebRTC
|
||||
import SessionUIKit
|
||||
import SessionMessagingKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
final class IncomingCallBanner: UIView, UIGestureRecognizerDelegate {
|
||||
private static let swipeToOperateThreshold: CGFloat = 60
|
||||
|
@ -20,14 +21,7 @@ final class IncomingCallBanner: UIView, UIGestureRecognizerDelegate {
|
|||
return result
|
||||
}()
|
||||
|
||||
private lazy var profilePictureView: ProfilePictureView = {
|
||||
let result = ProfilePictureView()
|
||||
let size: CGFloat = 60
|
||||
result.size = size
|
||||
result.set(.width, to: size)
|
||||
result.set(.height, to: size)
|
||||
return result
|
||||
}()
|
||||
private lazy var profilePictureView: ProfilePictureView = ProfilePictureView(size: .list)
|
||||
|
||||
private lazy var displayNameLabel: UILabel = {
|
||||
let result = UILabel()
|
||||
|
|
|
@ -1104,15 +1104,13 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
|
|||
|
||||
switch threadData.threadVariant {
|
||||
case .contact:
|
||||
let profilePictureView = ProfilePictureView()
|
||||
profilePictureView.size = Values.verySmallProfilePictureSize
|
||||
let profilePictureView = ProfilePictureView(size: .navigation)
|
||||
profilePictureView.update(
|
||||
publicKey: threadData.threadId, // Contact thread uses the contactId
|
||||
profile: threadData.profile,
|
||||
threadVariant: threadData.threadVariant
|
||||
)
|
||||
profilePictureView.set(.width, to: (44 - 16)) // Width of the standard back button
|
||||
profilePictureView.set(.height, to: Values.verySmallProfilePictureSize)
|
||||
profilePictureView.customWidth = (44 - 16) // Width of the standard back button
|
||||
|
||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(openSettings))
|
||||
profilePictureView.addGestureRecognizer(tapGestureRecognizer)
|
||||
|
|
|
@ -4,6 +4,7 @@ import UIKit
|
|||
import SessionUIKit
|
||||
import SessionMessagingKit
|
||||
import SessionUtilitiesKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
final class InputView: UIView, InputViewButtonDelegate, InputTextViewDelegate, MentionSelectionViewDelegate {
|
||||
// MARK: - Variables
|
||||
|
@ -491,7 +492,7 @@ final class InputView: UIView, InputViewButtonDelegate, InputTextViewDelegate, M
|
|||
func showMentionsUI(for candidates: [MentionInfo]) {
|
||||
mentionsView.candidates = candidates
|
||||
|
||||
let mentionCellHeight = (Values.smallProfilePictureSize + 2 * Values.smallSpacing)
|
||||
let mentionCellHeight = (ProfilePictureView.Size.message.viewSize + 2 * Values.smallSpacing)
|
||||
mentionsViewHeightConstraint.constant = CGFloat(min(3, candidates.count)) * mentionCellHeight
|
||||
layoutIfNeeded()
|
||||
|
||||
|
|
|
@ -111,9 +111,7 @@ private extension MentionSelectionView {
|
|||
final class Cell: UITableViewCell {
|
||||
// MARK: - UI
|
||||
|
||||
private lazy var profilePictureView: ProfilePictureView = ProfilePictureView()
|
||||
|
||||
private lazy var moderatorIconImageView: UIImageView = UIImageView(image: #imageLiteral(resourceName: "Crown"))
|
||||
private lazy var profilePictureView: ProfilePictureView = ProfilePictureView(size: .message)
|
||||
|
||||
private lazy var displayNameLabel: UILabel = {
|
||||
let result: UILabel = UILabel()
|
||||
|
@ -155,18 +153,12 @@ private extension MentionSelectionView {
|
|||
selectedBackgroundView.themeBackgroundColor = .highlighted(.settings_tabBackground)
|
||||
self.selectedBackgroundView = selectedBackgroundView
|
||||
|
||||
// Profile picture image view
|
||||
let profilePictureViewSize = Values.smallProfilePictureSize
|
||||
profilePictureView.set(.width, to: profilePictureViewSize)
|
||||
profilePictureView.set(.height, to: profilePictureViewSize)
|
||||
profilePictureView.size = profilePictureViewSize
|
||||
|
||||
// Main stack view
|
||||
let mainStackView = UIStackView(arrangedSubviews: [ profilePictureView, displayNameLabel ])
|
||||
mainStackView.axis = .horizontal
|
||||
mainStackView.alignment = .center
|
||||
mainStackView.spacing = Values.mediumSpacing
|
||||
mainStackView.set(.height, to: profilePictureViewSize)
|
||||
mainStackView.set(.height, to: ProfilePictureView.Size.message.viewSize)
|
||||
contentView.addSubview(mainStackView)
|
||||
mainStackView.pin(.leading, to: .leading, of: contentView, withInset: Values.mediumSpacing)
|
||||
mainStackView.pin(.top, to: .top, of: contentView, withInset: Values.smallSpacing)
|
||||
|
@ -174,13 +166,6 @@ private extension MentionSelectionView {
|
|||
contentView.pin(.bottom, to: .bottom, of: mainStackView, withInset: Values.smallSpacing)
|
||||
mainStackView.set(.width, to: UIScreen.main.bounds.width - 2 * Values.mediumSpacing)
|
||||
|
||||
// Moderator icon image view
|
||||
moderatorIconImageView.set(.width, to: 20)
|
||||
moderatorIconImageView.set(.height, to: 20)
|
||||
contentView.addSubview(moderatorIconImageView)
|
||||
moderatorIconImageView.pin(.trailing, to: .trailing, of: profilePictureView, withInset: 1)
|
||||
moderatorIconImageView.pin(.bottom, to: .bottom, of: profilePictureView, withInset: 4.5)
|
||||
|
||||
// Separator
|
||||
addSubview(separator)
|
||||
separator.pin(.leading, to: .leading, of: self)
|
||||
|
@ -200,9 +185,9 @@ private extension MentionSelectionView {
|
|||
profilePictureView.update(
|
||||
publicKey: profile.id,
|
||||
profile: profile,
|
||||
icon: (isUserModeratorOrAdmin ? .crown : .none),
|
||||
threadVariant: threadVariant
|
||||
)
|
||||
moderatorIconImageView.isHidden = !isUserModeratorOrAdmin
|
||||
separator.isHidden = isLast
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
|
|||
private lazy var authorLabelTopConstraint = authorLabel.pin(.top, to: .top, of: self)
|
||||
private lazy var authorLabelHeightConstraint = authorLabel.set(.height, to: 0)
|
||||
private lazy var profilePictureViewLeadingConstraint = profilePictureView.pin(.leading, to: .leading, of: self, withInset: VisibleMessageCell.groupThreadHSpacing)
|
||||
private lazy var profilePictureViewWidthConstraint = profilePictureView.set(.width, to: Values.verySmallProfilePictureSize)
|
||||
private lazy var contentViewLeadingConstraint1 = snContentView.pin(.leading, to: .trailing, of: profilePictureView, withInset: VisibleMessageCell.groupThreadHSpacing)
|
||||
private lazy var contentViewLeadingConstraint2 = snContentView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: VisibleMessageCell.gutterSize)
|
||||
private lazy var contentViewTopConstraint = snContentView.pin(.top, to: .bottom, of: authorLabel, withInset: VisibleMessageCell.authorLabelBottomSpacing)
|
||||
|
@ -51,22 +50,13 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
|
|||
private lazy var viewsToMoveForReply: [UIView] = [
|
||||
snContentView,
|
||||
profilePictureView,
|
||||
moderatorIconImageView,
|
||||
replyButton,
|
||||
timerView,
|
||||
messageStatusImageView,
|
||||
reactionContainerView
|
||||
]
|
||||
|
||||
private lazy var profilePictureView: ProfilePictureView = {
|
||||
let result: ProfilePictureView = ProfilePictureView()
|
||||
result.set(.height, to: Values.verySmallProfilePictureSize)
|
||||
result.size = Values.verySmallProfilePictureSize
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var moderatorIconImageView = UIImageView(image: #imageLiteral(resourceName: "Crown"))
|
||||
private lazy var profilePictureView: ProfilePictureView = ProfilePictureView(size: .message)
|
||||
|
||||
lazy var bubbleBackgroundView: UIView = {
|
||||
let result = UIView()
|
||||
|
@ -176,7 +166,6 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
|
|||
private static let messageStatusImageViewSize: CGFloat = 12
|
||||
private static let authorLabelBottomSpacing: CGFloat = 4
|
||||
private static let groupThreadHSpacing: CGFloat = 12
|
||||
private static let profilePictureSize = Values.verySmallProfilePictureSize
|
||||
private static let authorLabelInset: CGFloat = 12
|
||||
private static let replyButtonSize: CGFloat = 24
|
||||
private static let maxBubbleTranslationX: CGFloat = 40
|
||||
|
@ -186,7 +175,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
|
|||
static let contactThreadHSpacing = Values.mediumSpacing
|
||||
|
||||
static var gutterSize: CGFloat = {
|
||||
var result = groupThreadHSpacing + profilePictureSize + groupThreadHSpacing
|
||||
var result = groupThreadHSpacing + ProfilePictureView.Size.message.viewSize + groupThreadHSpacing
|
||||
|
||||
if UIDevice.current.isIPad {
|
||||
result += 168
|
||||
|
@ -195,7 +184,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
|
|||
return result
|
||||
}()
|
||||
|
||||
static var leftGutterSize: CGFloat { groupThreadHSpacing + profilePictureSize + groupThreadHSpacing }
|
||||
static var leftGutterSize: CGFloat { groupThreadHSpacing + ProfilePictureView.Size.message.viewSize + groupThreadHSpacing }
|
||||
|
||||
// MARK: Direction & Position
|
||||
|
||||
|
@ -214,21 +203,13 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
|
|||
// Profile picture view
|
||||
addSubview(profilePictureView)
|
||||
profilePictureViewLeadingConstraint.isActive = true
|
||||
profilePictureViewWidthConstraint.isActive = true
|
||||
|
||||
// Moderator icon image view
|
||||
moderatorIconImageView.set(.width, to: 20)
|
||||
moderatorIconImageView.set(.height, to: 20)
|
||||
addSubview(moderatorIconImageView)
|
||||
moderatorIconImageView.pin(.trailing, to: .trailing, of: profilePictureView, withInset: 1)
|
||||
moderatorIconImageView.pin(.bottom, to: .bottom, of: profilePictureView, withInset: 4.5)
|
||||
|
||||
// Content view
|
||||
addSubview(snContentView)
|
||||
contentViewLeadingConstraint1.isActive = true
|
||||
contentViewTopConstraint.isActive = true
|
||||
contentViewTrailingConstraint1.isActive = true
|
||||
snContentView.pin(.bottom, to: .bottom, of: profilePictureView, withInset: -1)
|
||||
snContentView.pin(.bottom, to: .bottom, of: profilePictureView)
|
||||
|
||||
// Bubble background view
|
||||
bubbleBackgroundView.addSubview(bubbleView)
|
||||
|
@ -317,14 +298,13 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
|
|||
|
||||
// Profile picture view
|
||||
profilePictureViewLeadingConstraint.constant = (isGroupThread ? VisibleMessageCell.groupThreadHSpacing : 0)
|
||||
profilePictureViewWidthConstraint.constant = (isGroupThread ? VisibleMessageCell.profilePictureSize : 0)
|
||||
profilePictureView.isHidden = (!cellViewModel.shouldShowProfile || cellViewModel.profile == nil)
|
||||
profilePictureView.update(
|
||||
publicKey: cellViewModel.authorId,
|
||||
profile: cellViewModel.profile,
|
||||
icon: (cellViewModel.isSenderOpenGroupModerator ? .crown : .none),
|
||||
threadVariant: cellViewModel.threadVariant
|
||||
)
|
||||
moderatorIconImageView.isHidden = (!cellViewModel.isSenderOpenGroupModerator || !cellViewModel.shouldShowProfile)
|
||||
|
||||
// Bubble view
|
||||
contentViewLeadingConstraint1.isActive = (
|
||||
|
|
|
@ -477,19 +477,15 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, SeedRemi
|
|||
|
||||
private func updateNavBarButtons() {
|
||||
// Profile picture view
|
||||
let profilePictureSize = Values.verySmallProfilePictureSize
|
||||
let profilePictureView = ProfilePictureView()
|
||||
let profilePictureView = ProfilePictureView(size: .navigation)
|
||||
profilePictureView.accessibilityIdentifier = "User settings"
|
||||
profilePictureView.accessibilityLabel = "User settings"
|
||||
profilePictureView.isAccessibilityElement = true
|
||||
profilePictureView.size = profilePictureSize
|
||||
profilePictureView.update(
|
||||
publicKey: getUserHexEncodedPublicKey(),
|
||||
profile: Profile.fetchOrCreateCurrentUser(),
|
||||
threadVariant: .contact
|
||||
)
|
||||
profilePictureView.set(.width, to: profilePictureSize)
|
||||
profilePictureView.set(.height, to: profilePictureSize)
|
||||
|
||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(openSettings))
|
||||
profilePictureView.addGestureRecognizer(tapGestureRecognizer)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
class MessageRequestsCell: UITableViewCell {
|
||||
static let reuseIdentifier = "MessageRequestsCell"
|
||||
|
@ -29,7 +30,7 @@ class MessageRequestsCell: UITableViewCell {
|
|||
result.translatesAutoresizingMaskIntoConstraints = false
|
||||
result.clipsToBounds = true
|
||||
result.themeBackgroundColor = .conversationButton_unreadBubbleBackground
|
||||
result.layer.cornerRadius = (Values.mediumProfilePictureSize / 2)
|
||||
result.layer.cornerRadius = (ProfilePictureView.Size.list.viewSize / 2)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
@ -100,8 +101,8 @@ class MessageRequestsCell: UITableViewCell {
|
|||
constant: (Values.accentLineThickness + Values.mediumSpacing)
|
||||
),
|
||||
iconContainerView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
|
||||
iconContainerView.widthAnchor.constraint(equalToConstant: Values.mediumProfilePictureSize),
|
||||
iconContainerView.heightAnchor.constraint(equalToConstant: Values.mediumProfilePictureSize),
|
||||
iconContainerView.widthAnchor.constraint(equalToConstant: ProfilePictureView.Size.list.viewSize),
|
||||
iconContainerView.heightAnchor.constraint(equalToConstant: ProfilePictureView.Size.list.viewSize),
|
||||
|
||||
iconImageView.centerXAnchor.constraint(equalTo: iconContainerView.centerXAnchor),
|
||||
iconImageView.centerYAnchor.constraint(equalTo: iconContainerView.centerYAnchor),
|
||||
|
|
|
@ -8,7 +8,7 @@ import SignalUtilitiesKit
|
|||
class BlockedContactCell: UITableViewCell {
|
||||
// MARK: - Components
|
||||
|
||||
private lazy var profilePictureView: ProfilePictureView = ProfilePictureView()
|
||||
private lazy var profilePictureView: ProfilePictureView = ProfilePictureView(size: .list)
|
||||
|
||||
private let selectionView: RadioButton = {
|
||||
let result: RadioButton = RadioButton(size: .medium)
|
||||
|
@ -61,9 +61,6 @@ class BlockedContactCell: UITableViewCell {
|
|||
.constraint(lessThanOrEqualTo: contentView.bottomAnchor, constant: -Values.mediumSpacing)
|
||||
.isActive = true
|
||||
profilePictureView.pin(.left, to: .left, of: contentView, withInset: Values.veryLargeSpacing)
|
||||
profilePictureView.set(.width, to: Values.mediumProfilePictureSize)
|
||||
profilePictureView.set(.height, to: Values.mediumProfilePictureSize)
|
||||
profilePictureView.size = Values.mediumProfilePictureSize
|
||||
|
||||
selectionView.center(.vertical, in: contentView)
|
||||
selectionView.topAnchor
|
||||
|
|
|
@ -18,7 +18,7 @@ public final class FullConversationCell: UITableViewCell {
|
|||
|
||||
private let accentLineView: UIView = UIView()
|
||||
|
||||
private lazy var profilePictureView: ProfilePictureView = ProfilePictureView()
|
||||
private lazy var profilePictureView: ProfilePictureView = ProfilePictureView(size: .list)
|
||||
|
||||
private lazy var displayNameLabel: UILabel = {
|
||||
let result: UILabel = UILabel()
|
||||
|
@ -159,12 +159,6 @@ public final class FullConversationCell: UITableViewCell {
|
|||
accentLineView.set(.width, to: Values.accentLineThickness)
|
||||
accentLineView.set(.height, to: cellHeight)
|
||||
|
||||
// Profile picture view
|
||||
let profilePictureViewSize = Values.mediumProfilePictureSize
|
||||
profilePictureView.set(.width, to: profilePictureViewSize)
|
||||
profilePictureView.set(.height, to: profilePictureViewSize)
|
||||
profilePictureView.size = profilePictureViewSize
|
||||
|
||||
// Unread count view
|
||||
unreadCountView.addSubview(unreadCountLabel)
|
||||
unreadCountLabel.setCompressionResistanceHigh()
|
||||
|
@ -640,7 +634,7 @@ public final class FullConversationCell: UITableViewCell {
|
|||
// This method determines if the content is probably too long and returns the truncated or untruncated
|
||||
// content accordingly
|
||||
func truncatingIfNeeded(approxWidth: CGFloat, content: NSAttributedString) -> NSAttributedString {
|
||||
let approxFullWidth: CGFloat = (approxWidth + profilePictureView.size + (Values.mediumSpacing * 3))
|
||||
let approxFullWidth: CGFloat = (approxWidth + profilePictureView.size.viewSize + (Values.mediumSpacing * 3))
|
||||
|
||||
guard ((bounds.width - approxFullWidth) < 0) else { return content }
|
||||
|
||||
|
|
|
@ -51,11 +51,10 @@ class SessionAvatarCell: UITableViewCell {
|
|||
}()
|
||||
|
||||
fileprivate let profilePictureView: ProfilePictureView = {
|
||||
let view: ProfilePictureView = ProfilePictureView()
|
||||
let view: ProfilePictureView = ProfilePictureView(size: .hero)
|
||||
view.accessibilityLabel = "Profile picture"
|
||||
view.isAccessibilityElement = true
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.size = Values.largeProfilePictureSize
|
||||
|
||||
return view
|
||||
}()
|
||||
|
@ -148,9 +147,6 @@ class SessionAvatarCell: UITableViewCell {
|
|||
private func setupLayout() {
|
||||
stackView.pin(to: contentView)
|
||||
|
||||
profilePictureView.set(.width, to: profilePictureView.size)
|
||||
profilePictureView.set(.height, to: profilePictureView.size)
|
||||
|
||||
displayNameLabel.pin(to: displayNameContainer)
|
||||
displayNameTextField.center(in: displayNameContainer)
|
||||
displayNameTextField.widthAnchor
|
||||
|
|
|
@ -141,12 +141,8 @@ extension SessionCell {
|
|||
}()
|
||||
|
||||
private lazy var profilePictureView: ProfilePictureView = {
|
||||
let result: ProfilePictureView = ProfilePictureView()
|
||||
result.translatesAutoresizingMaskIntoConstraints = false
|
||||
result.size = Values.smallProfilePictureSize
|
||||
let result: ProfilePictureView = ProfilePictureView(size: .list)
|
||||
result.isHidden = true
|
||||
result.set(.width, to: Values.smallProfilePictureSize)
|
||||
result.set(.height, to: Values.smallProfilePictureSize)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
|
|
@ -39,7 +39,7 @@ final class SimplifiedConversationCell: UITableViewCell {
|
|||
}()
|
||||
|
||||
private lazy var profilePictureView: ProfilePictureView = {
|
||||
let view: ProfilePictureView = ProfilePictureView()
|
||||
let view: ProfilePictureView = ProfilePictureView(size: .list)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
return view
|
||||
|
@ -79,10 +79,6 @@ final class SimplifiedConversationCell: UITableViewCell {
|
|||
accentLineView.set(.width, to: Values.accentLineThickness)
|
||||
accentLineView.set(.height, to: 68)
|
||||
|
||||
profilePictureView.set(.width, to: Values.mediumProfilePictureSize)
|
||||
profilePictureView.set(.height, to: Values.mediumProfilePictureSize)
|
||||
profilePictureView.size = Values.mediumProfilePictureSize
|
||||
|
||||
stackView.pin(to: self)
|
||||
}
|
||||
|
||||
|
|
|
@ -108,6 +108,11 @@ internal enum Theme_ClassicDark: ThemeColors {
|
|||
.reactions_contextMoreBackground: .classicDark1,
|
||||
|
||||
// NewConversation
|
||||
.newConversation_background: .classicDark1
|
||||
.newConversation_background: .classicDark1,
|
||||
|
||||
// Profile
|
||||
.profileIcon: .primary,
|
||||
.profileIcon_greenPrimaryColor: .black,
|
||||
.profileIcon_background: .white
|
||||
]
|
||||
}
|
||||
|
|
|
@ -108,6 +108,11 @@ internal enum Theme_ClassicLight: ThemeColors {
|
|||
.reactions_contextMoreBackground: .classicLight6,
|
||||
|
||||
// NewConversation
|
||||
.newConversation_background: .classicLight6
|
||||
.newConversation_background: .classicLight6,
|
||||
|
||||
// Profile
|
||||
.profileIcon: .primary,
|
||||
.profileIcon_greenPrimaryColor: .primary,
|
||||
.profileIcon_background: .black
|
||||
]
|
||||
}
|
||||
|
|
|
@ -108,6 +108,11 @@ internal enum Theme_OceanDark: ThemeColors {
|
|||
.reactions_contextMoreBackground: .oceanDark2,
|
||||
|
||||
// NewConversation
|
||||
.newConversation_background: .oceanDark3
|
||||
.newConversation_background: .oceanDark3,
|
||||
|
||||
// Profile
|
||||
.profileIcon: .primary,
|
||||
.profileIcon_greenPrimaryColor: .black,
|
||||
.profileIcon_background: .white
|
||||
]
|
||||
}
|
||||
|
|
|
@ -108,6 +108,11 @@ internal enum Theme_OceanLight: ThemeColors {
|
|||
.reactions_contextMoreBackground: .oceanLight6,
|
||||
|
||||
// NewConversation
|
||||
.newConversation_background: .oceanLight7
|
||||
.newConversation_background: .oceanLight7,
|
||||
|
||||
// Profile
|
||||
.profileIcon: .primary,
|
||||
.profileIcon_greenPrimaryColor: .primary,
|
||||
.profileIcon_background: .oceanLight1
|
||||
]
|
||||
}
|
||||
|
|
|
@ -197,6 +197,11 @@ public indirect enum ThemeValue: Hashable {
|
|||
|
||||
// NewConversation
|
||||
case newConversation_background
|
||||
|
||||
// Profile
|
||||
case profileIcon
|
||||
case profileIcon_greenPrimaryColor
|
||||
case profileIcon_background
|
||||
}
|
||||
|
||||
// MARK: - ForcedThemeValue
|
||||
|
|
|
@ -25,11 +25,6 @@ public final class Values : NSObject {
|
|||
|
||||
@objc public static let accentLineThickness = CGFloat(4)
|
||||
|
||||
@objc public static let verySmallProfilePictureSize = CGFloat(26)
|
||||
@objc public static let smallProfilePictureSize = CGFloat(33)
|
||||
@objc public static let mediumProfilePictureSize = CGFloat(45)
|
||||
@objc public static let largeProfilePictureSize = CGFloat(75)
|
||||
|
||||
@objc public static let searchBarHeight = CGFloat(36)
|
||||
|
||||
@objc public static var separatorThickness: CGFloat { return 1 / UIScreen.main.scale }
|
||||
|
|
|
@ -7,14 +7,107 @@ import SessionUIKit
|
|||
import SessionMessagingKit
|
||||
|
||||
public final class ProfilePictureView: UIView {
|
||||
private var hasTappableProfilePicture: Bool = false
|
||||
public var size: CGFloat = 0
|
||||
public enum Size {
|
||||
case navigation
|
||||
case message
|
||||
case list
|
||||
case hero
|
||||
|
||||
public var viewSize: CGFloat {
|
||||
switch self {
|
||||
case .navigation, .message: return 26
|
||||
case .list: return 46
|
||||
case .hero: return 110
|
||||
}
|
||||
}
|
||||
|
||||
var imageSize: CGFloat {
|
||||
switch self {
|
||||
case .navigation, .message: return 26
|
||||
case .list: return 46
|
||||
case .hero: return 80
|
||||
}
|
||||
}
|
||||
|
||||
var multiImageSize: CGFloat {
|
||||
switch self {
|
||||
case .navigation, .message: return 18 // Shouldn't be used
|
||||
case .list: return 32
|
||||
case .hero: return 80
|
||||
}
|
||||
}
|
||||
|
||||
var iconSize: CGFloat {
|
||||
switch self {
|
||||
case .navigation, .message: return 8
|
||||
case .list: return 16
|
||||
case .hero: return 24
|
||||
}
|
||||
}
|
||||
|
||||
var iconVerticalInset: CGFloat {
|
||||
switch self {
|
||||
case .navigation, .message: return 1
|
||||
case .list: return 3
|
||||
case .hero: return 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constraints
|
||||
public enum ProfileIcon {
|
||||
case none
|
||||
case crown
|
||||
case rightPlus
|
||||
}
|
||||
|
||||
public var size: Size {
|
||||
didSet {
|
||||
widthConstraint.constant = (customWidth ?? size.viewSize)
|
||||
heightConstraint.constant = size.viewSize
|
||||
profileIconTopConstraint.constant = size.iconVerticalInset
|
||||
profileIconBottomConstraint.constant = -size.iconVerticalInset
|
||||
profileIconBackgroundWidthConstraint.constant = size.iconSize
|
||||
profileIconBackgroundHeightConstraint.constant = size.iconSize
|
||||
additionalProfileIconTopConstraint.constant = size.iconVerticalInset
|
||||
additionalProfileIconBottomConstraint.constant = -size.iconVerticalInset
|
||||
additionalProfileIconBackgroundWidthConstraint.constant = size.iconSize
|
||||
additionalProfileIconBackgroundHeightConstraint.constant = size.iconSize
|
||||
|
||||
profileIconBackgroundView.layer.cornerRadius = (size.iconSize / 2)
|
||||
additionalProfileIconBackgroundView.layer.cornerRadius = (size.iconSize / 2)
|
||||
}
|
||||
}
|
||||
public var customWidth: CGFloat? {
|
||||
didSet {
|
||||
self.widthConstraint.constant = (customWidth ?? self.size.viewSize)
|
||||
}
|
||||
}
|
||||
private var hasTappableProfilePicture: Bool = false
|
||||
|
||||
// MARK: - Constraints
|
||||
|
||||
private var widthConstraint: NSLayoutConstraint!
|
||||
private var heightConstraint: NSLayoutConstraint!
|
||||
private var imageViewTopConstraint: NSLayoutConstraint!
|
||||
private var imageViewLeadingConstraint: NSLayoutConstraint!
|
||||
private var imageViewCenterXConstraint: NSLayoutConstraint!
|
||||
private var imageViewCenterYConstraint: NSLayoutConstraint!
|
||||
private var imageViewWidthConstraint: NSLayoutConstraint!
|
||||
private var imageViewHeightConstraint: NSLayoutConstraint!
|
||||
private var additionalImageViewWidthConstraint: NSLayoutConstraint!
|
||||
private var additionalImageViewHeightConstraint: NSLayoutConstraint!
|
||||
private var profileIconTopConstraint: NSLayoutConstraint!
|
||||
private var profileIconBottomConstraint: NSLayoutConstraint!
|
||||
private var profileIconBackgroundLeftAlignConstraint: NSLayoutConstraint!
|
||||
private var profileIconBackgroundRightAlignConstraint: NSLayoutConstraint!
|
||||
private var profileIconBackgroundWidthConstraint: NSLayoutConstraint!
|
||||
private var profileIconBackgroundHeightConstraint: NSLayoutConstraint!
|
||||
private var additionalProfileIconTopConstraint: NSLayoutConstraint!
|
||||
private var additionalProfileIconBottomConstraint: NSLayoutConstraint!
|
||||
private var additionalProfileIconBackgroundLeftAlignConstraint: NSLayoutConstraint!
|
||||
private var additionalProfileIconBackgroundRightAlignConstraint: NSLayoutConstraint!
|
||||
private var additionalProfileIconBackgroundWidthConstraint: NSLayoutConstraint!
|
||||
private var additionalProfileIconBackgroundHeightConstraint: NSLayoutConstraint!
|
||||
|
||||
// MARK: - Components
|
||||
|
||||
|
@ -51,7 +144,6 @@ public final class ProfilePictureView: UIView {
|
|||
result.clipsToBounds = true
|
||||
result.themeBackgroundColor = .primary
|
||||
result.themeBorderColor = .backgroundPrimary
|
||||
result.layer.cornerRadius = (Values.smallProfilePictureSize / 2)
|
||||
result.isHidden = true
|
||||
|
||||
return result
|
||||
|
@ -88,33 +180,72 @@ public final class ProfilePictureView: UIView {
|
|||
return result
|
||||
}()
|
||||
|
||||
private lazy var profileIconBackgroundView: UIView = {
|
||||
let result: UIView = UIView()
|
||||
result.isHidden = true
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var profileIconImageView: UIImageView = {
|
||||
let result: UIImageView = UIImageView()
|
||||
result.contentMode = .scaleAspectFit
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var additionalProfileIconBackgroundView: UIView = {
|
||||
let result: UIView = UIView()
|
||||
result.isHidden = true
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var additionalProfileIconImageView: UIImageView = {
|
||||
let result: UIImageView = UIImageView()
|
||||
result.contentMode = .scaleAspectFit
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
public init(size: Size) {
|
||||
self.size = size
|
||||
|
||||
super.init(frame: CGRect(x: 0, y: 0, width: size.viewSize, height: size.viewSize))
|
||||
|
||||
setUpViewHierarchy()
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
setUpViewHierarchy()
|
||||
preconditionFailure("Use init(size:) instead.")
|
||||
}
|
||||
|
||||
private func setUpViewHierarchy() {
|
||||
let imageViewSize = CGFloat(Values.mediumProfilePictureSize)
|
||||
let additionalImageViewSize = CGFloat(Values.smallProfilePictureSize)
|
||||
|
||||
addSubview(imageContainerView)
|
||||
addSubview(profileIconBackgroundView)
|
||||
addSubview(additionalImageContainerView)
|
||||
addSubview(additionalProfileIconBackgroundView)
|
||||
|
||||
imageContainerView.pin(.leading, to: .leading, of: self)
|
||||
imageContainerView.pin(.top, to: .top, of: self)
|
||||
imageViewWidthConstraint = imageContainerView.set(.width, to: imageViewSize)
|
||||
imageViewHeightConstraint = imageContainerView.set(.height, to: imageViewSize)
|
||||
profileIconBackgroundView.addSubview(profileIconImageView)
|
||||
additionalProfileIconBackgroundView.addSubview(additionalProfileIconImageView)
|
||||
|
||||
widthConstraint = self.set(.width, to: self.size.viewSize)
|
||||
heightConstraint = self.set(.height, to: self.size.viewSize)
|
||||
|
||||
imageViewTopConstraint = imageContainerView.pin(.top, to: .top, of: self)
|
||||
imageViewLeadingConstraint = imageContainerView.pin(.leading, to: .leading, of: self)
|
||||
imageViewCenterXConstraint = imageContainerView.center(.horizontal, in: self)
|
||||
imageViewCenterXConstraint.isActive = false
|
||||
imageViewCenterYConstraint = imageContainerView.center(.vertical, in: self)
|
||||
imageViewCenterYConstraint.isActive = false
|
||||
imageViewWidthConstraint = imageContainerView.set(.width, to: size.imageSize)
|
||||
imageViewHeightConstraint = imageContainerView.set(.height, to: size.imageSize)
|
||||
additionalImageContainerView.pin(.trailing, to: .trailing, of: self)
|
||||
additionalImageContainerView.pin(.bottom, to: .bottom, of: self)
|
||||
additionalImageViewWidthConstraint = additionalImageContainerView.set(.width, to: additionalImageViewSize)
|
||||
additionalImageViewHeightConstraint = additionalImageContainerView.set(.height, to: additionalImageViewSize)
|
||||
additionalImageViewWidthConstraint = additionalImageContainerView.set(.width, to: size.multiImageSize)
|
||||
additionalImageViewHeightConstraint = additionalImageContainerView.set(.height, to: size.multiImageSize)
|
||||
|
||||
imageContainerView.addSubview(imageView)
|
||||
imageContainerView.addSubview(animatedImageView)
|
||||
|
@ -131,32 +262,137 @@ public final class ProfilePictureView: UIView {
|
|||
additionalProfilePlaceholderImageView.pin(.left, to: .left, of: additionalImageContainerView)
|
||||
additionalProfilePlaceholderImageView.pin(.right, to: .right, of: additionalImageContainerView)
|
||||
additionalProfilePlaceholderImageView.pin(.bottom, to: .bottom, of: additionalImageContainerView, withInset: 5)
|
||||
|
||||
profileIconTopConstraint = profileIconImageView.pin(
|
||||
.top,
|
||||
to: .top,
|
||||
of: profileIconBackgroundView,
|
||||
withInset: size.iconVerticalInset
|
||||
)
|
||||
profileIconImageView.pin(.left, to: .left, of: profileIconBackgroundView)
|
||||
profileIconImageView.pin(.right, to: .right, of: profileIconBackgroundView)
|
||||
profileIconBottomConstraint = profileIconImageView.pin(
|
||||
.bottom,
|
||||
to: .bottom,
|
||||
of: profileIconBackgroundView,
|
||||
withInset: -size.iconVerticalInset
|
||||
)
|
||||
profileIconBackgroundLeftAlignConstraint = profileIconBackgroundView.pin(.leading, to: .leading, of: imageContainerView)
|
||||
profileIconBackgroundRightAlignConstraint = profileIconBackgroundView.pin(.trailing, to: .trailing, of: imageContainerView)
|
||||
profileIconBackgroundView.pin(.bottom, to: .bottom, of: imageContainerView)
|
||||
profileIconBackgroundWidthConstraint = profileIconBackgroundView.set(.width, to: size.iconSize)
|
||||
profileIconBackgroundHeightConstraint = profileIconBackgroundView.set(.height, to: size.iconSize)
|
||||
profileIconBackgroundLeftAlignConstraint.isActive = false
|
||||
profileIconBackgroundRightAlignConstraint.isActive = false
|
||||
|
||||
additionalProfileIconTopConstraint = additionalProfileIconImageView.pin(
|
||||
.top,
|
||||
to: .top,
|
||||
of: additionalProfileIconBackgroundView,
|
||||
withInset: size.iconVerticalInset
|
||||
)
|
||||
additionalProfileIconImageView.pin(.left, to: .left, of: additionalProfileIconBackgroundView)
|
||||
additionalProfileIconImageView.pin(.right, to: .right, of: additionalProfileIconBackgroundView)
|
||||
additionalProfileIconBottomConstraint = additionalProfileIconImageView.pin(
|
||||
.bottom,
|
||||
to: .bottom,
|
||||
of: additionalProfileIconBackgroundView,
|
||||
withInset: -size.iconVerticalInset
|
||||
)
|
||||
additionalProfileIconBackgroundLeftAlignConstraint = additionalProfileIconBackgroundView.pin(.leading, to: .leading, of: additionalImageContainerView)
|
||||
additionalProfileIconBackgroundRightAlignConstraint = additionalProfileIconBackgroundView.pin(.trailing, to: .trailing, of: additionalImageContainerView)
|
||||
additionalProfileIconBackgroundView.pin(.bottom, to: .bottom, of: additionalImageContainerView)
|
||||
additionalProfileIconBackgroundWidthConstraint = additionalProfileIconBackgroundView.set(.width, to: size.iconSize)
|
||||
additionalProfileIconBackgroundHeightConstraint = additionalProfileIconBackgroundView.set(.height, to: size.iconSize)
|
||||
additionalProfileIconBackgroundLeftAlignConstraint.isActive = false
|
||||
additionalProfileIconBackgroundRightAlignConstraint.isActive = false
|
||||
}
|
||||
|
||||
// MARK: - Content
|
||||
|
||||
private func updateIconView(
|
||||
icon: ProfileIcon,
|
||||
imageView: UIImageView,
|
||||
backgroundView: UIView,
|
||||
leftAlignConstraint: NSLayoutConstraint,
|
||||
rightAlignConstraint: NSLayoutConstraint
|
||||
) {
|
||||
backgroundView.isHidden = (icon == .none)
|
||||
leftAlignConstraint.isActive = (
|
||||
icon == .none ||
|
||||
icon == .crown
|
||||
)
|
||||
rightAlignConstraint.isActive = (
|
||||
icon == .rightPlus
|
||||
)
|
||||
|
||||
switch icon {
|
||||
case .none: imageView.image = nil
|
||||
|
||||
case .crown:
|
||||
imageView.image = UIImage(systemName: "crown.fill")
|
||||
backgroundView.themeBackgroundColor = .profileIcon_background
|
||||
|
||||
ThemeManager.onThemeChange(observer: imageView) { [weak imageView] _, primaryColor in
|
||||
let targetColor: ThemeValue = (primaryColor == .green ?
|
||||
.profileIcon_greenPrimaryColor :
|
||||
.profileIcon
|
||||
)
|
||||
|
||||
guard imageView?.themeTintColor != targetColor else { return }
|
||||
|
||||
imageView?.themeTintColor = targetColor
|
||||
}
|
||||
|
||||
case .rightPlus:
|
||||
imageView.image = UIImage(systemName: "plus")
|
||||
imageView.themeTintColor = .black
|
||||
backgroundView.themeBackgroundColor = .primary
|
||||
}
|
||||
}
|
||||
|
||||
public 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
|
||||
) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
// Sort out the profile icon first
|
||||
updateIconView(
|
||||
icon: icon,
|
||||
imageView: profileIconImageView,
|
||||
backgroundView: profileIconBackgroundView,
|
||||
leftAlignConstraint: profileIconBackgroundLeftAlignConstraint,
|
||||
rightAlignConstraint: profileIconBackgroundRightAlignConstraint
|
||||
)
|
||||
|
||||
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")
|
||||
case .navigation, .message: imageView.image = #imageLiteral(resourceName: "SessionWhite16")
|
||||
case .list: imageView.image = #imageLiteral(resourceName: "SessionWhite24")
|
||||
case .hero: imageView.image = #imageLiteral(resourceName: "SessionWhite40")
|
||||
}
|
||||
|
||||
imageView.contentMode = .center
|
||||
imageView.isHidden = false
|
||||
animatedImageView.isHidden = true
|
||||
imageContainerView.themeBackgroundColorForced = .theme(.classicDark, color: .borderSeparator)
|
||||
imageContainerView.layer.cornerRadius = (self.size / 2)
|
||||
imageViewWidthConstraint.constant = self.size
|
||||
imageViewHeightConstraint.constant = self.size
|
||||
imageContainerView.layer.cornerRadius = (self.size.imageSize / 2)
|
||||
imageViewWidthConstraint.constant = self.size.imageSize
|
||||
imageViewHeightConstraint.constant = self.size.imageSize
|
||||
profileIconBackgroundWidthConstraint.constant = self.size.iconSize
|
||||
profileIconBackgroundHeightConstraint.constant = self.size.iconSize
|
||||
profileIconBackgroundView.layer.cornerRadius = (self.size.iconSize / 2)
|
||||
additionalProfileIconBackgroundWidthConstraint.constant = self.size.iconSize
|
||||
additionalProfileIconBackgroundHeightConstraint.constant = self.size.iconSize
|
||||
additionalProfileIconBackgroundView.layer.cornerRadius = (self.size.iconSize / 2)
|
||||
additionalImageContainerView.isHidden = true
|
||||
animatedImageView.image = nil
|
||||
additionalImageView.image = nil
|
||||
|
@ -203,29 +439,29 @@ public final class ProfilePictureView: UIView {
|
|||
|
||||
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
|
||||
targetSize = self.size.multiImageSize
|
||||
additionalImageContainerView.isHidden = false
|
||||
imageViewTopConstraint.isActive = true
|
||||
imageViewLeadingConstraint.isActive = true
|
||||
imageViewCenterXConstraint.isActive = false
|
||||
imageViewCenterYConstraint.isActive = false
|
||||
|
||||
// Sort out the additinoal profile icon if needed
|
||||
updateIconView(
|
||||
icon: additionalIcon,
|
||||
imageView: additionalProfileIconImageView,
|
||||
backgroundView: additionalProfileIconBackgroundView,
|
||||
leftAlignConstraint: additionalProfileIconBackgroundLeftAlignConstraint,
|
||||
rightAlignConstraint: additionalProfileIconBackgroundRightAlignConstraint
|
||||
)
|
||||
|
||||
if let additionalProfile: Profile = additionalProfile {
|
||||
let (image, animatedImage, _): (UIImage?, YYImage?, Bool) = getProfilePicture(
|
||||
of: targetSize,
|
||||
of: self.size.multiImageSize,
|
||||
for: additionalProfile.id,
|
||||
profile: additionalProfile
|
||||
)
|
||||
|
||||
|
||||
// Set the images and show the appropriate imageView (non-animated should be
|
||||
// visible if there is no image)
|
||||
additionalImageView.image = image
|
||||
|
@ -241,15 +477,19 @@ public final class ProfilePictureView: UIView {
|
|||
}
|
||||
|
||||
default:
|
||||
targetSize = self.size
|
||||
imageViewWidthConstraint.constant = targetSize
|
||||
imageViewHeightConstraint.constant = targetSize
|
||||
targetSize = self.size.imageSize
|
||||
|
||||
additionalImageContainerView.isHidden = true
|
||||
additionalProfileIconBackgroundView.isHidden = true
|
||||
additionalImageView.image = nil
|
||||
additionalImageView.isHidden = true
|
||||
additionalAnimatedImageView.image = nil
|
||||
additionalAnimatedImageView.isHidden = true
|
||||
additionalProfilePlaceholderImageView.isHidden = true
|
||||
imageViewTopConstraint.isActive = false
|
||||
imageViewLeadingConstraint.isActive = false
|
||||
imageViewCenterXConstraint.isActive = true
|
||||
imageViewCenterYConstraint.isActive = true
|
||||
}
|
||||
|
||||
// Set the image
|
||||
|
@ -287,8 +527,14 @@ public final class ProfilePictureView: UIView {
|
|||
imageView.contentMode = .scaleAspectFill
|
||||
animatedImageView.contentMode = .scaleAspectFill
|
||||
imageContainerView.themeBackgroundColor = .backgroundSecondary
|
||||
imageViewWidthConstraint.constant = targetSize
|
||||
imageViewHeightConstraint.constant = targetSize
|
||||
imageContainerView.layer.cornerRadius = (targetSize / 2)
|
||||
additionalImageViewWidthConstraint.constant = targetSize
|
||||
additionalImageViewHeightConstraint.constant = targetSize
|
||||
additionalImageContainerView.layer.cornerRadius = (targetSize / 2)
|
||||
profileIconBackgroundView.layer.cornerRadius = (size.iconSize / 2)
|
||||
additionalProfileIconBackgroundView.layer.cornerRadius = (size.iconSize / 2)
|
||||
}
|
||||
|
||||
// MARK: - Convenience
|
||||
|
|
Loading…
Reference in New Issue