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
@@ -130,7 +130,7 @@ private final class MediaGroupsGridAlbumItemNode : ListViewItemNode {
self.countNode = TextNode()
self.countNode.isUserInteractionEnabled = false
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
super.init(layerBacked: false, rotated: false, seeThrough: false)
self.addSubnode(self.containerNode)
self.containerNode.addSubnode(self.imageNode)
@@ -283,7 +283,7 @@ private class MediaGroupsAlbumGridItemNode: ListViewItemNode {
self.listNode = ListView()
self.listNode.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
super.init(layerBacked: false, rotated: false, seeThrough: false)
self.addSubnode(self.listNode)
}
@@ -172,7 +172,7 @@ class MediaGroupsAlbumItemNode: ListViewItemNode {
self.activateArea = AccessibilityAreaNode()
super.init(layerBacked: false, dynamicBounce: false)
super.init(layerBacked: false)
self.addSubnode(self.iconNode)
self.addSubnode(self.titleNode)
@@ -59,7 +59,7 @@ private class MediaGroupsHeaderItemNode: ListViewItemNode {
init() {
self.titleNode = TextNode()
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
super.init(layerBacked: false, rotated: false, seeThrough: false)
self.addSubnode(self.titleNode)
}
@@ -33,6 +33,7 @@ import ComponentFlow
import BundleIconComponent
import LottieComponent
import GlassBarButtonComponent
import AlertComponent
final class MediaPickerInteraction {
let downloadManager: AssetDownloadManager
@@ -1436,7 +1437,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
}
if asFile && hasHeic {
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.MediaPicker_JpegConversionText, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.MediaPicker_KeepHeic, action: {
controller.present(textAlertController(context: controller.context, updatedPresentationData: self.controller?.updatedPresentationData, title: nil, text: self.presentationData.strings.MediaPicker_JpegConversionText, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.MediaPicker_KeepHeic, action: {
proceed(false)
}), TextAlertAction(type: .genericAction, title: self.presentationData.strings.MediaPicker_ConvertToJpeg, action: {
proceed(true)
@@ -2042,26 +2043,26 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
if let _ = item as? TGMediaPickerGalleryPhotoItem {
if self.bannedSendPhotos != nil {
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
return false
}
} else if let _ = item as? TGMediaPickerGalleryVideoItem {
if self.bannedSendVideos != nil {
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
return false
}
} else if let asset = item as? TGMediaAsset {
if asset.isVideo {
if self.bannedSendVideos != nil {
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
return false
}
} else {
if self.bannedSendPhotos != nil {
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
return false
}
@@ -2112,6 +2113,10 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
}
}
self.selectedButtonNode.action = { [weak self] in
self?.selectedPressed()
}
self.navigationItem.titleView = self.titleView
if case let .assets(collection, mode) = self.subject, mode != .default {
@@ -2184,8 +2189,6 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
// }
// }
self.selectedButtonNode.addTarget(self, action: #selector(self.selectedPressed), forControlEvents: .touchUpInside)
self.scrollToTop = { [weak self] in
if let strongSelf = self {
if let webSearchController = strongSelf.webSearchController {
@@ -2220,26 +2223,26 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
if let self = self, let selectionState = self.interaction?.selectionState {
if let _ = item as? TGMediaPickerGalleryPhotoItem {
if self.bannedSendPhotos != nil {
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
return false
}
} else if let _ = item as? TGMediaPickerGalleryVideoItem {
if self.bannedSendVideos != nil {
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
return false
}
} else if let asset = item as? TGMediaAsset {
if asset.isVideo {
if self.bannedSendVideos != nil {
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
return false
}
} else {
if self.bannedSendPhotos != nil {
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
return false
}
@@ -2548,7 +2551,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
let useGlassButtons = (isBack || !self.controllerNode.scrolledToTop) && !self.controllerNode.isSwitchingAssetGroup
let barButtonSideInset: CGFloat = 16.0
let barButtonSize = CGSize(width: 40.0, height: 40.0)
let barButtonSize = CGSize(width: 44.0, height: 44.0)
var buttonTransition = ComponentTransition.easeInOut(duration: 0.25)
if case let .animated(duration, _) = transition, duration > 0.25 {
@@ -2580,13 +2583,13 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
transition: buttonTransition,
component: AnyComponent(GlassBarButtonComponent(
size: barButtonSize,
backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
backgroundColor: nil,
isDark: self.presentationData.theme.overallDarkAppearance,
state: useGlassButtons ? .glass : .generic,
state: .glass,
component: AnyComponentWithIdentity(id: isBack ? "back" : "close", component: AnyComponent(
BundleIconComponent(
name: isBack ? "Navigation/Back" : "Navigation/Close",
tintColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor
tintColor: self.presentationData.theme.chat.inputPanel.panelControlColor
)
)),
action: { [weak self] _ in
@@ -2611,15 +2614,15 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
transition: buttonTransition,
component: AnyComponent(GlassBarButtonComponent(
size: barButtonSize,
backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
backgroundColor: nil,
isDark: self.presentationData.theme.overallDarkAppearance,
state: useGlassButtons ? .glass : .generic,
state: .glass,
component: AnyComponentWithIdentity(id: "more", component: AnyComponent(
LottieComponent(
content: LottieComponent.AppBundleContent(
name: "anim_morewide"
),
color: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor,
color: self.presentationData.theme.chat.inputPanel.panelControlColor,
size: CGSize(width: 34.0, height: 34.0),
playOnce: self.moreButtonPlayOnce
)
@@ -2673,7 +2676,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
} else {
navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData)
}
self.navigationBar?.updatePresentationData(navigationBarPresentationData)
self.navigationBar?.updatePresentationData(navigationBarPresentationData, transition: .immediate)
self.titleView.theme = self.presentationData.theme
self.cancelButtonNode.theme = self.presentationData.theme
self.moreButtonNode.theme = self.presentationData.theme
@@ -2717,16 +2720,22 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
} else {
text = self.presentationData.strings.Attachment_CancelSelectionAlertText
}
let controller = textAlertController(context: self.context, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertNo, action: {
}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertYes, action: { [weak self] in
self?.dismissAllTooltips()
completion()
})])
controller.dismissed = { [weak self] _ in
let alertController = AlertScreen(
context: self.context,
title: nil,
text: text,
actions: [
.init(title: self.presentationData.strings.Attachment_CancelSelectionAlertNo),
.init(title: self.presentationData.strings.Attachment_CancelSelectionAlertYes, type: .default, action: { [weak self] in
self?.dismissAllTooltips()
completion()
}),
]
)
alertController.dismissed = { [weak self] _ in
self?.isDismissing = false
}
self.present(controller, in: .window(.root))
self.present(alertController, in: .window(.root))
} else {
completion()
}
@@ -3777,126 +3786,188 @@ public func avatarMediaPickerController(
performDelete: @escaping () -> Void,
completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void,
dismissed: @escaping () -> Void
) -> ViewController {
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
let updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>) = (presentationData, .single(presentationData))
let controller = AttachmentController(
context: context,
updatedPresentationData: updatedPresentationData,
style: .glass,
chatLocation: nil,
buttons: [.standalone],
initialButton: .standalone,
fromMenu: false,
hasTextInput: false,
makeEntityInputView: {
return nil
})
controller.forceSourceRect = true
controller.getSourceRect = getSourceRect
controller.requestController = { [weak controller] _, present in
var mainButtonState: AttachmentMainButtonState?
if canDelete {
mainButtonState = AttachmentMainButtonState(text: presentationData.strings.MediaPicker_RemovePhoto, font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true, hasShimmer: false)
) -> (controller: ViewController?, holder: Any?) {
if #available(iOS 14.0, *), PHPhotoLibrary.authorizationStatus(for: .readWrite) != .authorized {
final class PickerDelegate: NSObject, PHPickerViewControllerDelegate {
var completion: ((Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void)?
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
for item in results {
if item.itemProvider.canLoadObject(ofClass: UIImage.self) {
item.itemProvider.loadObject(ofClass: UIImage.self) { image, error in
if let uiImage = image as? UIImage {
Queue.mainQueue().async {
self.completion?(uiImage, nil, CGRect(), nil, false, { _ in return nil }, {})
}
}
}
}
}
}
}
let mediaPickerController = MediaPickerScreenImpl(
let holder = PickerDelegate()
holder.completion = completion
let openMediaPicker = {
var configuration = PHPickerConfiguration(photoLibrary: .shared())
configuration.filter = .images
configuration.selectionLimit = 1
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = holder
(context.sharedContext.mainWindow?.viewController as? NavigationController)?.topViewController?.present(picker, animated: true, completion: nil)
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = ActionSheetController(presentationData: presentationData)
let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated()
}
var items: [ActionSheetButtonItem] = [
ActionSheetButtonItem(title: presentationData.strings.Settings_SetNewProfilePhotoOrVideo, color: .accent, action: {
dismissAction()
openMediaPicker()
}),
ActionSheetButtonItem(title: presentationData.strings.ProfilePhoto_SetEmoji, color: .accent, action: {
dismissAction()
completion(nil, nil, CGRect(), nil, false, { _ in return nil }, {})
})
]
if canDelete {
items.append(ActionSheetButtonItem(title: presentationData.strings.MediaPicker_RemovePhoto, color: .destructive, action: {
dismissAction()
performDelete()
}))
}
controller.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
])
return (controller, holder)
} else {
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
let updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>) = (presentationData, .single(presentationData))
let controller = AttachmentController(
context: context,
updatedPresentationData: updatedPresentationData,
style: .glass,
peer: nil,
threadTitle: nil,
chatLocation: nil,
bannedSendPhotos: nil,
bannedSendVideos: nil,
subject: .assets(nil, .createAvatar),
mainButtonState: mainButtonState,
mainButtonAction: { [weak controller] in
controller?.dismiss(animated: true)
performDelete()
}
)
mediaPickerController.customSelection = { controller, result in
if let result = result as? PHAsset {
controller.updateHiddenMediaId(result.localIdentifier)
if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) {
let transitionOut: (Bool?) -> (UIView, CGRect)? = { isNew in
if let isNew {
if isNew {
controller.updateHiddenMediaId(nil)
if let transitionView = controller.defaultTransitionView() {
return (transitionView, transitionView.bounds)
}
} else if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) {
return (transitionView, transitionView.bounds)
}
}
return nil
}
completion(result, transitionView, transitionView.bounds, controller.transitionImage(for: result.localIdentifier), false, transitionOut, { [weak controller] in
controller?.updateHiddenMediaId(nil)
})
}
}
}
mediaPickerController.openAvatarEditor = { [weak controller] in
completion(nil, nil, .zero, nil, false, { _ in return nil }, {
buttons: [.standalone],
initialButton: .standalone,
fromMenu: false,
hasTextInput: false,
makeEntityInputView: {
return nil
})
controller?.dismiss(animated: true)
}
mediaPickerController.openCamera = { [weak controller] cameraHolder in
let _ = controller
guard let cameraHolder = cameraHolder as? CameraHolder else {
return
controller.forceSourceRect = true
controller.getSourceRect = getSourceRect
controller.requestController = { [weak controller] _, present in
var mainButtonState: AttachmentMainButtonState?
if canDelete {
mainButtonState = AttachmentMainButtonState(text: presentationData.strings.MediaPicker_RemovePhoto, font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true, hasShimmer: false)
}
var returnToCameraImpl: (() -> Void)?
let cameraScreen = context.sharedContext.makeCameraScreen(
let mediaPickerController = MediaPickerScreenImpl(
context: context,
mode: .avatar,
cameraHolder: cameraHolder,
transitionIn: CameraScreenTransitionIn(
sourceView: cameraHolder.parentView,
sourceRect: cameraHolder.parentView.bounds,
sourceCornerRadius: 0.0,
useFillAnimation: false
),
transitionOut: { _ in
return CameraScreenTransitionOut(
destinationView: cameraHolder.parentView,
destinationRect: cameraHolder.parentView.bounds,
destinationCornerRadius: 0.0
)
},
completion: { result, commit in
completion(result, nil, .zero, nil, true, { _ in return nil }, {
returnToCameraImpl?()
})
},
transitionedOut: { [weak cameraHolder] in
if let cameraHolder {
cameraHolder.restore()
}
updatedPresentationData: updatedPresentationData,
style: .glass,
peer: nil,
threadTitle: nil,
chatLocation: nil,
bannedSendPhotos: nil,
bannedSendVideos: nil,
subject: .assets(nil, .createAvatar),
mainButtonState: mainButtonState,
mainButtonAction: { [weak controller] in
controller?.dismiss(animated: true)
performDelete()
}
)
controller?.push(cameraScreen)
returnToCameraImpl = { [weak cameraScreen] in
if let cameraScreen = cameraScreen as? CameraScreen {
cameraScreen.returnFromEditor()
mediaPickerController.customSelection = { controller, result in
if let result = result as? PHAsset {
controller.updateHiddenMediaId(result.localIdentifier)
if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) {
let transitionOut: (Bool?) -> (UIView, CGRect)? = { isNew in
if let isNew {
if isNew {
controller.updateHiddenMediaId(nil)
if let transitionView = controller.defaultTransitionView() {
return (transitionView, transitionView.bounds)
}
} else if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) {
return (transitionView, transitionView.bounds)
}
}
return nil
}
completion(result, transitionView, transitionView.bounds, controller.transitionImage(for: result.localIdentifier), false, transitionOut, { [weak controller] in
controller?.updateHiddenMediaId(nil)
})
}
}
}
mediaPickerController.openAvatarEditor = { [weak controller] in
completion(nil, nil, .zero, nil, false, { _ in return nil }, {
})
controller?.dismiss(animated: true)
}
mediaPickerController.openCamera = { [weak controller] cameraHolder in
let _ = controller
guard let cameraHolder = cameraHolder as? CameraHolder else {
return
}
var returnToCameraImpl: (() -> Void)?
let cameraScreen = context.sharedContext.makeCameraScreen(
context: context,
mode: .avatar,
cameraHolder: cameraHolder,
transitionIn: CameraScreenTransitionIn(
sourceView: cameraHolder.parentView,
sourceRect: cameraHolder.parentView.bounds,
sourceCornerRadius: 0.0,
useFillAnimation: false
),
transitionOut: { _ in
return CameraScreenTransitionOut(
destinationView: cameraHolder.parentView,
destinationRect: cameraHolder.parentView.bounds,
destinationCornerRadius: 0.0
)
},
completion: { result, commit in
completion(result, nil, .zero, nil, true, { _ in return nil }, {
returnToCameraImpl?()
})
},
transitionedOut: { [weak cameraHolder] in
if let cameraHolder {
cameraHolder.restore()
}
}
)
controller?.push(cameraScreen)
returnToCameraImpl = { [weak cameraScreen] in
if let cameraScreen = cameraScreen as? CameraScreen {
cameraScreen.returnFromEditor()
}
}
}
present(mediaPickerController, mediaPickerController.mediaPickerContext)
}
present(mediaPickerController, mediaPickerController.mediaPickerContext)
controller.willDismiss = {
dismissed()
}
controller.navigationPresentation = .flatModal
controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
return (controller, nil)
}
controller.willDismiss = {
dismissed()
}
controller.navigationPresentation = .flatModal
controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
return controller
}
@@ -3961,12 +4032,13 @@ public func coverMediaPickerController(
return controller
}
private class SelectedButtonNode: HighlightTrackingButtonNode {
private class SelectedButtonNode: ASDisplayNode {
private let containerView: UIView
private let backgroundView: GlassBackgroundView?
private let background: ASImageNode?
private let icon = ASImageNode()
private let label = ImmediateAnimatedCountLabelNode()
private let button = HighlightTrackingButton()
private let glass: Bool
@@ -3982,6 +4054,8 @@ private class SelectedButtonNode: HighlightTrackingButtonNode {
private var count: Int32 = 0
var action: () -> Void = {}
init(theme: PresentationTheme, glass: Bool) {
self.theme = theme
self.glass = glass
@@ -4007,6 +4081,9 @@ private class SelectedButtonNode: HighlightTrackingButtonNode {
self.view.addSubview(self.containerView)
if let backgroundView = self.backgroundView {
backgroundView.contentView.addSubnode(self.icon)
backgroundView.contentView.addSubnode(self.label)
backgroundView.contentView.addSubview(self.button)
self.containerView.addSubview(backgroundView)
}
if let background = self.background {
@@ -4014,35 +4091,19 @@ private class SelectedButtonNode: HighlightTrackingButtonNode {
self.containerView.addSubnode(background)
}
self.containerView.addSubnode(self.icon)
self.containerView.addSubnode(self.label)
self.highligthedChanged = { [weak self] highlighted in
if let self {
if glass {
let transition = ComponentTransition(animation: .curve(duration: highlighted ? 0.25 : 0.35, curve: .spring))
if highlighted {
transition.setScale(view: self.containerView, scale: 1.2)
} else {
transition.setScale(view: self.containerView, scale: 1.0)
}
} else {
if highlighted {
self.containerView.layer.removeAnimation(forKey: "opacity")
self.containerView.alpha = 0.4
} else {
self.containerView.alpha = 1.0
self.containerView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
}
}
}
}
self.button.addTarget(self, action: #selector(self.tapped), for: .touchUpInside)
}
@objc private func tapped() {
self.action()
}
func update(count: Int32) -> CGSize {
self.count = count
let diameter: CGFloat = self.glass ? 40.0 : 21.0
let diameter: CGFloat = self.glass ? 44.0 : 21.0
let font = self.glass ? Font.with(size: 17.0, weight: .medium, traits: [.monospacedNumbers]) : Font.with(size: 15.0, design: .round, weight: .semibold, traits: [.monospacedNumbers])
let stringValue = "\(max(1, count))"
@@ -4078,11 +4139,13 @@ private class SelectedButtonNode: HighlightTrackingButtonNode {
self.containerView.frame = backgroundFrame
if let backgroundView = self.backgroundView {
backgroundView.frame = backgroundFrame
backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.size.height * 0.5, isDark: false, tintColor: .init(kind: .custom, color: self.theme.list.itemCheckColors.fillColor), transition: .immediate)
backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.size.height * 0.5, isDark: false, tintColor: .init(kind: .custom, color: self.theme.list.itemCheckColors.fillColor), isInteractive: true, transition: .immediate)
}
if let background = self.background {
background.frame = backgroundFrame
}
self.button.frame = CGRect(origin: .zero, size: size)
return size
}
@@ -18,7 +18,7 @@ final class MediaPickerTitleView: UIView {
public var theme: PresentationTheme {
didSet {
self.titleNode.attributedText = NSAttributedString(string: self.title, font: NavigationBar.titleFont, textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor)
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor)
self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle, font: Font.regular(12.0), textColor: self.isDark ? .white.withAlphaComponent(0.5) : self.theme.rootController.navigationBar.secondaryTextColor)
self.segmentedControlNode.updateTheme(SegmentedControlTheme(theme: self.theme))
if self.glass {
@@ -31,7 +31,7 @@ final class MediaPickerTitleView: UIView {
public var isDark: Bool = false {
didSet {
if self.isDark != oldValue {
self.titleNode.attributedText = NSAttributedString(string: self.title, font: NavigationBar.titleFont, textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor)
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor)
self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle, font: Font.regular(12.0), textColor: self.isDark ? .white.withAlphaComponent(0.5) : self.theme.rootController.navigationBar.secondaryTextColor)
if self.glass {
self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Navigation/TitleExpand"), color: self.isDark ? UIColor.white.withAlphaComponent(0.5) : self.theme.rootController.navigationBar.primaryTextColor.withAlphaComponent(0.4))
@@ -44,7 +44,7 @@ final class MediaPickerTitleView: UIView {
public var title: String = "" {
didSet {
if self.title != oldValue {
self.titleNode.attributedText = NSAttributedString(string: self.title, font: NavigationBar.titleFont, textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor)
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor)
self.setNeedsLayout()
}
}
@@ -260,7 +260,7 @@ final class MediaPickerTitleView: UIView {
totalHeight += subtitleSize.height
}
let verticalOffset: CGFloat = self.glass ? 3.0 : 0.0
let verticalOffset: CGFloat = self.glass ? -1.0 : 0.0
let arrowOffset: CGFloat = self.glass ? 1.0 : 5.0
var totalWidth = titleSize.width