mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-07-07 16:47:48 +02:00
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:
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user