From 0055ca09cba3ed369b9db117a37457600a0c3afe Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Thu, 24 Jul 2025 00:10:02 +0200 Subject: [PATCH 1/7] Disable call conflict resolution --- .../TelegramCallsUI/Sources/PresentationCallManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift index e5e051a84c..993e94adc0 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift @@ -362,7 +362,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { ) strongSelf.updateCurrentCall(call) })) - } else if let currentCall = self.currentCall, currentCall.peerId == firstState.1.id, currentCall.peerId.id._internalGetInt64Value() < firstState.0.account.peerId.id._internalGetInt64Value() { + } else if !"".isEmpty, let currentCall = self.currentCall, currentCall.peerId == firstState.1.id, currentCall.peerId.id._internalGetInt64Value() < firstState.0.account.peerId.id._internalGetInt64Value() { let _ = currentCall.hangUp().startStandalone() self.currentCallDisposable.set((combineLatest( From ef0669ab91e9e45c62ad54b1f2512650d3531033 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 24 Jul 2025 01:01:24 +0200 Subject: [PATCH 2/7] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 3 + .../DataAndStorageSettingsController.swift | 6 - .../Sources/RevenueWithdrawalController.swift | 2 +- .../TelegramEngine/Payments/StarGifts.swift | 22 ++ .../Payments/StarGiftsCollections.swift | 13 +- .../Sources/AgeVerificationScreen.swift | 197 +++++++----------- .../PeerInfoScreen/Sources/PeerInfoData.swift | 4 +- .../Sources/AddGiftsScreen.swift | 9 +- .../Sources/PeerInfoGiftsPaneNode.swift | 44 +++- versions.json | 2 +- 10 files changed, 166 insertions(+), 136 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index ff8bcd7404..a738ce6093 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -14749,6 +14749,9 @@ Sorry for the inconvenience."; "AgeVerification.Text.GB" = "To access this content, you must confirm you are at least **18** years old as required by UK law.\n\nThis is a one-time process using your phone's camera. Your selfie will not be stored by Telegram."; "AgeVerification.Verify" = "Verify My Age"; +"AgeVerification.Unavailable.Title" = "18+"; +"AgeVerification.Unavailable.Text" = "This media may contain sensitive content suitable only for adults."; + "AgeVerification.Success.Title" = "Age check passed!"; "AgeVerification.Success.Text" = "You can now view this content."; diff --git a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift index 082da648a9..5db34b987b 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift @@ -912,12 +912,6 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da } }) updateSensitiveContentDisposable.set(updateRemoteContentSettingsConfiguration(postbox: context.account.postbox, network: context.account.network, sensitiveContentEnabled: value).start()) - - if !value { - let _ = updateAgeVerificationState(engine: context.engine, { _ in - return AgeVerificationState(verificationPassed: false) - }).start() - } } if value { let presentationData = context.sharedContext.currentPresentationData.with { $0 } diff --git a/submodules/StatisticsUI/Sources/RevenueWithdrawalController.swift b/submodules/StatisticsUI/Sources/RevenueWithdrawalController.swift index 174ed110a3..274060e7f0 100644 --- a/submodules/StatisticsUI/Sources/RevenueWithdrawalController.swift +++ b/submodules/StatisticsUI/Sources/RevenueWithdrawalController.swift @@ -46,7 +46,7 @@ func confirmRevenueWithdrawalController(context: AccountContext, updatedPresenta } contentNode.updateIsChecking(true) - let signal = context.engine.peers.requestStarsRevenueWithdrawalUrl(peerId: peerId, ton: false, amount: nil, password: contentNode.password) + let signal = context.engine.peers.requestStarsRevenueWithdrawalUrl(peerId: peerId, ton: true, amount: nil, password: contentNode.password) disposable.set((signal |> deliverOnMainQueue).start(next: { url in dismissImpl?() completion(url) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index 7ea5299346..c36f1a4c14 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -2143,6 +2143,28 @@ public final class ProfileGiftsContext { collectionIds: self.collectionIds ) } + + public func withCollectionIds(_ collectionIds: [Int32]?) -> StarGift { + return StarGift( + gift: self.gift, + reference: self.reference, + fromPeer: self.fromPeer, + date: self.date, + text: self.text, + entities: self.entities, + nameHidden: self.nameHidden, + savedToProfile: self.savedToProfile, + pinnedToTop: self.pinnedToTop, + convertStars: self.convertStars, + canUpgrade: self.canUpgrade, + canExportDate: self.canExportDate, + upgradeStars: self.upgradeStars, + transferStars: self.transferStars, + canTransferDate: self.canTransferDate, + canResaleDate: self.canResaleDate, + collectionIds: collectionIds + ) + } } public enum DataState: Equatable { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGiftsCollections.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGiftsCollections.swift index e2244884bf..7adb42b08a 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGiftsCollections.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGiftsCollections.swift @@ -190,10 +190,15 @@ private func _internal_reorderStarGiftCollections(account: Account, peerId: Engi } } -private func _internal_updateStarGiftCollection(account: Account, peerId: EnginePeer.Id, collectionId: Int32, giftsContext: ProfileGiftsContext?, actions: [ProfileGiftsCollectionsContext.UpdateAction]) -> Signal { +private func _internal_updateStarGiftCollection(account: Account, peerId: EnginePeer.Id, collectionId: Int32, giftsContext: ProfileGiftsContext?, allGiftsContext: ProfileGiftsContext?, actions: [ProfileGiftsCollectionsContext.UpdateAction]) -> Signal { for action in actions { switch action { case let .addGifts(gifts): + let gifts = gifts.map { gift in + var collectionIds = gift.collectionIds ?? [] + collectionIds.append(collectionId) + return gift.withCollectionIds(collectionIds) + } giftsContext?.insertStarGifts(gifts: gifts) case let .removeGifts(gifts): giftsContext?.removeStarGifts(references: gifts) @@ -294,6 +299,7 @@ public final class ProfileGiftsCollectionsContext { private let queue: Queue = .mainQueue() private let account: Account private let peerId: EnginePeer.Id + private weak var allGiftsContext: ProfileGiftsContext? private let disposable = MetaDisposable() @@ -306,9 +312,10 @@ public final class ProfileGiftsCollectionsContext { return self.stateValue.get() } - public init(account: Account, peerId: EnginePeer.Id) { + public init(account: Account, peerId: EnginePeer.Id, allGiftsContext: ProfileGiftsContext?) { self.account = account self.peerId = peerId + self.allGiftsContext = allGiftsContext self.reload() } @@ -362,7 +369,7 @@ public final class ProfileGiftsCollectionsContext { public func updateCollection(id: Int32, actions: [UpdateAction]) -> Signal { let giftsContext = self.giftsContextForCollection(id: id) - return _internal_updateStarGiftCollection(account: self.account, peerId: self.peerId, collectionId: id, giftsContext: giftsContext, actions: actions) + return _internal_updateStarGiftCollection(account: self.account, peerId: self.peerId, collectionId: id, giftsContext: giftsContext, allGiftsContext: self.allGiftsContext, actions: actions) |> deliverOn(self.queue) |> afterNext { [weak self] collection in guard let self else { diff --git a/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift b/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift index 03dbf571ac..90adffae9b 100644 --- a/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift +++ b/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift @@ -398,127 +398,92 @@ func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor public func presentAgeVerification(context: AccountContext, parentController: ViewController, completion: @escaping () -> Void) { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let _ = (context.engine.data.get( - TelegramEngine.EngineData.Item.Configuration.ApplicationSpecificPreference(key: ApplicationSpecificPreferencesKeys.ageVerificationState) - ) |> deliverOnMainQueue).start(next: { [weak parentController] ageVerificationStatePreference in - let state = ageVerificationStatePreference?.get(AgeVerificationState.self) ?? AgeVerificationState.default - if state.verificationPassed { - completion() - } else { - let miniappPromise = Promise(nil) - var useVerifyAgeBot = false - if let value = context.currentAppConfiguration.with({ $0 }).data?["force_verify_age_bot"] as? Bool, value { - useVerifyAgeBot = value - } - if useVerifyAgeBot, let verifyAgeBotUsername = context.currentAppConfiguration.with({ $0 }).data?["verify_age_bot_username"] as? String { - miniappPromise.set(context.engine.peers.resolvePeerByName(name: verifyAgeBotUsername, referrer: nil) - |> mapToSignal { result in - if case let .result(peer) = result { - return .single(peer) + let _ = (contentSettingsConfiguration(network: context.account.network) + |> deliverOnMainQueue).start(next: { [weak parentController] settings in + if !settings.canAdjustSensitiveContent { + let alertController = textAlertController( + context: context, + title: presentationData.strings.AgeVerification_Unavailable_Title, + text: presentationData.strings.AgeVerification_Unavailable_Text, + actions: [] + ) + parentController?.present(alertController, in: .window(.root)) + return + } + let miniappPromise = Promise(nil) + var useVerifyAgeBot = false + if let value = context.currentAppConfiguration.with({ $0 }).data?["force_verify_age_bot"] as? Bool, value { + useVerifyAgeBot = value + } + if useVerifyAgeBot, let verifyAgeBotUsername = context.currentAppConfiguration.with({ $0 }).data?["verify_age_bot_username"] as? String { + miniappPromise.set(context.engine.peers.resolvePeerByName(name: verifyAgeBotUsername, referrer: nil) + |> mapToSignal { result in + if case let .result(peer) = result { + return .single(peer) + } + return .complete() + }) + } + let infoScreen = AgeVerificationScreen(context: context, completion: { [weak parentController] check, availability in + if check { + var requiredAge = 18 + if let value = context.currentAppConfiguration.with({ $0 }).data?["verify_age_min"] as? Double { + requiredAge = Int(value) + } + + let success = { [weak parentController] in + completion() + + let navigationController = parentController?.navigationController + Queue.mainQueue().after(2.0) { + let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: presentationData.strings.AgeVerification_Success_Title, text: presentationData.strings.AgeVerification_Success_Text, cancel: nil, destructive: false), action: { _ in return true }) + (navigationController?.viewControllers.last as? ViewController)?.present(controller, in: .current) } - return .complete() - }) - } - let infoScreen = AgeVerificationScreen(context: context, completion: { [weak parentController] check, availability in - if check { - var requiredAge = 18 - if let value = context.currentAppConfiguration.with({ $0 }).data?["verify_age_min"] as? Double { - requiredAge = Int(value) - } - - let success = { [weak parentController] in - let _ = updateAgeVerificationState(engine: context.engine, { _ in - return AgeVerificationState(verificationPassed: true) - }).start() - completion() - - let navigationController = parentController?.navigationController - Queue.mainQueue().after(2.0) { - let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: presentationData.strings.AgeVerification_Success_Title, text: presentationData.strings.AgeVerification_Success_Text, cancel: nil, destructive: false), action: { _ in return true }) - (navigationController?.viewControllers.last as? ViewController)?.present(controller, in: .current) - } - } - - let failure = { [weak parentController] in - let controller = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_banned", scale: 0.066, colors: [:], title: presentationData.strings.AgeVerification_Fail_Title, text: presentationData.strings.AgeVerification_Fail_Text, customUndoText: nil, timeout: nil), action: { _ in return true }) - parentController?.present(controller, in: .current) - } - - let _ = (miniappPromise.get() - |> take(1) - |> deliverOnMainQueue).start(next: { peer in - if let peer, let parentController { - context.sharedContext.openWebApp( - context: context, - parentController: parentController, - updatedPresentationData: nil, - botPeer: peer, - chatPeer: nil, - threadId: nil, - buttonText: "", - url: "", - simple: true, - source: .generic, - skipTermsOfService: true, - payload: nil, - verifyAgeCompletion: { age in - if age >= requiredAge { - success() - } else { - failure() - } - } - ) - } else { - let scanScreen = FaceScanScreen(context: context, availability: availability, completion: { age in + } + + let failure = { [weak parentController] in + let controller = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_banned", scale: 0.066, colors: [:], title: presentationData.strings.AgeVerification_Fail_Title, text: presentationData.strings.AgeVerification_Fail_Text, customUndoText: nil, timeout: nil), action: { _ in return true }) + parentController?.present(controller, in: .current) + } + + let _ = (miniappPromise.get() + |> take(1) + |> deliverOnMainQueue).start(next: { peer in + if let peer, let parentController { + context.sharedContext.openWebApp( + context: context, + parentController: parentController, + updatedPresentationData: nil, + botPeer: peer, + chatPeer: nil, + threadId: nil, + buttonText: "", + url: "", + simple: true, + source: .generic, + skipTermsOfService: true, + payload: nil, + verifyAgeCompletion: { age in if age >= requiredAge { success() } else { failure() } - }) - parentController?.push(scanScreen) - } - }) - } - }) - parentController?.push(infoScreen) - } + } + ) + } else { + let scanScreen = FaceScanScreen(context: context, availability: availability, completion: { age in + if age >= requiredAge { + success() + } else { + failure() + } + }) + parentController?.push(scanScreen) + } + }) + } + }) + parentController?.push(infoScreen) }) } - -public func updateAgeVerificationState(engine: TelegramEngine, _ f: @escaping (AgeVerificationState) -> AgeVerificationState) -> Signal { - return engine.preferences.update(id: ApplicationSpecificPreferencesKeys.ageVerificationState, { entry in - let currentSettings: AgeVerificationState - if let entry = entry?.get(AgeVerificationState.self) { - currentSettings = entry - } else { - currentSettings = .default - } - return SharedPreferencesEntry(f(currentSettings)) - }) -} - -public struct AgeVerificationState: Equatable, Codable { - public var verificationPassed: Bool - - public static var `default`: AgeVerificationState { - return AgeVerificationState(verificationPassed: false) - } - - public init(verificationPassed: Bool) { - self.verificationPassed = verificationPassed - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: StringCodingKey.self) - - self.verificationPassed = (try container.decode(Int32.self, forKey: "verificationPassed")) != 0 - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: StringCodingKey.self) - - try container.encode((self.verificationPassed ? 1 : 0) as Int32, forKey: "verificationPassed") - } -} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index 4736a1e72f..61e7d41580 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -1082,7 +1082,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen if case .user = kind { if isMyProfile || userPeerId != context.account.peerId { profileGiftsContext = existingProfileGiftsContext ?? ProfileGiftsContext(account: context.account, peerId: userPeerId) - profileGiftsCollectionsContext = existingProfileGiftsCollectionsContext ?? ProfileGiftsCollectionsContext(account: context.account, peerId: userPeerId) + profileGiftsCollectionsContext = existingProfileGiftsCollectionsContext ?? ProfileGiftsCollectionsContext(account: context.account, peerId: userPeerId, allGiftsContext: profileGiftsContext) } else { profileGiftsContext = nil profileGiftsCollectionsContext = nil @@ -1629,7 +1629,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen } let profileGiftsContext = ProfileGiftsContext(account: context.account, peerId: peerId) - let profileGiftsCollectionsContext = ProfileGiftsCollectionsContext(account: context.account, peerId: peerId) + let profileGiftsCollectionsContext = ProfileGiftsCollectionsContext(account: context.account, peerId: peerId, allGiftsContext: profileGiftsContext) let personalChannel = peerInfoPersonalOrLinkedChannel(context: context, peerId: peerId, isSettings: false) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift index 63383fd9ea..1337347ae8 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift @@ -101,10 +101,10 @@ final class AddGiftsScreenComponent: Component { } func scrollViewDidScroll(_ scrollView: UIScrollView) { - self.updateScrolling(transition: .immediate) + self.updateScrolling(interactive: true, transition: .immediate) } - private func updateScrolling(transition: ComponentTransition) { + private func updateScrolling(interactive: Bool = false, transition: ComponentTransition) { guard let environment = self.environment, let giftsListView = self.giftsListView else { return } @@ -120,6 +120,11 @@ final class AddGiftsScreenComponent: Component { if self.scrollView.contentSize != contentSize { self.scrollView.contentSize = contentSize } + + let bottomContentOffset = max(0.0, self.scrollView.contentSize.height - self.scrollView.contentOffset.y - self.scrollView.frame.height) + if interactive, bottomContentOffset < 200.0 { + self.giftsListView?.loadMore() + } } func update(component: AddGiftsScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index 219bcc9a2d..f172864a81 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -538,8 +538,6 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr ) self.parentController?.presentInGlobalOverlay(contextController) } - - func updateScrolling(interactive: Bool = false, transition: ComponentTransition) { if let params = self.currentParams { @@ -590,12 +588,12 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr ) )), isReorderable: collections.count > 1, - contextAction: { [weak self] sourceNode, gesture in + contextAction: canEditCollections ? { [weak self] sourceNode, gesture in guard let self else { return } self.openCollectionContextMenu(id: collection.id, sourceNode: sourceNode, gesture: gesture) - } + } : nil )) } @@ -1024,6 +1022,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr let undoController = UndoOverlayController( presentationData: currentParams.presentationData, content: .sticker(context: self.context, file: giftFile, loop: false, title: nil, text: text, undoText: nil, customAction: nil), + elevatedLayout: true, action: { _ in return true } ) self.parentController?.present(undoController, in: .current) @@ -1302,8 +1301,43 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Context_RemoveFromCollection, textColor: .destructive, textLayout: .twoLinesMax, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/RemoveFromCollection"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, f in f(.default) + guard let self else { + return + } + if let reference = gift.reference { - let _ = self?.profileGiftsCollections.removeGifts(id: id, gifts: [reference]).start() + let _ = self.profileGiftsCollections.removeGifts(id: id, gifts: [reference]).start() + } + + var giftFile: TelegramMediaFile? + var giftTitle: String? + switch gift.gift { + case let .generic(gift): + giftFile = gift.file + case let .unique(uniqueGift): + giftTitle = uniqueGift.title + " #\(presentationStringsFormattedNumber(uniqueGift.number, currentParams.presentationData.dateTimeFormat.groupingSeparator))" + for attribute in uniqueGift.attributes { + if case let .model(_, file, _) = attribute { + giftFile = file + } + } + } + + if let giftFile, let collection = self.collections?.first(where: { $0.id == id }) { + let text: String + if let giftTitle { + text = currentParams.presentationData.strings.PeerInfo_Gifts_RemovedFromCollectionUnique(giftTitle, collection.title).string + } else { + text = currentParams.presentationData.strings.PeerInfo_Gifts_RemovedFromCollection(collection.title).string + } + + let undoController = UndoOverlayController( + presentationData: currentParams.presentationData, + content: .sticker(context: self.context, file: giftFile, loop: false, title: nil, text: text, undoText: nil, customAction: nil), + elevatedLayout: true, + action: { _ in return true } + ) + self.parentController?.present(undoController, in: .current) } }))) } diff --git a/versions.json b/versions.json index 03d9138921..4ff99ef456 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "11.13.3", + "app": "11.13.4", "xcode": "16.2", "bazel": "8.2.1:22ff65b05869f6160e5157b1b425a14a62085d71d8baef571f462b8fe5a703a3", "macos": "15" From 5c2d7a642bc6852d6a4ccb4169adf91214ab3af1 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 24 Jul 2025 04:42:14 +0200 Subject: [PATCH 3/7] Fix update screen layout --- .../Sources/UpdateInfoController.swift | 23 ++++++++++--- .../Sources/UpdateInfoItem.swift | 33 +++++++++++++++---- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/submodules/TelegramUpdateUI/Sources/UpdateInfoController.swift b/submodules/TelegramUpdateUI/Sources/UpdateInfoController.swift index 3af24efa21..e4dc9fe8f2 100644 --- a/submodules/TelegramUpdateUI/Sources/UpdateInfoController.swift +++ b/submodules/TelegramUpdateUI/Sources/UpdateInfoController.swift @@ -90,6 +90,21 @@ private func updateInfoControllerEntries(theme: PresentationTheme, strings: Pres return entries } +private class UpdateInfoController: ItemListController { + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + self.view.layer.animatePosition(from: CGPoint(x: self.view.layer.position.x, y: self.view.layer.position.y + self.view.layer.bounds.size.height), to: self.view.layer.position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in + }) + } + + func animateOut(completion: (() -> Void)? = nil) { + self.view.layer.animatePosition(from: self.view.layer.position, to: CGPoint(x: self.view.layer.position.x, y: self.view.layer.position.y + self.view.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { _ in + completion?() + }) + } +} + public func updateInfoController(context: AccountContext, appUpdateInfo: AppUpdateInfo) -> ViewController { var dismissImpl: (() -> Void)? var linkActionImpl: ((TextLinkItemActionType, TextLinkItem) -> Void)? @@ -128,16 +143,16 @@ public func updateInfoController(context: AccountContext, appUpdateInfo: AppUpda actionsDisposable.dispose() } - let controller = ItemListController(sharedContext: context.sharedContext, state: signal) - controller.navigationPresentation = .modal + let controller = UpdateInfoController(sharedContext: context.sharedContext, state: signal) linkActionImpl = { [weak controller, weak context] action, itemLink in if let strongController = controller, let context = context { context.sharedContext.handleTextLinkAction(context: context, peerId: nil, navigateDisposable: navigateDisposable, controller: strongController, action: action, itemLink: itemLink) } } dismissImpl = { [weak controller] in - controller?.view.endEditing(true) - controller?.presentingViewController?.dismiss(animated: true, completion: nil) + controller?.animateOut(completion: { [weak controller] in + controller?.dismiss() + }) } return controller } diff --git a/submodules/TelegramUpdateUI/Sources/UpdateInfoItem.swift b/submodules/TelegramUpdateUI/Sources/UpdateInfoItem.swift index ff3135a8ad..d64c98a5fa 100644 --- a/submodules/TelegramUpdateUI/Sources/UpdateInfoItem.swift +++ b/submodules/TelegramUpdateUI/Sources/UpdateInfoItem.swift @@ -102,6 +102,7 @@ class UpdateInfoItemNode: ListViewItemNode { private let bottomStripeNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode private var linkHighlightingNode: LinkHighlightingNode? + private let maskNode: ASImageNode private let iconNode: ASImageNode private let overlayNode: ASImageNode @@ -127,6 +128,9 @@ class UpdateInfoItemNode: ListViewItemNode { self.bottomStripeNode = ASDisplayNode() self.bottomStripeNode.isLayerBacked = true + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false + self.iconNode = ASImageNode() self.iconNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 62.0, height: 62.0)) self.iconNode.isLayerBacked = true @@ -198,6 +202,7 @@ class UpdateInfoItemNode: ListViewItemNode { let inset: CGFloat let itemBackgroundColor: UIColor let itemSeparatorColor: UIColor + let verticalInset: CGFloat = 14.0 switch item.style { case .plain: @@ -221,10 +226,10 @@ class UpdateInfoItemNode: ListViewItemNode { switch item.style { case .plain: - contentSize = CGSize(width: params.width, height: 88.0 + textLayout.size.height + inset) + contentSize = CGSize(width: params.width, height: 88.0 + textLayout.size.height + verticalInset * 2.0) insets = itemListNeighborsPlainInsets(neighbors) case .blocks: - contentSize = CGSize(width: params.width, height: 88.0 + textLayout.size.height + inset) + contentSize = CGSize(width: params.width, height: 88.0 + textLayout.size.height + verticalInset * 2.0) insets = itemListNeighborsGroupedInsets(neighbors, params) } @@ -289,11 +294,19 @@ class UpdateInfoItemNode: ListViewItemNode { if strongSelf.bottomStripeNode.supernode == nil { strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) } + if strongSelf.maskNode.supernode == nil { + strongSelf.addSubnode(strongSelf.maskNode) + } + + let hasCorners = itemListHasRoundedBlockLayout(params) + var hasTopCorners = false + var hasBottomCorners = false switch neighbors.top { - case .sameSection(false): - strongSelf.topStripeNode.isHidden = true - default: - strongSelf.topStripeNode.isHidden = false + case .sameSection(false): + strongSelf.topStripeNode.isHidden = true + default: + hasTopCorners = true + strongSelf.topStripeNode.isHidden = hasCorners } let bottomStripeInset: CGFloat let bottomStripeOffset: CGFloat @@ -305,13 +318,19 @@ class UpdateInfoItemNode: ListViewItemNode { default: bottomStripeInset = 0.0 bottomStripeOffset = 0.0 + hasBottomCorners = true + strongSelf.bottomStripeNode.isHidden = hasCorners } + + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) + strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) } - let iconFrame = CGRect(origin: CGPoint(x: inset, y: inset), size: CGSize(width: 62.0, height: 62.0)) + let iconFrame = CGRect(origin: CGPoint(x: inset, y: verticalInset), size: CGSize(width: 62.0, height: 62.0)) strongSelf.iconNode.frame = iconFrame strongSelf.overlayNode.frame = iconFrame From 1cebd4ac6d4db3f4ed442c5beaa1b85a3920dd15 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 24 Jul 2025 05:08:19 +0200 Subject: [PATCH 4/7] Fix --- .../Sources/PeerInfoGiftsPaneNode.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index f172864a81..9690269e72 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -1297,7 +1297,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr } } - if case let .collection(id) = self.currentCollection { + if canManage, case let .collection(id) = self.currentCollection { items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Context_RemoveFromCollection, textColor: .destructive, textLayout: .twoLinesMax, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/RemoveFromCollection"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, f in f(.default) From ee67dcb9815599f8e4eacbba8054024feb94b523 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 24 Jul 2025 13:16:41 +0200 Subject: [PATCH 5/7] Disable list preprocessing on paste --- submodules/Pasteboard/Sources/Pasteboard.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/submodules/Pasteboard/Sources/Pasteboard.swift b/submodules/Pasteboard/Sources/Pasteboard.swift index fae2182d67..382ea4433a 100644 --- a/submodules/Pasteboard/Sources/Pasteboard.swift +++ b/submodules/Pasteboard/Sources/Pasteboard.swift @@ -220,10 +220,10 @@ private func indexToRoman(_ index: Int) -> String { } private func chatInputStateString(attributedString: NSAttributedString) -> NSAttributedString? { - let preprocessedString = preprocessLists(attributedString: attributedString) + //let preprocessedString = preprocessLists(attributedString: attributedString) - let string = NSMutableAttributedString(string: preprocessedString.string) - preprocessedString.enumerateAttributes(in: NSRange(location: 0, length: attributedString.length), options: [], using: { attributes, range, _ in + let string = NSMutableAttributedString(string: attributedString.string) + attributedString.enumerateAttributes(in: NSRange(location: 0, length: attributedString.length), options: [], using: { attributes, range, _ in if let value = attributes[.link], let url = (value as? URL)?.absoluteString { string.addAttribute(ChatTextInputAttributes.textUrl, value: ChatTextInputTextUrlAttribute(url: url), range: range) } From 7fe2c54488ea7547d86084dbf37e000d283a4f8e Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Thu, 24 Jul 2025 18:12:55 +0200 Subject: [PATCH 6/7] Hide features --- Telegram/BUILD | 1 - .../ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift | 5 +++-- .../Sources/StoryItemSetContainerComponent.swift | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Telegram/BUILD b/Telegram/BUILD index fb07598b4e..af18262d33 100644 --- a/Telegram/BUILD +++ b/Telegram/BUILD @@ -1736,7 +1736,6 @@ ios_application( xcodeproj( name = "Telegram_xcodeproj", - build_mode = "bazel", bazel_path = telegram_bazel_path, project_name = "Telegram", tags = ["manual"], diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift index cf04937b71..9f77697a22 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift @@ -2720,9 +2720,10 @@ final class ShareWithPeersScreenComponent: Component { if hasCover { itemCount += 1 } - if self.selectedOptions.contains(.pin) { + //TODO:release + /*if self.selectedOptions.contains(.pin) { itemCount += 1 - } + }*/ sections.append(ItemLayout.Section( id: 4, insets: UIEdgeInsets(top: 28.0, left: 0.0, bottom: 0.0, right: 0.0), diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 075a35744c..1ee476f5b5 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -6109,8 +6109,9 @@ public final class StoryItemSetContainerComponent: Component { var items: [ContextMenuItem] = [] + //TODO:release //TODO:localize - items.append(.action(ContextMenuActionItem(text: "Add to Album", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Folder"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in + /*items.append(.action(ContextMenuActionItem(text: "Add to Album", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Folder"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in guard let self, let c else { f(.default) return @@ -6203,7 +6204,7 @@ public final class StoryItemSetContainerComponent: Component { c.pushItems(items: .single(ContextController.Items(content: .list(items)))) } - }))) + })))*/ if case .file = component.slice.item.storyItem.media { var speedValue: String = presentationData.strings.PlaybackSpeed_Normal From 0699602bbc2756d1feaeb67df485e376dce44ad2 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Thu, 24 Jul 2025 18:16:55 +0200 Subject: [PATCH 7/7] Fix --- .../Sources/ShareWithPeersScreen.swift | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift index 9f77697a22..39047b01f8 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift @@ -1824,7 +1824,8 @@ final class ShareWithPeersScreenComponent: Component { sectionOffset += footerSize.height } else if section.id == 4 && section.itemCount > 0 { var sectionItemOffset: CGFloat = 0.0 - if self.selectedOptions.contains(.pin) { + //TODO:release + if self.selectedOptions.contains(.pin) && !"".isEmpty { let itemFrame = CGRect(origin: CGPoint(x: itemLayout.sideInset, y: sectionOffset + section.insets.top + sectionItemOffset), size: CGSize(width: itemLayout.containerSize.width, height: section.itemHeight)) if !visibleBounds.intersects(itemFrame) { continue @@ -2724,12 +2725,14 @@ final class ShareWithPeersScreenComponent: Component { /*if self.selectedOptions.contains(.pin) { itemCount += 1 }*/ - sections.append(ItemLayout.Section( - id: 4, - insets: UIEdgeInsets(top: 28.0, left: 0.0, bottom: 0.0, right: 0.0), - itemHeight: optionItemSize.height, - itemCount: 1 - )) + if itemCount != 0 { + sections.append(ItemLayout.Section( + id: 4, + insets: UIEdgeInsets(top: 28.0, left: 0.0, bottom: 0.0, right: 0.0), + itemHeight: optionItemSize.height, + itemCount: itemCount + )) + } } } else { sections.append(ItemLayout.Section(