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 62323 additions and 40545 deletions
@@ -9,6 +9,16 @@ import SearchUI
import AccountContext
import TelegramCore
import StoryPeerListComponent
import EdgeEffect
import GlassBackgroundComponent
private func searchScrollHeightValue() -> CGFloat {
return 54.0
}
private func storiesHeightValue() -> CGFloat {
return 96.0
}
public final class ChatListNavigationBar: Component {
public final class AnimationHint {
@@ -20,20 +30,37 @@ public final class ChatListNavigationBar: Component {
self.crossfadeStoryPeers = crossfadeStoryPeers
}
}
public struct Search: Equatable {
public var isEnabled: Bool
public init(isEnabled: Bool) {
self.isEnabled = isEnabled
}
}
public struct ActiveSearch: Equatable {
public var isExternal: Bool
public init(isExternal: Bool) {
self.isExternal = isExternal
}
}
public let context: AccountContext
public let theme: PresentationTheme
public let strings: PresentationStrings
public let statusBarHeight: CGFloat
public let sideInset: CGFloat
public let isSearchActive: Bool
public let isSearchEnabled: Bool
public let search: Search?
public let activeSearch: ActiveSearch?
public let primaryContent: ChatListHeaderComponent.Content?
public let secondaryContent: ChatListHeaderComponent.Content?
public let secondaryTransition: CGFloat
public let storySubscriptions: EngineStorySubscriptions?
public let storiesIncludeHidden: Bool
public let uploadProgress: [EnginePeer.Id: Float]
public let headerPanels: AnyComponent<Empty>?
public let tabsNode: ASDisplayNode?
public let tabsNodeIsSearch: Bool
public let accessoryPanelContainer: ASDisplayNode?
@@ -48,14 +75,15 @@ public final class ChatListNavigationBar: Component {
strings: PresentationStrings,
statusBarHeight: CGFloat,
sideInset: CGFloat,
isSearchActive: Bool,
isSearchEnabled: Bool,
search: Search?,
activeSearch: ActiveSearch?,
primaryContent: ChatListHeaderComponent.Content?,
secondaryContent: ChatListHeaderComponent.Content?,
secondaryTransition: CGFloat,
storySubscriptions: EngineStorySubscriptions?,
storiesIncludeHidden: Bool,
uploadProgress: [EnginePeer.Id: Float],
headerPanels: AnyComponent<Empty>?,
tabsNode: ASDisplayNode?,
tabsNodeIsSearch: Bool,
accessoryPanelContainer: ASDisplayNode?,
@@ -69,14 +97,15 @@ public final class ChatListNavigationBar: Component {
self.strings = strings
self.statusBarHeight = statusBarHeight
self.sideInset = sideInset
self.isSearchActive = isSearchActive
self.isSearchEnabled = isSearchEnabled
self.search = search
self.activeSearch = activeSearch
self.primaryContent = primaryContent
self.secondaryContent = secondaryContent
self.secondaryTransition = secondaryTransition
self.storySubscriptions = storySubscriptions
self.storiesIncludeHidden = storiesIncludeHidden
self.uploadProgress = uploadProgress
self.headerPanels = headerPanels
self.tabsNode = tabsNode
self.tabsNodeIsSearch = tabsNodeIsSearch
self.accessoryPanelContainer = accessoryPanelContainer
@@ -102,10 +131,10 @@ public final class ChatListNavigationBar: Component {
if lhs.sideInset != rhs.sideInset {
return false
}
if lhs.isSearchActive != rhs.isSearchActive {
if lhs.search != rhs.search {
return false
}
if lhs.isSearchEnabled != rhs.isSearchEnabled {
if lhs.activeSearch != rhs.activeSearch {
return false
}
if lhs.primaryContent != rhs.primaryContent {
@@ -126,6 +155,9 @@ public final class ChatListNavigationBar: Component {
if lhs.uploadProgress != rhs.uploadProgress {
return false
}
if lhs.headerPanels != rhs.headerPanels {
return false
}
if lhs.tabsNode !== rhs.tabsNode {
return false
}
@@ -149,15 +181,13 @@ public final class ChatListNavigationBar: Component {
}
}
public static let searchScrollHeight: CGFloat = 52.0
public static let storiesScrollHeight: CGFloat = {
return 83.0
}()
public static let searchScrollHeight: CGFloat = searchScrollHeightValue()
public static let storiesScrollHeight: CGFloat = storiesHeightValue()
public final class View: UIView {
private let backgroundView: BlurredBackgroundView
private let separatorLayer: SimpleLayer
private let edgeEffectView: EdgeEffectView
private let headerBackgroundContainer: GlassBackgroundContainerView
public let headerContent = ComponentView<Empty>()
public private(set) var searchContentNode: NavigationBarSearchContentNode?
@@ -178,23 +208,36 @@ public final class ChatListNavigationBar: Component {
public private(set) var storiesUnlocked: Bool = false
private let bottomContentsContainer: UIView
private var tabsNode: ASDisplayNode?
private var tabsNodeIsSearch: Bool = false
private weak var disappearingTabsView: UIView?
private var disappearingTabsViewSearch: Bool = false
private var headerPanelsView: ComponentView<Empty>?
private var disappearingHeaderPanels: ComponentView<Empty>?
public var headerPanels: UIView? {
return self.headerPanelsView?.view
}
private var currentHeaderComponent: ChatListHeaderComponent?
private var currentHeight: CGFloat = 0.0
override public init(frame: CGRect) {
self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true)
self.backgroundView.layer.anchorPoint = CGPoint(x: 0.0, y: 1.0)
self.separatorLayer = SimpleLayer()
self.separatorLayer.anchorPoint = CGPoint()
self.edgeEffectView = EdgeEffectView()
self.headerBackgroundContainer = GlassBackgroundContainerView()
self.headerBackgroundContainer.layer.anchorPoint = CGPoint()
self.bottomContentsContainer = UIView()
self.bottomContentsContainer.layer.anchorPoint = CGPoint()
super.init(frame: frame)
self.addSubview(self.backgroundView)
self.layer.addSublayer(self.separatorLayer)
self.addSubview(self.edgeEffectView)
self.addSubview(self.bottomContentsContainer)
}
required init?(coder: NSCoder) {
@@ -202,7 +245,7 @@ public final class ChatListNavigationBar: Component {
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if !self.backgroundView.frame.contains(point) {
if point.y >= self.currentHeight {
return nil
}
@@ -248,7 +291,12 @@ public final class ChatListNavigationBar: Component {
let searchOffsetDistance: CGFloat = ChatListNavigationBar.searchScrollHeight
let minContentOffset: CGFloat = ChatListNavigationBar.searchScrollHeight
let minContentOffset: CGFloat
if component.search != nil {
minContentOffset = ChatListNavigationBar.searchScrollHeight
} else {
minContentOffset = 0.0
}
let clippedScrollOffset = min(minContentOffset, offset)
if self.clippedScrollOffset == clippedScrollOffset && !self.hasDeferredScrollOffset && !forceUpdate && !allowAvatarsExpansionUpdated {
@@ -259,71 +307,105 @@ public final class ChatListNavigationBar: Component {
let visibleSize = CGSize(width: currentLayout.size.width, height: max(0.0, currentLayout.size.height - clippedScrollOffset))
let previousHeight = self.separatorLayer.position.y
let previousHeight = self.currentHeight
self.currentHeight = visibleSize.height
self.backgroundView.update(size: CGSize(width: visibleSize.width, height: 1000.0), transition: transition.containedViewLayoutTransition)
transition.setBounds(view: self.backgroundView, bounds: CGRect(origin: CGPoint(), size: CGSize(width: visibleSize.width, height: 1000.0)))
transition.animatePosition(view: self.backgroundView, from: CGPoint(x: 0.0, y: -visibleSize.height + self.backgroundView.layer.position.y), to: CGPoint(), additive: true)
self.backgroundView.layer.position = CGPoint(x: 0.0, y: visibleSize.height)
transition.setFrameWithAdditivePosition(layer: self.separatorLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height), size: CGSize(width: visibleSize.width, height: UIScreenPixel)))
let searchContentNode: NavigationBarSearchContentNode
if let current = self.searchContentNode {
searchContentNode = current
if themeUpdated {
var embeddedSearchBarExpansionHeight: CGFloat = 0.0
var searchFrameValue: CGRect?
if let search = component.search {
let searchContentNode: NavigationBarSearchContentNode
if let current = self.searchContentNode {
searchContentNode = current
if themeUpdated {
let placeholder: String
let compactPlaceholder: String
placeholder = component.strings.Common_Search
compactPlaceholder = component.strings.Common_Search
searchContentNode.updateThemeAndPlaceholder(theme: component.theme, placeholder: placeholder, compactPlaceholder: compactPlaceholder)
}
} else {
let placeholder: String
let compactPlaceholder: String
placeholder = component.strings.Common_Search
compactPlaceholder = component.strings.Common_Search
searchContentNode.updateThemeAndPlaceholder(theme: component.theme, placeholder: placeholder, compactPlaceholder: compactPlaceholder)
}
} else {
let placeholder: String
let compactPlaceholder: String
placeholder = component.strings.Common_Search
compactPlaceholder = component.strings.Common_Search
searchContentNode = NavigationBarSearchContentNode(
theme: component.theme,
placeholder: placeholder,
compactPlaceholder: compactPlaceholder,
activate: { [weak self] in
guard let self, let component = self.component, let searchContentNode = self.searchContentNode else {
return
searchContentNode = NavigationBarSearchContentNode(
theme: component.theme,
placeholder: placeholder,
compactPlaceholder: compactPlaceholder,
activate: { [weak self] in
guard let self, let component = self.component, let searchContentNode = self.searchContentNode else {
return
}
component.activateSearch(searchContentNode)
}
component.activateSearch(searchContentNode)
)
searchContentNode.view.layer.anchorPoint = CGPoint()
self.searchContentNode = searchContentNode
self.addSubview(searchContentNode.view)
}
let searchSize = CGSize(width: currentLayout.size.width, height: navigationBarSearchContentHeight)
var searchFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height - searchSize.height - self.bottomContentsContainer.bounds.height - 2.0), size: searchSize)
if let activeSearch = component.activeSearch, !activeSearch.isExternal {
searchFrame.origin.y = component.statusBarHeight + 8.0
}
if component.tabsNode != nil {
searchFrame.origin.y -= 40.0
}
if let activeSearch = component.activeSearch {
if !activeSearch.isExternal {
searchFrame.origin.y -= component.accessoryPanelContainerHeight
}
)
searchContentNode.view.layer.anchorPoint = CGPoint()
self.searchContentNode = searchContentNode
self.addSubview(searchContentNode.view)
} else {
searchFrame.origin.y -= component.accessoryPanelContainerHeight
}
let clippedSearchOffset = max(0.0, min(clippedScrollOffset, searchOffsetDistance))
let searchOffsetFraction = clippedSearchOffset / searchOffsetDistance
searchContentNode.expansionProgress = 1.0 - searchOffsetFraction
embeddedSearchBarExpansionHeight = 60.0 - floorToScreenPixels((1.0 - searchOffsetFraction) * searchSize.height)
if searchOffsetFraction > 0.0 {
searchFrame.origin.y -= (60.0 - 44.0) * 0.5 * searchOffsetFraction
}
searchFrameValue = searchFrame
transition.setFrameWithAdditivePosition(view: searchContentNode.view, frame: searchFrame)
let _ = searchContentNode.updateLayout(size: searchSize, leftInset: component.sideInset, rightInset: component.sideInset, transition: transition.containedViewLayoutTransition)
var searchAlpha: CGFloat = search.isEnabled ? 1.0 : 0.5
if let activeSearch = component.activeSearch, activeSearch.isExternal {
searchAlpha = 0.0
}
transition.setAlpha(view: searchContentNode.view, alpha: searchAlpha)
searchContentNode.isUserInteractionEnabled = search.isEnabled
} else {
if let searchContentNode = self.searchContentNode {
self.searchContentNode = nil
searchContentNode.view.removeFromSuperview()
}
}
let searchSize = CGSize(width: currentLayout.size.width - 6.0 * 2.0, height: navigationBarSearchContentHeight)
var searchFrame = CGRect(origin: CGPoint(x: 6.0, y: visibleSize.height - searchSize.height), size: searchSize)
if component.tabsNode != nil {
searchFrame.origin.y -= 40.0
}
if !component.isSearchActive {
searchFrame.origin.y -= component.accessoryPanelContainerHeight
let edgeEffectHeight: CGFloat = currentLayout.size.height + 20.0
var edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: currentLayout.size.width, height: edgeEffectHeight))
if component.search != nil {
if component.activeSearch != nil {
edgeEffectFrame.origin.y -= 16.0
} else {
edgeEffectFrame.origin.y -= embeddedSearchBarExpansionHeight
}
} else if component.activeSearch != nil {
edgeEffectFrame.origin.y -= 16.0
}
transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
self.edgeEffectView.update(content: component.theme.list.plainBackgroundColor, blur: true, alpha: 0.55, rect: edgeEffectFrame, edge: .top, edgeSize: 40.0, transition: transition)
let clippedSearchOffset = max(0.0, min(clippedScrollOffset, searchOffsetDistance))
let searchOffsetFraction = clippedSearchOffset / searchOffsetDistance
searchContentNode.expansionProgress = 1.0 - searchOffsetFraction
transition.setFrameWithAdditivePosition(view: searchContentNode.view, frame: searchFrame)
searchContentNode.updateLayout(size: searchSize, leftInset: component.sideInset, rightInset: component.sideInset, transition: transition.containedViewLayoutTransition)
transition.setAlpha(view: searchContentNode.view, alpha: component.isSearchEnabled ? 1.0 : 0.5)
searchContentNode.isUserInteractionEnabled = component.isSearchEnabled
let headerTransition = transition
@@ -405,28 +487,31 @@ public final class ChatListNavigationBar: Component {
containerSize: CGSize(width: currentLayout.size.width, height: 44.0)
)
let headerContentY: CGFloat
if component.isSearchActive {
if component.activeSearch != nil {
headerContentY = -headerContentSize.height
} else {
if component.statusBarHeight < 1.0 {
headerContentY = 0.0
} else {
headerContentY = component.statusBarHeight + 5.0
headerContentY = component.statusBarHeight + 10.0
}
}
let headerContentFrame = CGRect(origin: CGPoint(x: 0.0, y: headerContentY), size: headerContentSize)
if let headerContentView = self.headerContent.view {
if headerContentView.superview == nil {
headerContentView.layer.anchorPoint = CGPoint()
self.addSubview(headerContentView)
self.addSubview(self.headerBackgroundContainer)
self.headerBackgroundContainer.contentView.addSubview(headerContentView)
}
transition.setFrameWithAdditivePosition(view: headerContentView, frame: headerContentFrame)
transition.setFrameWithAdditivePosition(view: self.headerBackgroundContainer, frame: headerContentFrame)
self.headerBackgroundContainer.update(size: headerContentFrame.size, isDark: component.theme.overallDarkAppearance, transition: transition)
transition.setFrameWithAdditivePosition(view: headerContentView, frame: CGRect(origin: CGPoint(), size: headerContentFrame.size))
if component.isSearchActive != (headerContentView.alpha == 0.0) {
headerContentView.alpha = component.isSearchActive ? 0.0 : 1.0
if (component.activeSearch != nil) != (headerContentView.alpha == 0.0) {
headerContentView.alpha = component.activeSearch != nil ? 0.0 : 1.0
if !transition.animation.isImmediate {
if component.isSearchActive {
if component.activeSearch != nil {
headerContentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.14)
} else {
headerContentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
@@ -435,6 +520,18 @@ public final class ChatListNavigationBar: Component {
}
}
let bottomContentsContainerPosition: CGPoint
if let activeSearch = component.activeSearch {
if let searchFrameValue, !activeSearch.isExternal {
bottomContentsContainerPosition = CGPoint(x: 0.0, y: searchFrameValue.maxY - 8.0)
} else {
bottomContentsContainerPosition = CGPoint(x: 0.0, y: -self.bottomContentsContainer.bounds.height)
}
} else {
bottomContentsContainerPosition = CGPoint(x: 0.0, y: visibleSize.height - self.bottomContentsContainer.bounds.height)
}
transition.setPosition(view: self.bottomContentsContainer, position: bottomContentsContainerPosition)
if component.tabsNode !== self.tabsNode {
if let tabsNode = self.tabsNode {
tabsNode.layer.anchorPoint = CGPoint()
@@ -455,7 +552,8 @@ public final class ChatListNavigationBar: Component {
}
var tabsFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height), size: CGSize(width: visibleSize.width, height: 46.0))
if !component.isSearchActive {
if component.activeSearch != nil {
} else {
tabsFrame.origin.y -= component.accessoryPanelContainerHeight
}
if component.tabsNode != nil {
@@ -463,7 +561,8 @@ public final class ChatListNavigationBar: Component {
}
var accessoryPanelContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height), size: CGSize(width: visibleSize.width, height: component.accessoryPanelContainerHeight))
if !component.isSearchActive {
if component.activeSearch != nil {
} else {
accessoryPanelContainerFrame.origin.y -= component.accessoryPanelContainerHeight
}
@@ -528,14 +627,15 @@ public final class ChatListNavigationBar: Component {
strings: component.strings,
statusBarHeight: component.statusBarHeight,
sideInset: component.sideInset,
isSearchActive: component.isSearchActive,
isSearchEnabled: component.isSearchEnabled,
search: component.search,
activeSearch: component.activeSearch,
primaryContent: component.primaryContent,
secondaryContent: component.secondaryContent,
secondaryTransition: component.secondaryTransition,
storySubscriptions: component.storySubscriptions,
storiesIncludeHidden: component.storiesIncludeHidden,
uploadProgress: storyUploadProgress,
headerPanels: component.headerPanels,
tabsNode: component.tabsNode,
tabsNodeIsSearch: component.tabsNodeIsSearch,
accessoryPanelContainer: component.accessoryPanelContainer,
@@ -575,8 +675,6 @@ public final class ChatListNavigationBar: Component {
}
func update(component: ChatListNavigationBar, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
let themeUpdated = self.component?.theme !== component.theme
var uploadProgressUpdated = false
var storySubscriptionsUpdated = false
if let previousComponent = self.component {
@@ -591,32 +689,85 @@ public final class ChatListNavigationBar: Component {
self.component = component
self.state = state
if themeUpdated {
self.backgroundView.updateColor(color: component.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
self.separatorLayer.backgroundColor = component.theme.rootController.navigationBar.separatorColor.cgColor
}
var contentHeight = component.statusBarHeight
if component.statusBarHeight >= 1.0 {
contentHeight += 3.0
}
contentHeight += 44.0
if component.isSearchActive {
if component.statusBarHeight < 1.0 {
contentHeight += 8.0
if let activeSearch = component.activeSearch {
if !activeSearch.isExternal {
contentHeight += navigationBarSearchContentHeight
}
} else {
contentHeight += navigationBarSearchContentHeight
contentHeight += 44.0
contentHeight += 9.0
if component.search != nil {
contentHeight += navigationBarSearchContentHeight + 2.0
}
}
if component.tabsNode != nil {
contentHeight += 40.0
var headersContentHeight: CGFloat = 0.0
if let disappearingHeaderPanelsView = self.disappearingHeaderPanels?.view {
let headerPanelsFrame = CGRect(origin: CGPoint(x: 0.0, y: headersContentHeight), size: disappearingHeaderPanelsView.bounds.size)
transition.setFrame(view: disappearingHeaderPanelsView, frame: headerPanelsFrame)
}
if let headerPanels = component.headerPanels {
let headerPanelsView: ComponentView<Empty>
var headerPanelsTransition = transition
if let current = self.headerPanelsView {
headerPanelsView = current
} else {
headerPanelsTransition = headerPanelsTransition.withAnimation(.none)
headerPanelsView = ComponentView()
self.headerPanelsView = headerPanelsView
}
let headerPanelsSize = headerPanelsView.update(
transition: headerPanelsTransition,
component: headerPanels,
environment: {},
containerSize: CGSize(width: availableSize.width - component.sideInset * 2.0, height: 10000.0)
)
let headerPanelsFrame = CGRect(origin: CGPoint(x: component.sideInset, y: headersContentHeight), size: headerPanelsSize)
if let headerPanelsComponentView = headerPanelsView.view {
if headerPanelsComponentView.superview == nil {
self.bottomContentsContainer.addSubview(headerPanelsComponentView)
transition.animateAlpha(view: headerPanelsComponentView, from: 0.0, to: 1.0)
}
headerPanelsTransition.setFrame(view: headerPanelsComponentView, frame: headerPanelsFrame)
}
headersContentHeight += headerPanelsSize.height
} else if let headerPanelsView = self.headerPanelsView {
self.headerPanelsView = nil
self.disappearingHeaderPanels = headerPanelsView
if let headerPanelsComponentView = headerPanelsView.view {
transition.setAlpha(view: headerPanelsComponentView, alpha: 0.0, completion: { [weak self, weak headerPanelsComponentView] _ in
guard let self, let headerPanelsComponentView else {
return
}
headerPanelsComponentView.removeFromSuperview()
if self.disappearingHeaderPanels?.view === headerPanelsComponentView {
self.disappearingHeaderPanels = nil
}
})
}
}
headersContentHeight += 3.0
transition.setBounds(view: self.bottomContentsContainer, bounds: CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: headersContentHeight)))
if let activeSearch = component.activeSearch, !activeSearch.isExternal {
transition.setAlpha(view: self.bottomContentsContainer, alpha: 0.0)
} else {
transition.setAlpha(view: self.bottomContentsContainer, alpha: 1.0)
}
if component.accessoryPanelContainer != nil && !component.isSearchActive {
contentHeight += component.accessoryPanelContainerHeight
if component.activeSearch == nil {
contentHeight += headersContentHeight
if component.tabsNode != nil {
contentHeight += 40.0
}
}
let size = CGSize(width: availableSize.width, height: contentHeight)