TM-SGNL-iOS/Signal/ConversationView/Components/CVComponentUnreadIndicator.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

234 lines
9.5 KiB
Swift

//
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import SignalServiceKit
public import SignalUI
public class CVComponentUnreadIndicator: CVComponentBase, CVRootComponent {
public var componentKey: CVComponentKey { .unreadIndicator }
public var cellReuseIdentifier: CVCellReuseIdentifier {
CVCellReuseIdentifier.unreadIndicator
}
public let isDedicatedCell = true
override init(itemModel: CVItemModel) {
super.init(itemModel: itemModel)
}
public func configureCellRootComponent(cellView: UIView,
cellMeasurement: CVCellMeasurement,
componentDelegate: CVComponentDelegate,
messageSwipeActionState: CVMessageSwipeActionState,
componentView: CVComponentView) {
Self.configureCellRootComponent(rootComponent: self,
cellView: cellView,
cellMeasurement: cellMeasurement,
componentDelegate: componentDelegate,
componentView: componentView)
}
public func buildComponentView(componentDelegate: CVComponentDelegate) -> CVComponentView {
CVComponentViewUnreadIndicator()
}
public override func wallpaperBlurView(componentView: CVComponentView) -> CVWallpaperBlurView? {
guard let componentView = componentView as? CVComponentViewUnreadIndicator else {
owsFailDebug("Unexpected componentView.")
return nil
}
return componentView.wallpaperBlurView
}
public func configureForRendering(componentView: CVComponentView,
cellMeasurement: CVCellMeasurement,
componentDelegate: CVComponentDelegate) {
guard let componentView = componentView as? CVComponentViewUnreadIndicator else {
owsFailDebug("Unexpected componentView.")
return
}
let themeHasChanged = conversationStyle.isDarkThemeEnabled != componentView.isDarkThemeEnabled
let hasWallpaper = conversationStyle.hasWallpaper
let wallpaperModeHasChanged = hasWallpaper != componentView.hasWallpaper
let isReusing = (componentView.rootView.superview != nil &&
!themeHasChanged &&
!wallpaperModeHasChanged)
if !isReusing {
componentView.reset(resetReusableState: true)
}
componentView.isDarkThemeEnabled = conversationStyle.isDarkThemeEnabled
componentView.hasWallpaper = hasWallpaper
let outerStack = componentView.outerStack
let innerStack = componentView.innerStack
let strokeView = componentView.strokeView
let titleLabel = componentView.titleLabel
titleLabelConfig.applyForRendering(label: titleLabel)
if isReusing {
innerStack.configureForReuse(config: innerStackConfig,
cellMeasurement: cellMeasurement,
measurementKey: Self.measurementKey_innerStack)
outerStack.configureForReuse(config: outerStackConfig,
cellMeasurement: cellMeasurement,
measurementKey: Self.measurementKey_outerStack)
} else {
outerStack.reset()
titleLabel.removeFromSuperview()
componentView.wallpaperBlurView?.removeFromSuperview()
componentView.wallpaperBlurView = nil
innerStack.reset()
innerStack.configure(config: innerStackConfig,
cellMeasurement: cellMeasurement,
measurementKey: Self.measurementKey_innerStack,
subviews: [ titleLabel ])
if hasWallpaper {
strokeView.backgroundColor = .ows_blackAlpha80
let wallpaperBlurView = componentView.ensureWallpaperBlurView()
configureWallpaperBlurView(wallpaperBlurView: wallpaperBlurView,
maskCornerRadius: 8,
componentDelegate: componentDelegate)
innerStack.addSubviewToFillSuperviewEdges(wallpaperBlurView)
} else {
strokeView.backgroundColor = .ows_gray45
}
outerStack.configure(config: outerStackConfig,
cellMeasurement: cellMeasurement,
measurementKey: Self.measurementKey_outerStack,
subviews: [
strokeView,
innerStack
])
}
}
private var titleLabelConfig: CVLabelConfig {
CVLabelConfig.unstyledText(
OWSLocalizedString(
"MESSAGES_VIEW_UNREAD_INDICATOR",
comment: "Indicator that separates read from unread messages."
),
font: UIFont.dynamicTypeFootnote.semibold(),
textColor: Theme.primaryTextColor,
numberOfLines: 0,
lineBreakMode: .byTruncatingTail,
textAlignment: .center
)
}
private var outerStackConfig: CVStackViewConfig {
CVStackViewConfig(axis: .vertical,
alignment: .fill,
spacing: 12,
layoutMargins: UIEdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0))
}
private var innerStackConfig: CVStackViewConfig {
CVStackViewConfig(axis: .vertical,
alignment: .center,
spacing: 0,
layoutMargins: UIEdgeInsets(hMargin: 10, vMargin: 4))
}
private static let measurementKey_outerStack = "CVComponentUnreadIndicator.measurementKey_outerStack"
private static let measurementKey_innerStack = "CVComponentUnreadIndicator.measurementKey_innerStack"
public func measure(maxWidth: CGFloat, measurementBuilder: CVCellMeasurement.Builder) -> CGSize {
owsAssertDebug(maxWidth > 0)
let availableWidth = max(0, maxWidth -
(innerStackConfig.layoutMargins.totalWidth +
outerStackConfig.layoutMargins.totalWidth))
let labelSize = CVText.measureLabel(config: titleLabelConfig, maxWidth: availableWidth)
let strokeSize = CGSize(width: 0, height: 1)
let labelInfo = labelSize.asManualSubviewInfo
let innerStackMeasurement = ManualStackView.measure(config: innerStackConfig,
measurementBuilder: measurementBuilder,
measurementKey: Self.measurementKey_innerStack,
subviewInfos: [ labelInfo ])
let strokeInfo = strokeSize.asManualSubviewInfo(hasFixedHeight: true)
let innerStackInfo = innerStackMeasurement.measuredSize.asManualSubviewInfo(hasFixedWidth: true)
let vStackSubviewInfos = [
strokeInfo,
innerStackInfo
]
let vStackMeasurement = ManualStackView.measure(config: outerStackConfig,
measurementBuilder: measurementBuilder,
measurementKey: Self.measurementKey_outerStack,
subviewInfos: vStackSubviewInfos,
maxWidth: maxWidth)
return vStackMeasurement.measuredSize
}
// MARK: -
// Used for rendering some portion of an Conversation View item.
// It could be the entire item or some part thereof.
public class CVComponentViewUnreadIndicator: NSObject, CVComponentView {
fileprivate let outerStack = ManualStackView(name: "unreadIndicator.outerStack")
fileprivate let innerStack = ManualStackView(name: "unreadIndicator.innerStack")
fileprivate let titleLabel = CVLabel()
fileprivate var wallpaperBlurView: CVWallpaperBlurView?
fileprivate func ensureWallpaperBlurView() -> CVWallpaperBlurView {
if let wallpaperBlurView = self.wallpaperBlurView {
return wallpaperBlurView
}
let wallpaperBlurView = CVWallpaperBlurView()
self.wallpaperBlurView = wallpaperBlurView
return wallpaperBlurView
}
fileprivate var hasWallpaper = false
fileprivate var isDarkThemeEnabled = false
fileprivate let strokeView = UIView()
public var isDedicatedCellView = false
public var rootView: UIView {
outerStack
}
// MARK: -
public func setIsCellVisible(_ isCellVisible: Bool) {}
public func reset() {
reset(resetReusableState: false)
}
public func reset(resetReusableState: Bool) {
owsAssertDebug(isDedicatedCellView)
titleLabel.text = nil
if resetReusableState {
outerStack.reset()
innerStack.reset()
wallpaperBlurView?.removeFromSuperview()
wallpaperBlurView?.resetContentAndConfiguration()
hasWallpaper = false
isDarkThemeEnabled = false
}
}
}
}