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
@@ -419,13 +419,13 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget {
context.setFillColor(color.withMultipliedAlpha(0.2).cgColor)
context.addPath(UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: 21.0).cgPath)
context.addPath(UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: 30.0).cgPath)
context.fillPath()
context.setFillColor(color.cgColor)
let plusSize = CGSize(width: 3.5, height: 28.0)
context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.width) / 2.0), y: floorToScreenPixels((size.height - plusSize.height) / 2.0), width: plusSize.width, height: plusSize.height).offsetBy(dx: 0.0, dy: -17.0), cornerRadius: plusSize.width / 2.0).cgPath)
context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.height) / 2.0), y: floorToScreenPixels((size.height - plusSize.width) / 2.0), width: plusSize.height, height: plusSize.width).offsetBy(dx: 0.0, dy: -17.0), cornerRadius: plusSize.width / 2.0).cgPath)
let plusSize = CGSize(width: 4.0, height: 27.0)
context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.width) / 2.0), y: floorToScreenPixels((size.height - plusSize.height) / 2.0), width: plusSize.width, height: plusSize.height).offsetBy(dx: 0.0, dy: -18.0), cornerRadius: plusSize.width / 2.0).cgPath)
context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.height) / 2.0), y: floorToScreenPixels((size.height - plusSize.width) / 2.0), width: plusSize.height, height: plusSize.width).offsetBy(dx: 0.0, dy: -18.0), cornerRadius: plusSize.width / 2.0).cgPath)
context.fillPath()
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
@@ -437,7 +437,7 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget {
let components = string.components(separatedBy: "\n")
for component in components {
context.saveGState()
let attributedString = NSAttributedString(string: component, attributes: [NSAttributedString.Key.font: Font.medium(17.0), NSAttributedString.Key.foregroundColor: color])
let attributedString = NSAttributedString(string: component, attributes: [NSAttributedString.Key.font: Font.medium(16.0), NSAttributedString.Key.foregroundColor: color])
let line = CTLineCreateWithAttributedString(attributedString)
let lineBounds = CTLineGetBoundsWithOptions(line, .useGlyphPathBounds)
@@ -4026,7 +4026,7 @@ public final class EmojiPagerContentComponent: Component {
private func updateTopPanelSeparator(transition: ComponentTransition) {
if let topPanelSeparator = self.topPanelSeparator {
var offset = self.scrollView.contentOffset.y
let startOffset: CGFloat = 40.0 - self.topPanelHeight
let startOffset: CGFloat = 46.0 - self.topPanelHeight
let endOffset: CGFloat = startOffset + 10.0
offset = min(max(offset, startOffset), endOffset)
@@ -403,7 +403,7 @@ public final class EntityKeyboardComponent: Component {
if let _ = component.maskContent?.inputInteractionHolder.inputInteraction?.openStickerSettings {
contentAccessoryRightButtons.append(AnyComponentWithIdentity(id: "masks", component: AnyComponent(EntityKeyboardBottomPanelButton(
icon: "Chat/Input/Media/EntityInputSettingsIcon",
color: component.theme.chat.inputPanel.inputControlColor,
theme: component.theme,
action: {
maskContent.inputInteractionHolder.inputInteraction?.openStickerSettings?()
}
@@ -417,7 +417,7 @@ public final class EntityKeyboardComponent: Component {
if let addImage = component.stickerContent?.inputInteractionHolder.inputInteraction?.addImage {
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "gifs", component: AnyComponent(EntityKeyboardBottomPanelButton(
icon: "Media Editor/AddImage",
color: component.theme.chat.inputPanel.inputControlColor,
theme: component.theme,
action: {
addImage()
}
@@ -537,7 +537,7 @@ public final class EntityKeyboardComponent: Component {
if let _ = component.stickerContent?.inputInteractionHolder.inputInteraction?.openStickerSettings {
contentAccessoryRightButtons.append(AnyComponentWithIdentity(id: "stickers", component: AnyComponent(EntityKeyboardBottomPanelButton(
icon: "Chat/Input/Media/EntityInputSettingsIcon",
color: component.theme.chat.inputPanel.inputControlColor,
theme: component.theme,
action: {
stickerContent.inputInteractionHolder.inputInteraction?.openStickerSettings?()
}
@@ -546,7 +546,7 @@ public final class EntityKeyboardComponent: Component {
if let addImage = component.stickerContent?.inputInteractionHolder.inputInteraction?.addImage {
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "stickers", component: AnyComponent(EntityKeyboardBottomPanelButton(
icon: "Media Editor/AddImage",
color: component.theme.chat.inputPanel.inputControlColor,
theme: component.theme,
action: {
addImage()
}
@@ -649,7 +649,7 @@ public final class EntityKeyboardComponent: Component {
if let _ = deleteBackwards {
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(EntityKeyboardBottomPanelButton(
icon: "Chat/Input/Media/EntityInputGlobeIcon",
color: component.theme.chat.inputPanel.inputControlColor,
theme: component.theme,
action: { [weak self] in
guard let strongSelf = self, let component = strongSelf.component else {
return
@@ -660,7 +660,7 @@ public final class EntityKeyboardComponent: Component {
} else if let addImage = component.emojiContent?.inputInteractionHolder.inputInteraction?.addImage {
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(EntityKeyboardBottomPanelButton(
icon: "Media Editor/AddImage",
color: component.theme.chat.inputPanel.inputControlColor,
theme: component.theme,
action: {
addImage()
}
@@ -671,7 +671,7 @@ public final class EntityKeyboardComponent: Component {
if let _ = deleteBackwards {
contentAccessoryRightButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(EntityKeyboardBottomPanelButton(
icon: "Chat/Input/Media/EntityInputClearIcon",
color: component.theme.chat.inputPanel.inputControlColor,
theme: component.theme,
action: {
deleteBackwards?()
AudioServicesPlaySystemSound(1155)
@@ -721,7 +721,8 @@ public final class EntityKeyboardComponent: Component {
topPanel: AnyComponent(EntityKeyboardTopContainerPanelComponent(
theme: component.theme,
overflowHeight: component.hiddenInputHeight,
topInset: component.externalTopPanelContainer == nil ? 6.0 : 0.0,
topInset: component.externalTopPanelContainer == nil ? 8.0 : 0.0,
height: component.externalTopPanelContainer == nil ? 40.0 : 34.0,
displayBackground: component.externalTopPanelContainer != nil ? .none : component.displayTopPanelBackground
)),
externalTopPanelContainer: component.externalTopPanelContainer,
@@ -2,6 +2,7 @@ import Foundation
import UIKit
import Display
import ComponentFlow
import TelegramPresentationData
import PagerComponent
import ComponentDisplayAdapters
import BundleIconComponent
@@ -10,18 +11,18 @@ import AppBundle
final class EntityKeyboardBottomPanelButton: Component {
let icon: String
let color: UIColor
let theme: PresentationTheme
let action: () -> Void
let holdAction: (() -> Void)?
init(
icon: String,
color: UIColor,
theme: PresentationTheme,
action: @escaping () -> Void,
holdAction: (() -> Void)? = nil
) {
self.icon = icon
self.color = color
self.theme = theme
self.action = action
self.holdAction = holdAction
}
@@ -30,7 +31,7 @@ final class EntityKeyboardBottomPanelButton: Component {
if lhs.icon != rhs.icon {
return false
}
if lhs.color != rhs.color {
if lhs.theme !== rhs.theme {
return false
}
if (lhs.holdAction == nil) != (rhs.holdAction == nil) {
@@ -39,7 +40,9 @@ final class EntityKeyboardBottomPanelButton: Component {
return true
}
final class View: HighlightTrackingButton {
final class View: UIView {
private let backgroundView: GlassBackgroundView
let buttonView: HighlightTrackingButton
let iconView: GlassBackgroundView.ContentImageView
let tintMaskContainer: UIView
@@ -48,15 +51,19 @@ final class EntityKeyboardBottomPanelButton: Component {
var component: EntityKeyboardBottomPanelButton?
private var currentIsHighlighted: Bool = false {
didSet {
if self.currentIsHighlighted != oldValue {
self.updateAlpha(transition: .immediate)
}
}
}
// private var currentIsHighlighted: Bool = false {
// didSet {
// if self.currentIsHighlighted != oldValue {
// self.updateAlpha(transition: .immediate)
// }
// }
// }
override init(frame: CGRect) {
self.backgroundView = GlassBackgroundView()
self.buttonView = HighlightTrackingButton()
self.iconView = GlassBackgroundView.ContentImageView()
self.iconView.isUserInteractionEnabled = false
@@ -65,9 +72,11 @@ final class EntityKeyboardBottomPanelButton: Component {
super.init(frame: frame)
self.addSubview(self.iconView)
self.addSubview(self.backgroundView)
self.backgroundView.contentView.addSubview(self.iconView)
self.backgroundView.contentView.addSubview(self.buttonView)
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
self.buttonView.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
}
required init?(coder: NSCoder) {
@@ -86,65 +95,65 @@ final class EntityKeyboardBottomPanelButton: Component {
}
}
override public func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
self.currentIsHighlighted = true
self.holdActionTriggerred = false
if self.component?.holdAction != nil {
self.holdActionTriggerred = true
self.component?.action()
self.holdActionTimer?.invalidate()
let holdActionTimer = Timer(timeInterval: 0.5, repeats: false, block: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.holdActionTimer?.invalidate()
strongSelf.component?.holdAction?()
strongSelf.beginExecuteHoldActionTimer()
})
self.holdActionTimer = holdActionTimer
RunLoop.main.add(holdActionTimer, forMode: .common)
}
return super.beginTracking(touch, with: event)
}
// override public func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
// self.currentIsHighlighted = true
//
// self.holdActionTriggerred = false
//
// if self.component?.holdAction != nil {
// self.holdActionTriggerred = true
// self.component?.action()
//
// self.holdActionTimer?.invalidate()
// let holdActionTimer = Timer(timeInterval: 0.5, repeats: false, block: { [weak self] _ in
// guard let strongSelf = self else {
// return
// }
// strongSelf.holdActionTimer?.invalidate()
// strongSelf.component?.holdAction?()
// strongSelf.beginExecuteHoldActionTimer()
// })
// self.holdActionTimer = holdActionTimer
// RunLoop.main.add(holdActionTimer, forMode: .common)
// }
//
// return super.beginTracking(touch, with: event)
// }
private func beginExecuteHoldActionTimer() {
self.holdActionTimer?.invalidate()
let holdActionTimer = Timer(timeInterval: 0.1, repeats: true, block: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.component?.holdAction?()
})
self.holdActionTimer = holdActionTimer
RunLoop.main.add(holdActionTimer, forMode: .common)
}
// private func beginExecuteHoldActionTimer() {
// self.holdActionTimer?.invalidate()
// let holdActionTimer = Timer(timeInterval: 0.1, repeats: true, block: { [weak self] _ in
// guard let strongSelf = self else {
// return
// }
// strongSelf.component?.holdAction?()
// })
// self.holdActionTimer = holdActionTimer
// RunLoop.main.add(holdActionTimer, forMode: .common)
// }
override public func endTracking(_ touch: UITouch?, with event: UIEvent?) {
self.currentIsHighlighted = false
self.holdActionTimer?.invalidate()
self.holdActionTimer = nil
super.endTracking(touch, with: event)
}
override public func cancelTracking(with event: UIEvent?) {
self.currentIsHighlighted = false
self.holdActionTimer?.invalidate()
self.holdActionTimer = nil
super.cancelTracking(with: event)
}
// override public func endTracking(_ touch: UITouch?, with event: UIEvent?) {
// self.currentIsHighlighted = false
//
// self.holdActionTimer?.invalidate()
// self.holdActionTimer = nil
//
// super.endTracking(touch, with: event)
// }
//
// override public func cancelTracking(with event: UIEvent?) {
// self.currentIsHighlighted = false
//
// self.holdActionTimer?.invalidate()
// self.holdActionTimer = nil
//
// super.cancelTracking(with: event)
// }
private func updateAlpha(transition: ComponentTransition) {
let alpha: CGFloat = self.currentIsHighlighted ? 0.6 : 1.0
transition.setAlpha(view: self.iconView, alpha: alpha)
}
// private func updateAlpha(transition: ComponentTransition) {
// let alpha: CGFloat = self.currentIsHighlighted ? 0.6 : 1.0
// transition.setAlpha(view: self.iconView, alpha: alpha)
// }
func update(component: EntityKeyboardBottomPanelButton, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
if self.component?.icon != component.icon {
@@ -153,15 +162,21 @@ final class EntityKeyboardBottomPanelButton: Component {
self.component = component
self.iconView.tintColor = component.color
self.iconView.tintColor = component.theme.chat.inputPanel.panelControlColor
let size = CGSize(width: 38.0, height: 38.0)
let size = CGSize(width: 40.0, height: 40.0)
if let image = self.iconView.image {
let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) * 0.5), y: floor((size.height - image.size.height) * 0.5)), size: image.size)
self.iconView.frame = iconFrame
}
let tintColor: GlassBackgroundView.TintColor = .init(kind: .panel, color: component.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7))
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size))
self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: tintColor, isInteractive: true, transition: transition)
self.buttonView.frame = CGRect(origin: .zero, size: size)
return size
}
}
@@ -8,6 +8,9 @@ import TelegramCore
import ComponentDisplayAdapters
import BundleIconComponent
import GlassBackgroundComponent
import EdgeEffect
import LiquidLens
import TabSelectionRecognizer
private final class BottomPanelIconComponent: Component {
let title: String
@@ -57,22 +60,15 @@ private final class BottomPanelIconComponent: Component {
super.init(frame: frame)
self.addSubview(self.contentView)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.component?.action()
}
}
func update(component: BottomPanelIconComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
if self.component?.title != component.title {
let text = NSAttributedString(string: component.title, font: Font.medium(15.0), textColor: .white)
let text = NSAttributedString(string: component.title, font: Font.medium(14.0), textColor: .white)
let textBounds = text.boundingRect(with: CGSize(width: 120.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)
self.contentView.image = generateImage(CGSize(width: ceil(textBounds.width), height: ceil(textBounds.height)), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
@@ -89,19 +85,9 @@ private final class BottomPanelIconComponent: Component {
let textSize = self.contentView.image?.size ?? CGSize()
let size = CGSize(width: textSize.width + textInset * 2.0, height: 28.0)
let color = component.theme.chat.inputPanel.inputControlColor
self.contentView.tintColor = component.theme.chat.inputPanel.panelControlColor
if self.contentView.tintColor != color {
if !transition.animation.isImmediate {
UIView.animate(withDuration: 0.15, delay: 0.0, options: [], animations: {
self.contentView.tintColor = color
}, completion: nil)
} else {
self.contentView.tintColor = color
}
}
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: (size.height - textSize.height) / 2.0 - 1.0), size: textSize))
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: (size.height - textSize.height) / 2.0), size: textSize))
return size
}
@@ -163,17 +149,27 @@ final class EntityKeyboardBottomPanelComponent: Component {
private var leftAccessoryButton: AccessoryButtonView?
private var rightAccessoryButton: AccessoryButtonView?
private var iconViews: [AnyHashable: ComponentHostView<Empty>] = [:]
private var highlightedIconBackgroundView: UIView
private var highlightedTintIconBackgroundView: UIView
private let edgeEffectView: EdgeEffectView
private let backgroundContainer: GlassBackgroundContainerView
private let liquidLensView: LiquidLensView
private var itemViews: [AnyHashable: ComponentHostView<Empty>] = [:]
private var selectedItemViews: [AnyHashable: ComponentHostView<Empty>] = [:]
private var tabSelectionRecognizer: TabSelectionRecognizer?
private var selectionGestureState: (startX: CGFloat, currentX: CGFloat, itemId: AnyHashable)?
let tintContentMask: UIView
private var component: EntityKeyboardBottomPanelComponent?
private var state: EmptyComponentState?
private var environment: PagerComponentPanelEnvironment<EntityKeyboardTopContainerPanelEnvironment>?
override init(frame: CGRect) {
self.tintContentMask = UIView()
self.edgeEffectView = EdgeEffectView()
self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true, customBlurRadius: 10.0)
self.separatorView = UIView()
@@ -182,51 +178,102 @@ final class EntityKeyboardBottomPanelComponent: Component {
self.tintSeparatorView.isUserInteractionEnabled = false
self.tintSeparatorView.backgroundColor = UIColor(white: 0.0, alpha: 0.7)
self.tintContentMask.addSubview(self.tintSeparatorView)
self.highlightedIconBackgroundView = UIView()
self.highlightedIconBackgroundView.isUserInteractionEnabled = false
self.highlightedIconBackgroundView.layer.cornerRadius = 10.0
self.highlightedIconBackgroundView.clipsToBounds = true
self.highlightedTintIconBackgroundView = UIView()
self.highlightedTintIconBackgroundView.isUserInteractionEnabled = false
self.highlightedTintIconBackgroundView.layer.cornerRadius = 10.0
self.highlightedTintIconBackgroundView.clipsToBounds = true
self.highlightedTintIconBackgroundView.backgroundColor = UIColor(white: 0.0, alpha: 0.1)
self.tintContentMask.addSubview(self.highlightedTintIconBackgroundView)
self.backgroundContainer = GlassBackgroundContainerView()
self.liquidLensView = LiquidLensView(kind: .externalContainer)
super.init(frame: frame)
self.addSubview(self.backgroundView)
self.addSubview(self.highlightedIconBackgroundView)
self.addSubview(self.separatorView)
self.addSubview(self.edgeEffectView)
self.addSubview(self.backgroundContainer)
self.backgroundContainer.contentView.addSubview(self.liquidLensView)
let tabSelectionRecognizer = TabSelectionRecognizer(target: self, action: #selector(self.onTabSelectionGesture(_:)))
self.tabSelectionRecognizer = tabSelectionRecognizer
self.liquidLensView.addGestureRecognizer(tabSelectionRecognizer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func item(at point: CGPoint) -> AnyHashable? {
var closestItem: (AnyHashable, CGFloat)?
for (id, itemView) in self.itemViews {
if itemView.frame.contains(point) {
return id
} else {
let distance = abs(point.x - itemView.center.x)
if let closestItemValue = closestItem {
if closestItemValue.1 > distance {
closestItem = (id, distance)
}
} else {
closestItem = (id, distance)
}
}
}
return closestItem?.0
}
@objc private func onTabSelectionGesture(_ recognizer: TabSelectionRecognizer) {
guard let environment = self.environment else {
return
}
let location = recognizer.location(in: self.liquidLensView.contentView)
switch recognizer.state {
case .began:
if let itemId = self.item(at: location), let itemView = self.itemViews[itemId] {
let startX = itemView.frame.minX - 4.0
self.selectionGestureState = (startX, startX, itemId)
self.state?.updated(transition: .spring(duration: 0.4), isLocal: true)
}
case .changed:
if var selectionGestureState = self.selectionGestureState {
selectionGestureState.currentX = selectionGestureState.startX + recognizer.translation(in: self).x
if let itemId = self.item(at: location) {
selectionGestureState.itemId = itemId
}
self.selectionGestureState = selectionGestureState
self.state?.updated(transition: .immediate, isLocal: true)
}
case .ended, .cancelled:
if let selectionGestureState = self.selectionGestureState {
self.selectionGestureState = nil
if case .ended = recognizer.state {
guard let item = environment.contentIcons.first(where: { $0.id == selectionGestureState.itemId }) else {
return
}
environment.navigateToContentId(item.id)
}
self.state?.updated(transition: .spring(duration: 0.4), isLocal: true)
}
default:
break
}
}
func update(component: EntityKeyboardBottomPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
if self.component?.theme !== component.theme {
self.separatorView.backgroundColor = component.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5)
self.backgroundView.updateColor(color: component.theme.chat.inputPanel.panelBackgroundColor.withMultipliedAlpha(1.0), transition: .immediate)
self.highlightedIconBackgroundView.backgroundColor = component.theme.chat.inputMediaPanel.panelHighlightedIconBackgroundColor
}
let intrinsicHeight: CGFloat = 34.0
let height = intrinsicHeight + component.containerInsets.bottom
let height = intrinsicHeight + component.containerInsets.bottom + 20.0
let accessoryButtonOffset: CGFloat
if component.containerInsets.bottom > 0.0 {
accessoryButtonOffset = 2.0
accessoryButtonOffset = 0.0
} else {
accessoryButtonOffset = -2.0
}
self.component = component
self.state = state
let panelEnvironment = environment[PagerComponentPanelEnvironment<EntityKeyboardTopContainerPanelEnvironment>.self].value
self.environment = panelEnvironment
let activeContentId = panelEnvironment.activeContentId
var leftAccessoryButtonComponent: AnyComponentWithIdentity<Empty>?
@@ -257,7 +304,7 @@ final class EntityKeyboardBottomPanelComponent: Component {
environment: {},
containerSize: CGSize(width: .greatestFiniteMagnitude, height: intrinsicHeight)
)
let leftAccessoryButtonFrame = CGRect(origin: CGPoint(x: component.containerInsets.left + 2.0, y: accessoryButtonOffset), size: leftAccessoryButtonSize)
let leftAccessoryButtonFrame = CGRect(origin: CGPoint(x: component.containerInsets.left + 18.0, y: accessoryButtonOffset), size: leftAccessoryButtonSize)
leftAccessoryButtonTransition.setFrame(view: leftAccessoryButton.view, frame: leftAccessoryButtonFrame)
if let leftAccessoryButtonView = leftAccessoryButton.view.componentView as? PagerTopPanelView {
if leftAccessoryButtonView.tintContentMask.superview == nil {
@@ -328,7 +375,7 @@ final class EntityKeyboardBottomPanelComponent: Component {
containerSize: CGSize(width: .greatestFiniteMagnitude, height: intrinsicHeight)
)
let rightAccessoryButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - component.containerInsets.right - 2.0 - rightAccessoryButtonSize.width, y: accessoryButtonOffset), size: rightAccessoryButtonSize)
let rightAccessoryButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - component.containerInsets.right - 18.0 - rightAccessoryButtonSize.width, y: accessoryButtonOffset), size: rightAccessoryButtonSize)
rightAccessoryButtonTransition.setFrame(view: rightAccessoryButton.view, frame: rightAccessoryButtonFrame)
if let rightAccessoryButtonView = rightAccessoryButton.view.componentView as? PagerTopPanelView {
if rightAccessoryButtonView.tintContentMask.superview == nil {
@@ -374,23 +421,32 @@ final class EntityKeyboardBottomPanelComponent: Component {
var iconInfos: [AnyHashable: (size: CGSize, transition: ComponentTransition)] = [:]
var iconTotalSize = CGSize()
let iconSpacing: CGFloat = 4.0
let iconSpacing: CGFloat = 0.0
let navigateToContentId = panelEnvironment.navigateToContentId
var lensSelection: (x: CGFloat, width: CGFloat) = (0.0, 0.0)
if panelEnvironment.contentIcons.count > 1 {
for icon in panelEnvironment.contentIcons {
validIconIds.append(icon.id)
var iconTransition = transition
let iconView: ComponentHostView<Empty>
if let current = self.iconViews[icon.id] {
let selectedIconView: ComponentHostView<Empty>
if let current = self.itemViews[icon.id], let currentSelected = self.selectedItemViews[icon.id] {
iconView = current
selectedIconView = currentSelected
} else {
iconTransition = .immediate
iconView = ComponentHostView<Empty>()
self.iconViews[icon.id] = iconView
self.addSubview(iconView)
iconView.isUserInteractionEnabled = false
selectedIconView = ComponentHostView<Empty>()
selectedIconView.isUserInteractionEnabled = false
self.itemViews[icon.id] = iconView
self.selectedItemViews[icon.id] = selectedIconView
self.liquidLensView.contentView.addSubview(iconView)
self.liquidLensView.selectedContentView.addSubview(selectedIconView)
}
let iconSize = iconView.update(
@@ -407,65 +463,70 @@ final class EntityKeyboardBottomPanelComponent: Component {
containerSize: CGSize(width: 28.0, height: 28.0)
)
let _ = selectedIconView.update(
transition: iconTransition,
component: AnyComponent(BottomPanelIconComponent(
title: icon.title,
isHighlighted: icon.id == activeContentId,
theme: component.theme,
action: {
navigateToContentId(icon.id)
}
)),
environment: {},
containerSize: CGSize(width: 28.0, height: 28.0)
)
iconInfos[icon.id] = (size: iconSize, transition: iconTransition)
if !iconTotalSize.width.isZero {
iconTotalSize.width += iconSpacing
iconTotalSize.width += iconSpacing - 8.0
}
iconTotalSize.width += iconSize.width
iconTotalSize.height = max(iconTotalSize.height, iconSize.height)
}
}
var nextIconOrigin = CGPoint(x: floor((availableSize.width - iconTotalSize.width) / 2.0), y: floor((intrinsicHeight - iconTotalSize.height) / 2.0))
if component.containerInsets.bottom > 0.0 {
nextIconOrigin.y += 3.0
}
let tabsSize = CGSize(width: iconTotalSize.width, height: 40.0)
var nextIconOrigin = CGPoint(x: floor((tabsSize.width - iconTotalSize.width) / 2.0), y: floor((tabsSize.height - iconTotalSize.height) / 2.0))
transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: .zero, size: availableSize))
self.backgroundContainer.update(size: availableSize, isDark: component.theme.overallDarkAppearance, transition: transition)
if panelEnvironment.contentIcons.count > 1 {
for icon in panelEnvironment.contentIcons {
guard let iconInfo = iconInfos[icon.id], let iconView = self.iconViews[icon.id] else {
guard let iconInfo = iconInfos[icon.id], let iconView = self.itemViews[icon.id], let selectedIconView = self.selectedItemViews[icon.id] else {
continue
}
let iconFrame = CGRect(origin: nextIconOrigin, size: iconInfo.size)
iconInfo.transition.setFrame(view: iconView, frame: iconFrame, completion: nil)
if let iconView = iconView.componentView as? BottomPanelIconComponent.View {
if iconView.tintMaskContainer.superview == nil {
self.tintContentMask.addSubview(iconView.tintMaskContainer)
}
iconInfo.transition.setFrame(view: iconView.tintMaskContainer, frame: iconFrame, completion: nil)
}
iconInfo.transition.setFrame(view: selectedIconView, frame: iconFrame, completion: nil)
if let activeContentId = activeContentId, activeContentId == icon.id {
self.highlightedIconBackgroundView.isHidden = false
self.highlightedTintIconBackgroundView.isHidden = false
transition.setFrame(view: self.highlightedIconBackgroundView, frame: iconFrame)
transition.setFrame(view: self.highlightedTintIconBackgroundView, frame: iconFrame)
let cornerRadius: CGFloat = min(iconFrame.width, iconFrame.height) / 2.0
transition.setCornerRadius(layer: self.highlightedIconBackgroundView.layer, cornerRadius: cornerRadius)
transition.setCornerRadius(layer: self.highlightedTintIconBackgroundView.layer, cornerRadius: cornerRadius)
lensSelection = (iconFrame.origin.x, iconFrame.width)
}
nextIconOrigin.x += iconInfo.size.width + iconSpacing
nextIconOrigin.x += iconInfo.size.width + iconSpacing - 8.0
}
}
if activeContentId == nil {
self.highlightedIconBackgroundView.isHidden = true
if let selectionGestureState = self.selectionGestureState {
lensSelection = (selectionGestureState.currentX, lensSelection.width)
}
transition.setFrame(view: self.liquidLensView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - tabsSize.width) / 2.0), y: 0.0), size: tabsSize))
self.liquidLensView.update(size: CGSize(width: tabsSize.width, height: tabsSize.height), selectionOrigin: CGPoint(x: max(0.0, min(tabsSize.width - lensSelection.width, lensSelection.x)), y: 0.0), selectionSize: CGSize(width: lensSelection.width, height: tabsSize.height), inset: 3.0, isDark: component.theme.overallDarkAppearance, isLifted: self.selectionGestureState != nil, isCollapsed: activeContentId == nil, transition: transition)
var removedIconViewIds: [AnyHashable] = []
for (id, iconView) in self.iconViews {
for (id, iconView) in self.itemViews {
if !validIconIds.contains(id) {
removedIconViewIds.append(id)
iconView.removeFromSuperview()
}
}
for id in removedIconViewIds {
self.iconViews.removeValue(forKey: id)
self.itemViews.removeValue(forKey: id)
}
transition.setFrame(view: self.separatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: UIScreenPixel)))
@@ -473,8 +534,11 @@ final class EntityKeyboardBottomPanelComponent: Component {
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: height)))
//self.backgroundView.update(size: CGSize(width: availableSize.width, height: height), transition: transition.containedViewLayoutTransition)
self.component = component
let edgeEffectHeight: CGFloat = 80.0
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: height - edgeEffectHeight), size: CGSize(width: availableSize.width, height: edgeEffectHeight))
transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
self.edgeEffectView.update(content: component.theme.chat.inputMediaPanel.backgroundColor.withMultipliedAlpha(0.8), rect: edgeEffectFrame, edge: .bottom, edgeSize: min(edgeEffectHeight, 50.0), transition: transition)
return CGSize(width: availableSize.width, height: height)
}
@@ -9,15 +9,18 @@ import Postbox
public final class EntityKeyboardTopContainerPanelEnvironment: Equatable {
let isContentInFocus: Bool
let height: CGFloat
let visibilityFractionUpdated: ActionSlot<(CGFloat, ComponentTransition)>
let isExpandedUpdated: (Bool, ComponentTransition) -> Void
init(
isContentInFocus: Bool,
height: CGFloat,
visibilityFractionUpdated: ActionSlot<(CGFloat, ComponentTransition)>,
isExpandedUpdated: @escaping (Bool, ComponentTransition) -> Void
) {
self.isContentInFocus = isContentInFocus
self.height = height
self.visibilityFractionUpdated = visibilityFractionUpdated
self.isExpandedUpdated = isExpandedUpdated
}
@@ -26,6 +29,9 @@ public final class EntityKeyboardTopContainerPanelEnvironment: Equatable {
if lhs.isContentInFocus != rhs.isContentInFocus {
return false
}
if lhs.height != rhs.height {
return false
}
if lhs.visibilityFractionUpdated !== rhs.visibilityFractionUpdated {
return false
}
@@ -39,17 +45,20 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
let theme: PresentationTheme
let overflowHeight: CGFloat
let topInset: CGFloat
let height: CGFloat
let displayBackground: EntityKeyboardComponent.DisplayTopPanelBackground
init(
theme: PresentationTheme,
overflowHeight: CGFloat,
topInset: CGFloat,
height: CGFloat,
displayBackground: EntityKeyboardComponent.DisplayTopPanelBackground
) {
self.theme = theme
self.overflowHeight = overflowHeight
self.topInset = topInset
self.height = height
self.displayBackground = displayBackground
}
@@ -60,6 +69,9 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
if lhs.overflowHeight != rhs.overflowHeight {
return false
}
if lhs.height != rhs.height {
return false
}
if lhs.topInset != rhs.topInset {
return false
}
@@ -105,7 +117,7 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
}
func update(component: EntityKeyboardTopContainerPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
let intrinsicHeight: CGFloat = 34.0
let intrinsicHeight: CGFloat = component.height
let height = intrinsicHeight + component.topInset
let panelEnvironment = environment[PagerComponentPanelEnvironment.self].value
@@ -165,6 +177,7 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
environment: {
EntityKeyboardTopContainerPanelEnvironment(
isContentInFocus: panelEnvironment.isContentInFocus,
height: intrinsicHeight,
visibilityFractionUpdated: panelView.visibilityFractionUpdated,
isExpandedUpdated: { [weak self] isExpanded, transition in
guard let strongSelf = self else {
@@ -2009,7 +2009,7 @@ public final class EntityKeyboardTopPanelComponent: Component {
let panelEnvironment = environment[EntityKeyboardTopContainerPanelEnvironment.self].value
self.environment = panelEnvironment
let isExpanded = availableSize.height > 34.0
let isExpanded = availableSize.height > panelEnvironment.height
let wasExpanded = self.isExpanded
self.isExpanded = isExpanded