// // Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import SignalServiceKit import SignalUI public class CVContactShareView: ManualStackView { struct State: Equatable { let contactShare: ContactShareViewModel let isIncoming: Bool let conversationStyle: ConversationStyle let avatar: UIImage? } private let labelStack = ManualStackView(name: "CVContactShareView.labelStack") private let avatarView: AvatarImageView = AvatarImageView(shouldDeactivateConstraints: true) private let contactNameLabel = CVLabel() private let disclosureImageView = CVImageView() static func buildState( contactShare: ContactShareViewModel, isIncoming: Bool, conversationStyle: ConversationStyle, transaction: SDSAnyReadTransaction ) -> State { let avatar = contactShare.getAvatarImage(diameter: Self.avatarSize, transaction: transaction) owsAssertDebug(avatar != nil) return State(contactShare: contactShare, isIncoming: isIncoming, conversationStyle: conversationStyle, avatar: avatar) } private static var avatarSize: CGFloat { CGFloat(AvatarBuilder.standardAvatarSizePoints) } private static let disclosureIconSize = CGSize.square(20) func configureForRendering(state: State, cellMeasurement: CVCellMeasurement) { let labelConfig = Self.contactNameLabelConfig(state: state) labelConfig.applyForRendering(label: contactNameLabel) avatarView.image = state.avatar let disclosureColor = state.isIncoming ? UIColor.ows_gray25 : UIColor.ows_whiteAlpha80 disclosureImageView.setTemplateImage(UIImage(imageLiteralResourceName: "chevron-right-20"), tintColor: disclosureColor) self.configure(config: Self.outerStackConfig, cellMeasurement: cellMeasurement, measurementKey: Self.measurementKey_outerStack, subviews: [ avatarView, contactNameLabel, disclosureImageView ]) } private static var outerStackConfig: CVStackViewConfig { CVStackViewConfig(axis: .horizontal, alignment: .center, spacing: 12, layoutMargins: UIEdgeInsets(hMargin: 0, vMargin: 4)) } private static func contactNameLabelConfig(state: State) -> CVLabelConfig { let textColor = state.conversationStyle.bubbleTextColor(isIncoming: state.isIncoming) return CVLabelConfig.unstyledText( state.contactShare.displayName, font: .dynamicTypeBody, textColor: textColor, lineBreakMode: .byTruncatingTail ) } private static let measurementKey_outerStack = "CVContactShareView.measurementKey_outerStack" static func measure(maxWidth: CGFloat, measurementBuilder: CVCellMeasurement.Builder, state: State) -> CGSize { owsAssertDebug(maxWidth > 0) var maxContentWidth = (maxWidth - (Self.avatarSize + disclosureIconSize.width + outerStackConfig.spacing * 2)) maxContentWidth = max(0, maxContentWidth) let labelConfig = self.contactNameLabelConfig(state: state) let labelSize = CVText.measureLabel(config: labelConfig, maxWidth: maxContentWidth) var outerSubviewInfos = [ManualStackSubviewInfo]() let avatarSize = CGSize(square: Self.avatarSize) outerSubviewInfos.append(avatarSize.asManualSubviewInfo(hasFixedSize: true)) outerSubviewInfos.append(labelSize.asManualSubviewInfo) outerSubviewInfos.append(disclosureIconSize.asManualSubviewInfo(hasFixedSize: true)) let outerStackMeasurement = ManualStackView.measure(config: outerStackConfig, measurementBuilder: measurementBuilder, measurementKey: Self.measurementKey_outerStack, subviewInfos: outerSubviewInfos, maxWidth: maxWidth) return outerStackMeasurement.measuredSize } public override func reset() { super.reset() labelStack.reset() avatarView.image = nil contactNameLabel.text = nil disclosureImageView.image = nil } }