chore: migrate to new version + fixed several critical bugs

- Migrated project to latest Telegram iOS base (v12.3.2+)
- Fixed circular dependency between GhostModeManager and MiscSettingsManager
- Fixed multiple Bazel build configuration errors (select() default conditions)
- Fixed duplicate type definitions in PeerInfoScreen
- Fixed swiftmodule directory resolution in build scripts
- Added Ghostgram Settings tab in main Settings menu with all 5 features
- Cleared sensitive credentials from config.json (template-only now)
- Excluded bazel-cache from version control
This commit is contained in:
ichmagmaus 812
2026-02-23 23:04:32 +01:00
parent 703e291bcb
commit db53826061
1017 changed files with 62337 additions and 40559 deletions
@@ -31,6 +31,8 @@ swift_library(
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
"//submodules/Components/ComponentDisplayAdapters:ComponentDisplayAdapters",
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
"//submodules/TelegramUI/Components/AnimatedTextComponent",
],
visibility = [
"//visibility:public",
File diff suppressed because it is too large Load Diff
@@ -14,7 +14,6 @@ import PeerPresenceStatusManager
import ChatTitleActivityNode
import LocalizedPeerData
import PhoneNumberFormat
import ChatTitleActivityNode
import AnimatedCountLabelNode
import AccountContext
import ComponentFlow
@@ -22,6 +21,8 @@ import EmojiStatusComponent
import AnimationCache
import MultiAnimationRenderer
import ComponentDisplayAdapters
import GlassBackgroundComponent
import AnimatedTextComponent
private let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers])
private let subtitleFont = Font.regular(13.0)
@@ -92,9 +93,26 @@ public enum ChatTitleContent: Equatable {
case replies
}
public struct TitleTextItem: Equatable {
public enum Content: Equatable {
case text(String)
case number(Int, minDigits: Int)
}
public var id: AnyHashable
public var isUnbreakable: Bool
public var content: Content
public init(id: AnyHashable, isUnbreakable: Bool = true, content: Content) {
self.id = id
self.isUnbreakable = isUnbreakable
self.content = content
}
}
case peer(peerView: PeerData, customTitle: String?, customSubtitle: String?, onlineMemberCount: (total: Int32?, recent: Int32?), isScheduledMessages: Bool, isMuted: Bool?, customMessageCount: Int?, isEnabled: Bool)
case replyThread(type: ReplyThreadType, count: Int)
case custom(String, String?, Bool)
case custom(title: [TitleTextItem], subtitle: String?, isEnabled: Bool)
public static func ==(lhs: ChatTitleContent, rhs: ChatTitleContent) -> Bool {
switch lhs {
@@ -144,13 +162,13 @@ public enum ChatTitleContent: Equatable {
}
}
private enum ChatTitleIcon {
enum ChatTitleIcon {
case none
case lock
case mute
}
private enum ChatTitleCredibilityIcon: Equatable {
enum ChatTitleCredibilityIcon: Equatable {
case none
case fake
case scam
@@ -170,7 +188,6 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
private let context: AccountContext
private var theme: PresentationTheme
private var hasEmbeddedTitleContent: Bool = false
private var strings: PresentationStrings
private var dateTimeFormat: PresentationDateTimeFormat
private var nameDisplayOrder: PresentationPersonNameOrder
@@ -178,6 +195,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
private let animationRenderer: MultiAnimationRenderer
private let contentContainer: ASDisplayNode
private let backgroundView: GlassBackgroundView
public let titleContainerView: PortalSourceView
public let titleTextNode: ImmediateAnimatedCountLabelNode
public let titleLeftIconNode: ASImageNode
@@ -192,7 +210,9 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
public var disableAnimations: Bool = false
var manualLayout: Bool = false
private var validLayout: (CGSize, CGRect)?
private var validLayout: CGSize?
public var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
private var titleLeftIcon: ChatTitleIcon = .none
private var titleRightIcon: ChatTitleIcon = .none
@@ -204,7 +224,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
private var pointerInteraction: PointerInteraction?
public var inputActivities: (PeerId, [(Peer, PeerInputActivity)])? {
public var inputActivities: ChatTitleComponent.Activities? {
didSet {
let _ = self.updateStatus()
}
@@ -239,7 +259,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
public var titleContent: ChatTitleContent? {
didSet {
if let titleContent = self.titleContent {
let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme
let titleTheme = self.theme
var segments: [AnimatedCountLabelNode.Segment] = []
var titleLeftIcon: ChatTitleIcon = .none
@@ -394,8 +414,17 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
}
isEnabled = false
case let .custom(text, _, enabled):
segments = [.text(0, NSAttributedString(string: text, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
case let .custom(textItems, _, enabled):
var nextId = -1
segments = textItems.map { item -> AnimatedCountLabelNode.Segment in
nextId += 1
switch item.content {
case let .number(value, _):
return .number(nextId, NSAttributedString(string: "\(value)", font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))
case let .text(text):
return .text(nextId, NSAttributedString(string: text, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))
}
}
isEnabled = enabled
}
@@ -461,8 +490,8 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
if !self.updateStatus(enableAnimation: enableAnimation) {
if updated {
if !self.manualLayout, let (size, clearBounds) = self.validLayout {
let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: (self.disableAnimations || !enableAnimation) ? .immediate : .animated(duration: 0.2, curve: .easeInOut))
if !self.manualLayout, let size = self.validLayout {
let _ = self.updateLayout(availableSize: size, transition: (self.disableAnimations || !enableAnimation) ? .immediate : .animated(duration: 0.2, curve: .easeInOut))
}
}
}
@@ -487,7 +516,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
}
}
let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme
let titleTheme = self.theme
var state = ChatTitleActivityNodeState.none
switch self.networkState {
@@ -505,51 +534,51 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
}
state = .info(NSAttributedString(string: infoText, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor), .generic)
case .online:
if let (peerId, inputActivities) = self.inputActivities, !inputActivities.isEmpty, inputActivitiesAllowed {
if let inputActivities = self.inputActivities, !inputActivities.items.isEmpty, inputActivitiesAllowed {
var stringValue = ""
var mergedActivity = inputActivities[0].1
for (_, activity) in inputActivities {
if activity != mergedActivity {
var mergedActivity = inputActivities.items[0].activity
for item in inputActivities.items {
if item.activity != mergedActivity {
mergedActivity = .typingText
break
}
}
if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat {
if inputActivities.peerId.namespace == Namespaces.Peer.CloudUser || inputActivities.peerId.namespace == Namespaces.Peer.SecretChat {
switch mergedActivity {
case .typingText:
stringValue = strings.Conversation_typing
case .uploadingFile:
stringValue = strings.Activity_UploadingDocument
case .recordingVoice:
stringValue = strings.Activity_RecordingAudio
case .uploadingPhoto:
stringValue = strings.Activity_UploadingPhoto
case .uploadingVideo:
stringValue = strings.Activity_UploadingVideo
case .playingGame:
stringValue = strings.Activity_PlayingGame
case .recordingInstantVideo:
stringValue = strings.Activity_RecordingVideoMessage
case .uploadingInstantVideo:
stringValue = strings.Activity_UploadingVideoMessage
case .choosingSticker:
stringValue = strings.Activity_ChoosingSticker
case let .seeingEmojiInteraction(emoticon):
stringValue = strings.Activity_EnjoyingAnimations(emoticon).string
case .speakingInGroupCall, .interactingWithEmoji:
stringValue = ""
case .typingText:
stringValue = strings.Conversation_typing
case .uploadingFile:
stringValue = strings.Activity_UploadingDocument
case .recordingVoice:
stringValue = strings.Activity_RecordingAudio
case .uploadingPhoto:
stringValue = strings.Activity_UploadingPhoto
case .uploadingVideo:
stringValue = strings.Activity_UploadingVideo
case .playingGame:
stringValue = strings.Activity_PlayingGame
case .recordingInstantVideo:
stringValue = strings.Activity_RecordingVideoMessage
case .uploadingInstantVideo:
stringValue = strings.Activity_UploadingVideoMessage
case .choosingSticker:
stringValue = strings.Activity_ChoosingSticker
case let .seeingEmojiInteraction(emoticon):
stringValue = strings.Activity_EnjoyingAnimations(emoticon).string
case .speakingInGroupCall, .interactingWithEmoji:
stringValue = ""
}
} else {
if inputActivities.count > 1 {
let peerTitle = EnginePeer(inputActivities[0].0).compactDisplayTitle
if inputActivities.count == 2 {
let secondPeerTitle = EnginePeer(inputActivities[1].0).compactDisplayTitle
stringValue = strings.Chat_MultipleTypingPair(peerTitle, secondPeerTitle).string
if inputActivities.items.count > 1 {
let peerTitle = inputActivities.items[0].peer.compactDisplayTitle
if inputActivities.items.count == 2 {
let secondPeerTitle = inputActivities.items[1].peer.compactDisplayTitle
stringValue = self.strings.Chat_MultipleTypingPair(peerTitle, secondPeerTitle).string
} else {
stringValue = strings.Chat_MultipleTypingMore(peerTitle, String(inputActivities.count - 1)).string
stringValue = self.strings.Chat_MultipleTypingMore(peerTitle, String(inputActivities.items.count - 1)).string
}
} else if let (peer, _) = inputActivities.first {
stringValue = EnginePeer(peer).compactDisplayTitle
} else if let item = inputActivities.items.first {
stringValue = item.peer.compactDisplayTitle
}
}
let color = titleTheme.rootController.navigationBar.accentTextColor
@@ -719,8 +748,8 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
}
if self.activityNode.transitionToState(state, animation: enableAnimation ? .slide : .none) {
if !self.manualLayout, let (size, clearBounds) = self.validLayout {
let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: enableAnimation ? .animated(duration: 0.3, curve: .spring) : .immediate)
if !self.manualLayout, let size = self.validLayout {
let _ = self.updateLayout(availableSize: size, transition: enableAnimation ? .animated(duration: 0.3, curve: .spring) : .immediate)
}
return true
} else {
@@ -739,6 +768,8 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
self.contentContainer = ASDisplayNode()
self.backgroundView = GlassBackgroundView()
self.titleContainerView = PortalSourceView()
self.titleTextNode = ImmediateAnimatedCountLabelNode()
@@ -770,6 +801,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
self.accessibilityTraits = .header
self.addSubnode(self.contentContainer)
self.contentContainer.view.addSubview(self.backgroundView)
self.titleContainerView.addSubnode(self.titleTextNode)
self.contentContainer.view.addSubview(self.titleContainerView)
self.contentContainer.addSubnode(self.activityNode)
@@ -813,15 +845,14 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
override public func layoutSubviews() {
super.layoutSubviews()
if !self.manualLayout, let (size, clearBounds) = self.validLayout {
let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate)
if !self.manualLayout, let size = self.validLayout {
let _ = self.updateLayout(availableSize: size, transition: .immediate)
}
}
public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, hasEmbeddedTitleContent: Bool) {
if self.theme !== theme || self.strings !== strings || self.hasEmbeddedTitleContent != hasEmbeddedTitleContent {
public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
if self.theme !== theme || self.strings !== strings {
self.theme = theme
self.hasEmbeddedTitleContent = hasEmbeddedTitleContent
self.strings = strings
let titleContent = self.titleContent
@@ -830,17 +861,19 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
self.titleContent = titleContent
let _ = self.updateStatus()
if !self.manualLayout, let (size, clearBounds) = self.validLayout {
let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate)
if !self.manualLayout, let size = self.validLayout {
let _ = self.updateLayout(availableSize: size, transition: .immediate)
}
}
}
public func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) -> CGRect {
self.validLayout = (size, clearBounds)
public func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
let size = availableSize
self.button.frame = clearBounds
self.contentContainer.frame = clearBounds
self.validLayout = size
self.button.frame = CGRect(origin: CGPoint(), size: size)
self.contentContainer.frame = CGRect(origin: CGPoint(), size: size)
var leftIconWidth: CGFloat = 0.0
var rightIconWidth: CGFloat = 0.0
@@ -986,108 +1019,87 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
}
let statusSpacing: CGFloat = 3.0
let titleSideInset: CGFloat = 6.0
let titleSideInset: CGFloat = 12.0 + 8.0
var titleFrame: CGRect
if size.height > 40.0 {
var titleInsets: UIEdgeInsets = .zero
if case .emojiStatus = self.titleVerifiedIcon, verifiedIconWidth > 0.0 {
titleInsets.left = verifiedIconWidth
}
var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - verifiedIconWidth - statusIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), insets: titleInsets, animated: titleTransition.isAnimated)
titleSize.width += credibilityIconWidth
titleSize.width += verifiedIconWidth
if statusIconWidth > 0.0 {
titleSize.width += statusIconWidth
if credibilityIconWidth > 0.0 {
titleSize.width += statusSpacing
}
}
let activitySize = self.activityNode.updateLayout(CGSize(width: clearBounds.size.width - titleSideInset * 2.0, height: clearBounds.size.height), alignment: .center)
let titleInfoSpacing: CGFloat = 0.0
if activitySize.height.isZero {
titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
if titleFrame.size.width < size.width {
titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0)
}
titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame)
titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
} else {
let combinedHeight = titleSize.height + activitySize.height + titleInfoSpacing
titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize)
if titleFrame.size.width < size.width {
titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0)
}
titleFrame.origin.x = max(titleFrame.origin.x, clearBounds.minX + leftIconWidth)
titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame)
titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
var activityFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - activitySize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0) + titleSize.height + titleInfoSpacing), size: activitySize)
if activitySize.width < size.width {
activityFrame.origin.x = -clearBounds.minX + floor((size.width - activityFrame.width) / 2.0)
}
titleTransition.updateFrameAdditiveToCenter(node: self.activityNode, frame: activityFrame)
}
if let image = self.titleLeftIconNode.image {
titleTransition.updateFrame(node: self.titleLeftIconNode, frame: CGRect(origin: CGPoint(x: -image.size.width - 3.0 - UIScreenPixel, y: 4.0), size: image.size))
}
var nextIconX: CGFloat = titleFrame.width
titleTransition.updateFrame(view: self.titleVerifiedIconView, frame: CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize))
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
nextIconX -= titleCredibilitySize.width
if credibilityIconWidth > 0.0 {
nextIconX -= statusSpacing
}
self.titleStatusIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleStatusSize.width, y: floor((titleFrame.height - titleStatusSize.height) / 2.0)), size: titleStatusSize)
nextIconX -= titleStatusSize.width
if let image = self.titleRightIconNode.image {
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.width + 3.0 + UIScreenPixel, y: 6.0), size: image.size)
var titleInsets: UIEdgeInsets = .zero
if case .emojiStatus = self.titleVerifiedIcon, verifiedIconWidth > 0.0 {
titleInsets.left = verifiedIconWidth
}
var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: size.width - leftIconWidth - credibilityIconWidth - verifiedIconWidth - statusIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), insets: titleInsets, animated: titleTransition.isAnimated)
titleSize.width += credibilityIconWidth
titleSize.width += verifiedIconWidth
if statusIconWidth > 0.0 {
titleSize.width += statusIconWidth
if credibilityIconWidth > 0.0 {
titleSize.width += statusSpacing
}
}
let activitySize = self.activityNode.updateLayout(CGSize(width: size.width - titleSideInset * 2.0, height: size.height), alignment: .center)
let titleInfoSpacing: CGFloat = 0.0
var activityFrame = CGRect()
if activitySize.height.isZero {
titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
if titleFrame.size.width < size.width {
titleFrame.origin.x = floor((size.width - titleFrame.width) / 2.0)
}
titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame)
titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
} else {
let titleSize = self.titleTextNode.updateLayout(size: CGSize(width: floor(clearBounds.width / 2.0 - leftIconWidth - credibilityIconWidth - verifiedIconWidth - statusIconWidth - rightIconWidth - titleSideInset * 2.0), height: size.height), animated: titleTransition.isAnimated)
let activitySize = self.activityNode.updateLayout(CGSize(width: floor(clearBounds.width / 2.0), height: size.height), alignment: .center)
let combinedHeight = titleSize.height + activitySize.height + titleInfoSpacing
let titleInfoSpacing: CGFloat = 8.0
let combinedWidth = titleSize.width + leftIconWidth + credibilityIconWidth + verifiedIconWidth + statusIconWidth + rightIconWidth + activitySize.width + titleInfoSpacing
let contentWidth = max(titleSize.width + rightIconWidth, activitySize.width)
var contentX = floor((size.width - contentWidth) / 2.0)
contentX = max(contentX, 20.0)
titleFrame = CGRect(origin: CGPoint(x: leftIconWidth + floor((clearBounds.width - combinedWidth) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
titleFrame = CGRect(origin: CGPoint(x: contentX + floor((contentWidth - titleSize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize)
titleTransition.updateFrameAdditiveToCenter(view: self.titleContainerView, frame: titleFrame)
titleTransition.updateFrameAdditiveToCenter(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
titleFrame.origin.x = max(titleFrame.origin.x, leftIconWidth)
titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame)
titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
titleTransition.updateFrameAdditiveToCenter(node: self.activityNode, frame: CGRect(origin: CGPoint(x: floor((clearBounds.width - combinedWidth) / 2.0 + titleSize.width + leftIconWidth + credibilityIconWidth + verifiedIconWidth + statusIconWidth + rightIconWidth + titleInfoSpacing), y: floor((size.height - activitySize.height) / 2.0)), size: activitySize))
if let image = self.titleLeftIconNode.image {
self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.minY + 4.0), size: image.size)
}
var nextIconX: CGFloat = titleFrame.maxX
self.titleVerifiedIconView.frame = CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize)
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
nextIconX -= titleCredibilitySize.width
titleTransition.updateFrame(view: self.titleStatusIconView, frame: CGRect(origin: CGPoint(x: nextIconX - titleStatusSize.width, y: floor((titleFrame.height - titleStatusSize.height) / 2.0)), size: titleStatusSize))
nextIconX -= titleStatusSize.width
if let image = self.titleRightIconNode.image {
titleTransition.updateFrame(node: self.titleRightIconNode, frame: CGRect(origin: CGPoint(x: titleFrame.maxX - image.size.width, y: titleFrame.minY + 6.0), size: image.size))
}
activityFrame = CGRect(origin: CGPoint(x: titleFrame.minX + floor((titleFrame.width - activitySize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0) + titleSize.height + titleInfoSpacing), size: activitySize)
titleTransition.updateFrameAdditiveToCenter(node: self.activityNode, frame: activityFrame.offsetBy(dx: activitySize.width * 0.5, dy: 0.0))
}
if let image = self.titleLeftIconNode.image {
titleTransition.updateFrame(node: self.titleLeftIconNode, frame: CGRect(origin: CGPoint(x: -image.size.width - 3.0 - UIScreenPixel, y: 4.0), size: image.size))
}
var nextIconX: CGFloat = titleFrame.width
titleTransition.updateFrame(view: self.titleVerifiedIconView, frame: CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize))
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
nextIconX -= titleCredibilitySize.width
if credibilityIconWidth > 0.0 {
nextIconX -= statusSpacing
}
self.titleStatusIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleStatusSize.width, y: floor((titleFrame.height - titleStatusSize.height) / 2.0)), size: titleStatusSize)
nextIconX -= titleStatusSize.width
if let image = self.titleRightIconNode.image {
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.width + 3.0 + UIScreenPixel, y: 6.0), size: image.size)
}
self.pointerInteraction = PointerInteraction(view: self, style: .rectangle(CGSize(width: titleFrame.width + 16.0, height: 40.0)))
return titleFrame
var backgroundFrame = CGRect(origin: CGPoint(x: titleFrame.minX, y: 6.0), size: CGSize(width: titleFrame.width, height: 44.0))
if !activityFrame.isEmpty {
backgroundFrame.origin.x = min(backgroundFrame.minX, activityFrame.minX)
backgroundFrame.size.width = max(backgroundFrame.maxX, activityFrame.maxX) - backgroundFrame.minX
}
backgroundFrame = backgroundFrame.insetBy(dx: -12.0, dy: 0.0)
let componentTransition = ComponentTransition(transition)
componentTransition.setFrame(view: self.backgroundView, frame: backgroundFrame)
self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: false, transition: componentTransition)
return availableSize
}
@objc private func buttonPressed() {
@@ -1162,122 +1174,3 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -offset.x, y: -offset.y), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
}
}
public final class ChatTitleComponent: Component {
public let context: AccountContext
public let theme: PresentationTheme
public let strings: PresentationStrings
public let dateTimeFormat: PresentationDateTimeFormat
public let nameDisplayOrder: PresentationPersonNameOrder
public let content: ChatTitleContent
public let tapped: () -> Void
public let longTapped: () -> Void
public init(
context: AccountContext,
theme: PresentationTheme,
strings: PresentationStrings,
dateTimeFormat: PresentationDateTimeFormat,
nameDisplayOrder: PresentationPersonNameOrder,
content: ChatTitleContent,
tapped: @escaping () -> Void,
longTapped: @escaping () -> Void
) {
self.context = context
self.theme = theme
self.strings = strings
self.dateTimeFormat = dateTimeFormat
self.nameDisplayOrder = nameDisplayOrder
self.content = content
self.tapped = tapped
self.longTapped = longTapped
}
public static func ==(lhs: ChatTitleComponent, rhs: ChatTitleComponent) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.strings !== rhs.strings {
return false
}
if lhs.dateTimeFormat != rhs.dateTimeFormat {
return false
}
if lhs.nameDisplayOrder != rhs.nameDisplayOrder {
return false
}
if lhs.content != rhs.content {
return false
}
return true
}
public final class View: UIView {
public private(set) var contentView: ChatTitleView?
private var component: ChatTitleComponent?
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(component: ChatTitleComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
self.component = component
let contentView: ChatTitleView
if let current = self.contentView {
contentView = current
} else {
contentView = ChatTitleView(
context: component.context,
theme: component.theme,
strings: component.strings,
dateTimeFormat: component.dateTimeFormat,
nameDisplayOrder: component.nameDisplayOrder,
animationCache: component.context.animationCache,
animationRenderer: component.context.animationRenderer
)
contentView.pressed = { [weak self] in
guard let self else {
return
}
self.component?.tapped()
}
contentView.longPressed = { [weak self] in
guard let self else {
return
}
self.component?.longTapped()
}
contentView.manualLayout = true
self.contentView = contentView
self.addSubview(contentView)
}
if contentView.titleContent != component.content {
contentView.titleContent = component.content
}
contentView.updateThemeAndStrings(theme: component.theme, strings: component.strings, hasEmbeddedTitleContent: false)
let _ = contentView.updateLayout(size: availableSize, clearBounds: CGRect(origin: CGPoint(), size: availableSize), transition: transition.containedViewLayoutTransition)
transition.setFrame(view: contentView, frame: CGRect(origin: CGPoint(), size: availableSize))
return availableSize
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}
@@ -0,0 +1,96 @@
import Foundation
import UIKit
import Display
import ComponentFlow
import TelegramPresentationData
final class TitleIconComponent: Component {
enum Kind {
case mute
case lock
}
let kind: Kind
let color: UIColor
init(
kind: Kind,
color: UIColor
) {
self.kind = kind
self.color = color
}
static func ==(lhs: TitleIconComponent, rhs: TitleIconComponent) -> Bool {
if lhs.kind != rhs.kind {
return false
}
if lhs.color != rhs.color {
return false
}
return true
}
final class View: UIView {
let iconView: UIImageView
var component: TitleIconComponent?
override init(frame: CGRect) {
self.iconView = UIImageView()
super.init(frame: frame)
self.addSubview(self.iconView)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(component: TitleIconComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
if self.component?.kind != component.kind {
switch component.kind {
case .mute:
self.iconView.image = generateImage(CGSize(width: 9.0, height: 9.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.white.cgColor)
let _ = try? drawSvgPath(context, path: "M2.97607626,2.27306995 L5.1424026,0.18411241 C5.25492443,0.0756092198 5.40753677,0.0146527621 5.56666667,0.0146527621 C5.89803752,0.0146527621 6.16666667,0.273688014 6.16666667,0.593224191 L6.16666667,5.47790407 L8.86069303,8.18395735 C9.05193038,8.37604845 9.04547086,8.68126082 8.84626528,8.86566828 C8.6470597,9.05007573 8.33054317,9.0438469 8.13930581,8.85175581 L0.139306972,0.816042647 C-0.0519303838,0.623951552 -0.0454708626,0.318739175 0.153734717,0.134331724 C0.352940296,-0.0500757275 0.669456833,-0.0438469035 0.860694189,0.148244192 L2.97607626,2.27306995 Z M0.933196438,2.75856564 L6.16666667,8.01539958 L6.16666667,8.40677707 C6.16666667,8.56022375 6.10345256,8.70738566 5.99093074,8.81588885 C5.75661616,9.04183505 5.37671717,9.04183505 5.1424026,8.81588885 L2.59763107,6.36200202 C2.53511895,6.30172247 2.45033431,6.26785777 2.36192881,6.26785777 L1.16666667,6.26785777 C0.614381917,6.26785777 0.166666667,5.83613235 0.166666667,5.30357206 L0.166666667,3.6964292 C0.166666667,3.24138962 0.493527341,2.85996592 0.933196438,2.75856564 Z ")
})?.withRenderingMode(.alwaysTemplate)
case .lock:
self.iconView.image = generateImage(CGSize(width: 9.0, height: 13.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.translateBy(x: 0.0, y: 1.0)
context.setFillColor(UIColor.white.cgColor)
context.setStrokeColor(UIColor.white.cgColor)
context.setLineWidth(1.32)
let _ = try? drawSvgPath(context, path: "M4.5,0.600000024 C5.88071187,0.600000024 7,1.88484952 7,3.46979169 L7,7.39687502 C7,8.9818172 5.88071187,10.2666667 4.5,10.2666667 C3.11928813,10.2666667 2,8.9818172 2,7.39687502 L2,3.46979169 C2,1.88484952 3.11928813,0.600000024 4.5,0.600000024 S ")
let _ = try? drawSvgPath(context, path: "M1.32,5.65999985 L7.68,5.65999985 C8.40901587,5.65999985 9,6.25098398 9,6.97999985 L9,10.6733332 C9,11.4023491 8.40901587,11.9933332 7.68,11.9933332 L1.32,11.9933332 C0.59098413,11.9933332 1.11022302e-16,11.4023491 0,10.6733332 L2.22044605e-16,6.97999985 C1.11022302e-16,6.25098398 0.59098413,5.65999985 1.32,5.65999985 Z ")
})?.withRenderingMode(.alwaysTemplate)
}
}
let size = CGSize(width: 14.0, height: 14.0)
if let image = self.iconView.image {
let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - image.size.width) * 0.5), y: floorToScreenPixels((size.height - image.size.height) * 0.5)), size: image.size)
self.iconView.frame = iconFrame
}
self.iconView.tintColor = component.color
return size
}
}
func makeView() -> View {
return View(frame: CGRect())
}
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}