From db099664995da9476e324d6286990de1691f80f0 Mon Sep 17 00:00:00 2001 From: Mikhail Filimonov Date: Sat, 19 Jul 2025 20:29:53 +0200 Subject: [PATCH 01/18] reorder folders --- .../Messages/StoryListContext.swift | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift index dee5ba492b..86fc8ed6f0 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift @@ -1424,6 +1424,27 @@ public final class PeerStoryListContext: StoryListContext { }).start() } + func reorderFolders(_ folders: [State.Folder]) { + var state = self.stateValue + state.availableFolders = folders + self.stateValue = state + + let peerId = self.peerId + let isArchived = self.isArchived + let items = state.items + let pinnedIds = state.pinnedIds + let totalCount = state.totalCount + let folders = folders + let _ = (self.account.postbox.transaction { transaction -> Void in + let key = ValueBoxKey(length: 8 + 1) + key.setInt64(0, value: peerId.toInt64()) + key.setInt8(8, value: isArchived ? 1 : 0) + if let entry = CodableEntry(CachedPeerStoryListHead(items: items.prefix(100).map { .item($0.storyItem.asStoryItem()) }, pinnedIds: pinnedIds, totalCount: Int32(totalCount), folders: folders)) { + transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry) + } + }).start() + } + func addToFolder(id: Int64, items: [EngineStoryItem]) { let peerId = self.peerId let _ = (self.account.postbox.transaction { transaction -> Void in @@ -1655,6 +1676,13 @@ public final class PeerStoryListContext: StoryListContext { } } + public func reorderFolders(_ folders: [State.Folder]) { + self.impl.with { impl in + impl.reorderFolders(folders) + } + } + + public func addToFolder(id: Int64, items: [EngineStoryItem]) { self.impl.with { impl in impl.addToFolder(id: id, items: items) From c825438b72d40f9ea8d4282bcd7e361c647ece3e Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sun, 20 Jul 2025 19:49:14 +0200 Subject: [PATCH 02/18] Various improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 4 ++-- .../Sources/BotCheckoutController.swift | 3 +++ .../Payments/BotPaymentForm.swift | 4 ++++ .../Sources/AgeVerificationScreen.swift | 7 ++++++- .../Sources/FaceScanScreen.swift | 20 ++++++++++--------- .../Sources/GiftSetupScreen.swift | 14 +++++++++++++ .../Sources/GiftViewScreen.swift | 5 ++++- .../Sources/AddGiftsScreen.swift | 8 ++++++-- .../Sources/GiftsListView.swift | 10 +++++++--- .../Sources/PeerInfoGiftsPaneNode.swift | 10 +++++++++- .../Sources/TelegramRootController.swift | 17 ---------------- 11 files changed, 66 insertions(+), 36 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 7051fae35d..c369488679 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -14736,8 +14736,8 @@ Sorry for the inconvenience."; "Gift.Options.Gift.BuyLimitReached_any" = "You've already sent %@ of these gifts, and it's the limit."; "AgeVerification.Title" = "Age Verification"; -"AgeVerification.Text" = "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.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.Text" = "To access this content, you must confirm you are at least **18** years old.\n\nThis is a one-time process using your phone's camera. Your selfie will not be stored by Telegram."; +"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.Success.Title" = "Age check passed!"; diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutController.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutController.swift index 95baaa332f..035fa49f99 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutController.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutController.swift @@ -12,6 +12,7 @@ public final class BotCheckoutController: ViewController { public enum FetchError { case generic case disallowedStarGifts + case starGiftsUserLimit } public let form: BotPaymentForm @@ -58,6 +59,8 @@ public final class BotCheckoutController: ViewController { switch error { case .disallowedStarGift: return .disallowedStarGifts + case .starGiftUserLimit: + return .starGiftsUserLimit default: return .generic } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift index 9330c8db39..173c4b50c5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift @@ -180,6 +180,7 @@ public enum BotPaymentFormRequestError { case noPaymentNeeded case disallowedStarGift case starGiftResellTooEarly(Int32) + case starGiftUserLimit } extension BotPaymentInvoice { @@ -488,6 +489,8 @@ func _internal_fetchBotPaymentForm(accountPeerId: PeerId, postbox: Postbox, netw if let value = Int32(timeout) { return .fail(.starGiftResellTooEarly(value)) } + } else if error.errorDescription == "STARGIFT_USER_USAGE_LIMITED" { + return .fail(.starGiftUserLimit) } return .fail(.generic) } @@ -651,6 +654,7 @@ public enum SendBotPaymentFormError { case alreadyPaid case starGiftOutOfStock case disallowedStarGift + case starGiftUserLimit } public enum SendBotPaymentResult { diff --git a/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift b/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift index efb8d18c65..bcf91273b2 100644 --- a/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift +++ b/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift @@ -407,7 +407,12 @@ public func presentAgeVerification(context: AccountContext, parentController: Vi } else { let infoScreen = AgeVerificationScreen(context: context, completion: { [weak parentController] check, availability in if check { - let scanScreen = FaceScanScreen(context: context, availability: availability, completion: { [weak parentController] passed in + var requiredAge = 18 + if let value = context.currentAppConfiguration.with({ $0 }).data?["verify_age_min"] as? Double { + requiredAge = Int(value) + } + + let scanScreen = FaceScanScreen(context: context, availability: availability, requiredAge: requiredAge, completion: { [weak parentController] passed in if passed { let _ = updateAgeVerificationState(engine: context.engine, { _ in return AgeVerificationState(verificationPassed: passed) diff --git a/submodules/TelegramUI/Components/FaceScanScreen/Sources/FaceScanScreen.swift b/submodules/TelegramUI/Components/FaceScanScreen/Sources/FaceScanScreen.swift index 334469152d..2be0e9db59 100644 --- a/submodules/TelegramUI/Components/FaceScanScreen/Sources/FaceScanScreen.swift +++ b/submodules/TelegramUI/Components/FaceScanScreen/Sources/FaceScanScreen.swift @@ -20,20 +20,21 @@ import ZipArchive import PlainButtonComponent import MultilineTextComponent -private let requiredAge = 18 - final class FaceScanScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext let availability: Signal + let requiredAge: Int init( context: AccountContext, - availability: Signal + availability: Signal, + requiredAge: Int ) { self.context = context self.availability = availability + self.requiredAge = requiredAge } static func ==(lhs: FaceScanScreenComponent, rhs: FaceScanScreenComponent) -> Bool { @@ -109,7 +110,7 @@ final class FaceScanScreenComponent: Component { self.backgroundColor = .black - self.previewLayer.backgroundColor = UIColor.red.cgColor + //self.previewLayer.backgroundColor = UIColor.red.cgColor self.previewLayer.videoGravity = .resizeAspectFill self.layer.addSublayer(previewLayer) @@ -240,7 +241,7 @@ final class FaceScanScreenComponent: Component { let targetCenter = CGPoint(x: 0.5, y: 0.5) let distance = sqrt(pow(faceCenter.x - targetCenter.x, 2) + pow(faceCenter.y - targetCenter.y, 2)) - if distance < 0.35 { + if distance < 0.24 { switch processState { case .waitingForFace: self.processState = .positioning @@ -306,7 +307,7 @@ final class FaceScanScreenComponent: Component { } private func fillSegment(_ segmentIndex: Int) { - guard !self.completedAngles.contains(segmentIndex) else { + guard let component = self.component, !self.completedAngles.contains(segmentIndex) else { return } self.completedAngles.insert(segmentIndex) @@ -320,7 +321,7 @@ final class FaceScanScreenComponent: Component { if !self.ages.isEmpty { let averageAge = self.ages.reduce(0, +) / Double(self.ages.count) if let environment = self.environment, let controller = environment.controller() as? FaceScanScreen { - controller.completion(averageAge >= Double(requiredAge)) + controller.completion(averageAge >= Double(component.requiredAge)) controller.dismiss(animated: true) } } else { @@ -457,7 +458,6 @@ final class FaceScanScreenComponent: Component { self.frameView.frame = frameViewFrame self.frameView.update(size: frameViewFrame.size) - //TODO:localize var instructionString = environment.strings.FaceScan_Instruction_Position switch self.processState { case .waitingForFace, .positioning: @@ -550,6 +550,7 @@ public final class FaceScanScreen: ViewControllerComponentContainer { public init( context: AccountContext, availability: Signal, + requiredAge: Int, completion: @escaping (Bool) -> Void ) { self.context = context @@ -557,7 +558,8 @@ public final class FaceScanScreen: ViewControllerComponentContainer { super.init(context: context, component: FaceScanScreenComponent( context: context, - availability: availability + availability: availability, + requiredAge: requiredAge ), navigationBarAppearance: .none, theme: .default, updatedPresentationData: nil) self.title = "" diff --git a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift index eec3b4716c..fd33e2dc21 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift @@ -363,6 +363,8 @@ final class GiftSetupScreenComponent: Component { let entities = generateChatInputTextEntities(self.textInputState.text) var finalPrice: Int64 + var perUserLimit: Int32? + var giftFile: TelegramMediaFile? let source: BotPaymentInvoiceSource switch component.subject { case let .premium(product): @@ -377,6 +379,8 @@ final class GiftSetupScreenComponent: Component { if self.includeUpgrade, let upgradeStars = starGift.upgradeStars { finalPrice += upgradeStars } + perUserLimit = starGift.perUserLimit?.total + giftFile = starGift.file source = .starGift(hideName: self.hideName, includeUpgrade: self.includeUpgrade, peerId: peerId, giftId: starGift.id, text: self.textInputState.text.string, entities: entities) } @@ -395,6 +399,8 @@ final class GiftSetupScreenComponent: Component { switch error { case .disallowedStarGifts: return .fail(.disallowedStarGift) + case .starGiftsUserLimit: + return .fail(.starGiftUserLimit) default: return .fail(.generic) } @@ -468,6 +474,14 @@ final class GiftSetupScreenComponent: Component { var errorText: String? switch error { + case .starGiftUserLimit: + if let perUserLimit, let giftFile { + let text = presentationData.strings.Gift_Options_Gift_BuyLimitReached(perUserLimit) + let undoController = UndoOverlayController(presentationData: presentationData, content: .sticker(context: component.context, file: giftFile, loop: true, title: nil, text: text, undoText: nil, customAction: nil), action: { _ in return false }) + controller.present(undoController, in: .current) + return + } + return case .starGiftOutOfStock: errorText = presentationData.strings.Gift_Send_ErrorOutOfStock case .disallowedStarGift: diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 3f2bb8baec..9ff2dc3ff3 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -288,11 +288,14 @@ private final class GiftViewSheetContent: CombinedComponent { } var minRequiredAmount = StarsAmount(value: 100, nanos: 0) + var canUpgrade = false if let resellStars = self.subject.arguments?.resellStars { minRequiredAmount = StarsAmount(value: resellStars, nanos: 0) + } else if let arguments = self.subject.arguments, arguments.canUpgrade && arguments.upgradeStars == nil { + canUpgrade = true } - if let starsContext = context.starsContext, let state = starsContext.currentState, state.balance < minRequiredAmount { + if let starsContext = context.starsContext, let state = starsContext.currentState, state.balance < minRequiredAmount || canUpgrade { self.optionsDisposable = (context.engine.payments.starsTopUpOptions() |> deliverOnMainQueue).start(next: { [weak self] options in guard let self else { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift index b1ec546109..23cdd4f560 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift @@ -23,17 +23,20 @@ final class AddGiftsScreenComponent: Component { let context: AccountContext let peerId: EnginePeer.Id let collectionId: Int32 + let remainingCount: Int32 let profileGifts: ProfileGiftsContext init( context: AccountContext, peerId: EnginePeer.Id, collectionId: Int32, + remainingCount: Int32, profileGifts: ProfileGiftsContext ) { self.context = context self.peerId = peerId self.collectionId = collectionId + self.remainingCount = remainingCount self.profileGifts = profileGifts } @@ -128,7 +131,7 @@ final class AddGiftsScreenComponent: Component { if let current = self.giftsListView { giftsListView = current } else { - giftsListView = GiftsListView(context: component.context, peerId: component.peerId, profileGifts: component.profileGifts, giftsCollections: nil, canSelect: true, ignoreCollection: component.collectionId) + giftsListView = GiftsListView(context: component.context, peerId: component.peerId, profileGifts: component.profileGifts, giftsCollections: nil, canSelect: true, ignoreCollection: component.collectionId, remainingSelectionCount: component.remainingCount) giftsListView.selectionUpdated = { [weak self] in guard let self else { return @@ -248,6 +251,7 @@ public final class AddGiftsScreen: ViewControllerComponentContainer { context: AccountContext, peerId: EnginePeer.Id, collectionId: Int32, + remainingCount: Int32, completion: @escaping ([ProfileGiftsContext.State.StarGift]) -> Void ) { self.context = context @@ -264,10 +268,10 @@ public final class AddGiftsScreen: ViewControllerComponentContainer { context: context, peerId: peerId, collectionId: collectionId, + remainingCount: remainingCount, profileGifts: self.profileGifts ), navigationBarAppearance: .default, theme: .default, updatedPresentationData: nil) - self.title = presentationData.strings.AddGifts_Title self.navigationPresentation = .modal diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift index ef195fb43d..b42e5eae26 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift @@ -36,6 +36,7 @@ final class GiftsListView: UIView { private let canSelect: Bool private let ignoreCollection: Int32? + private let remainingSelectionCount: Int32 private var dataDisposable: Disposable? @@ -124,13 +125,14 @@ final class GiftsListView: UIView { var contextAction: ((ProfileGiftsContext.State.StarGift, UIView, ContextGesture) -> Void)? var addToCollection: (() -> Void)? - init(context: AccountContext, peerId: PeerId, profileGifts: ProfileGiftsContext, giftsCollections: ProfileGiftsCollectionsContext?, canSelect: Bool, ignoreCollection: Int32? = nil) { + init(context: AccountContext, peerId: PeerId, profileGifts: ProfileGiftsContext, giftsCollections: ProfileGiftsCollectionsContext?, canSelect: Bool, ignoreCollection: Int32? = nil, remainingSelectionCount: Int32 = 0) { self.context = context self.peerId = peerId self.profileGifts = profileGifts self.giftsCollections = giftsCollections self.canSelect = canSelect self.ignoreCollection = ignoreCollection + self.remainingSelectionCount = remainingSelectionCount if let value = context.currentAppConfiguration.with({ $0 }).data?["stargifts_pinned_to_top_limit"] as? Double { self.maxPinnedCount = Int(value) @@ -548,8 +550,10 @@ final class GiftsListView: UIView { if self.selectedItemIds.contains(itemReferenceId) { self.selectedItemIds.remove(itemReferenceId) } else { - self.selectedItemIds.insert(itemReferenceId) - self.selectedItemsMap[itemReferenceId] = product + if self.selectedItemIds.count < self.remainingSelectionCount { + self.selectedItemIds.insert(itemReferenceId) + self.selectedItemsMap[itemReferenceId] = product + } } self.selectionUpdated() self.updateScrolling(transition: .easeInOut(duration: 0.25)) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index ab1595fa59..a736019aad 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -242,7 +242,15 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr } public func addGiftsToCollection(id: Int32) { - let screen = AddGiftsScreen(context: self.context, peerId: self.peerId, collectionId: id, completion: { [weak self] gifts in + var collectionGiftsMaxCount: Int32 = 1000 + if let value = self.context.currentAppConfiguration.with({ $0 }).data?["stargifts_collection_gifts_limit"] as? Double { + collectionGiftsMaxCount = Int32(value) + } + var remainingCount = collectionGiftsMaxCount + if let currentCount = self.giftsListView.profileGifts.currentState?.count { + remainingCount = max(0, collectionGiftsMaxCount - currentCount) + } + let screen = AddGiftsScreen(context: self.context, peerId: self.peerId, collectionId: id, remainingCount: remainingCount, completion: { [weak self] gifts in guard let self else { return } diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 683e3bb911..35348db5b1 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -31,7 +31,6 @@ import PeerInfoScreen import PeerInfoStoryGridScreen import ShareWithPeersScreen import ChatEmptyNode -//import FaceScanScreen import UndoUI private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceholderNode { @@ -237,22 +236,6 @@ public final class TelegramRootController: NavigationController, TelegramRootCon self.accountSettingsController = accountSettingsController self.rootTabController = tabBarController self.pushViewController(tabBarController, animated: false) - -// Queue.mainQueue().after(1.0) { -// let context = self.context -// let infoScreen = AgeVerificationScreen(context: context, completion: { [weak chatListController] proceed in -// if proceed { -// let scanScreen = FaceScanScreen(context: context, completion: { success in -// let controller = UndoOverlayController(presentationData: self.presentationData, content: .actionSucceeded(title: "Age check passed!", text: "You can now view this content.", cancel: nil, destructive: false), elevatedLayout: true, action: { _ in return true }) -// Queue.mainQueue().after(0.1) { -// chatListController?.present(controller, in: .window(.root)) -// } -// }) -// chatListController?.push(scanScreen) -// } -// }) -// chatListController.push(infoScreen) -// } } public func updateRootControllers(showCallsTab: Bool) { From 9d703f5b60460b62256f7c50d0aad9e7b2554ece Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 21 Jul 2025 00:38:47 +0200 Subject: [PATCH 03/18] Various fixes --- .../Sources/AccountContext.swift | 2 +- .../Sources/ChatListController.swift | 3 +- .../Sources/ChatListSearchListPaneNode.swift | 9 ++- .../Sources/AgeVerificationScreen.swift | 80 +++++++++++++++---- .../Sources/FaceScanScreen.swift | 19 ++--- .../Sources/AffiliateProgramSetupScreen.swift | 3 +- .../Sources/PeerInfoScreen.swift | 3 +- .../Sources/PeerInfoGiftsPaneNode.swift | 4 +- .../Sources/ApplicationContext.swift | 16 +++- .../Chat/ChatControllerOpenWebApp.swift | 37 ++++++++- .../TelegramUI/Sources/ChatController.swift | 16 +++- .../Sources/SharedAccountContext.swift | 4 +- .../WebUI/Sources/WebAppController.swift | 45 ++++++++--- versions.json | 2 +- 14 files changed, 188 insertions(+), 55 deletions(-) diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 7b0b0d692f..bdbab5249c 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1296,7 +1296,7 @@ public protocol SharedAccountContext: AnyObject { func makeIncomingMessagePrivacyScreen(context: AccountContext, value: GlobalPrivacySettings.NonContactChatsPrivacy, exceptions: SelectivePrivacySettings, update: @escaping (GlobalPrivacySettings.NonContactChatsPrivacy) -> Void) -> ViewController - func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?) + func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?, verifyAgeCompletion: ((Int) -> Void)?) func makeAffiliateProgramSetupScreenInitialData(context: AccountContext, peerId: EnginePeer.Id, mode: AffiliateProgramSetupScreenMode) -> Signal func makeAffiliateProgramSetupScreen(context: AccountContext, initialData: AffiliateProgramSetupScreenInitialData) -> ViewController diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 858ccf4ab0..5b8e949137 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1250,7 +1250,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController simple: true, source: .generic, skipTermsOfService: true, - payload: nil + payload: nil, + verifyAgeCompletion: nil ) } diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 6e4a1e43e0..cb4f254746 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -3282,7 +3282,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { simple: true, source: .generic, skipTermsOfService: true, - payload: nil + payload: nil, + verifyAgeCompletion: nil ) interaction.dismissSearch() } @@ -4268,7 +4269,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { simple: true, source: .generic, skipTermsOfService: true, - payload: nil + payload: nil, + verifyAgeCompletion: nil ) } } @@ -4287,7 +4289,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { simple: true, source: .generic, skipTermsOfService: true, - payload: nil + payload: nil, + verifyAgeCompletion: nil ) interaction.dismissSearch() } else { diff --git a/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift b/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift index bcf91273b2..03dbf571ac 100644 --- a/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift +++ b/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift @@ -405,6 +405,20 @@ public func presentAgeVerification(context: AccountContext, parentController: Vi 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) + } + return .complete() + }) + } let infoScreen = AgeVerificationScreen(context: context, completion: { [weak parentController] check, availability in if check { var requiredAge = 18 @@ -412,24 +426,60 @@ public func presentAgeVerification(context: AccountContext, parentController: Vi requiredAge = Int(value) } - let scanScreen = FaceScanScreen(context: context, availability: availability, requiredAge: requiredAge, completion: { [weak parentController] passed in - if passed { - let _ = updateAgeVerificationState(engine: context.engine, { _ in - return AgeVerificationState(verificationPassed: passed) - }).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: .window(.root)) - } + 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 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: .window(.root)) + let scanScreen = FaceScanScreen(context: context, availability: availability, completion: { age in + if age >= requiredAge { + success() + } else { + failure() + } + }) + parentController?.push(scanScreen) } }) - parentController?.push(scanScreen) } }) parentController?.push(infoScreen) diff --git a/submodules/TelegramUI/Components/FaceScanScreen/Sources/FaceScanScreen.swift b/submodules/TelegramUI/Components/FaceScanScreen/Sources/FaceScanScreen.swift index 2be0e9db59..66d5ad7bc3 100644 --- a/submodules/TelegramUI/Components/FaceScanScreen/Sources/FaceScanScreen.swift +++ b/submodules/TelegramUI/Components/FaceScanScreen/Sources/FaceScanScreen.swift @@ -25,16 +25,13 @@ final class FaceScanScreenComponent: Component { let context: AccountContext let availability: Signal - let requiredAge: Int init( context: AccountContext, - availability: Signal, - requiredAge: Int + availability: Signal ) { self.context = context self.availability = availability - self.requiredAge = requiredAge } static func ==(lhs: FaceScanScreenComponent, rhs: FaceScanScreenComponent) -> Bool { @@ -307,7 +304,7 @@ final class FaceScanScreenComponent: Component { } private func fillSegment(_ segmentIndex: Int) { - guard let component = self.component, !self.completedAngles.contains(segmentIndex) else { + guard !self.completedAngles.contains(segmentIndex) else { return } self.completedAngles.insert(segmentIndex) @@ -321,7 +318,7 @@ final class FaceScanScreenComponent: Component { if !self.ages.isEmpty { let averageAge = self.ages.reduce(0, +) / Double(self.ages.count) if let environment = self.environment, let controller = environment.controller() as? FaceScanScreen { - controller.completion(averageAge >= Double(component.requiredAge)) + controller.completion(Int(averageAge)) controller.dismiss(animated: true) } } else { @@ -437,7 +434,7 @@ final class FaceScanScreenComponent: Component { let center = CGPoint(x: availableSize.width / 2, y: environment.statusBarHeight + 10.0 + widthRadius * 1.3) - var previewScale = 1.0 + var previewScale = 0.85 if self.processState == .tracking || self.processState == .readyToStart || self.processState == .completed || self.transitioningToViewFinder { let circlePath = CGPath(roundedRect: CGRect(x: center.x - radius, y: center.y - radius, width: radius * 2, height: radius * 2), cornerWidth: radius, cornerHeight: radius, transform: nil) path.addPath(circlePath) @@ -545,21 +542,19 @@ final class FaceScanScreenComponent: Component { public final class FaceScanScreen: ViewControllerComponentContainer { private let context: AccountContext - fileprivate let completion: (Bool) -> Void + fileprivate let completion: (Int) -> Void public init( context: AccountContext, availability: Signal, - requiredAge: Int, - completion: @escaping (Bool) -> Void + completion: @escaping (Int) -> Void ) { self.context = context self.completion = completion super.init(context: context, component: FaceScanScreenComponent( context: context, - availability: availability, - requiredAge: requiredAge + availability: availability ), navigationBarAppearance: .none, theme: .default, updatedPresentationData: nil) self.title = "" diff --git a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift index f784de7564..6acf5283b7 100644 --- a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift @@ -1257,7 +1257,8 @@ final class AffiliateProgramSetupScreenComponent: Component { simple: true, source: .generic, skipTermsOfService: true, - payload: nil + payload: nil, + verifyAgeCompletion: nil ) } else if let navigationController = controller.navigationController as? NavigationController { component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: component.context, chatLocation: .peer(item.peer), subject: nil, keepStack: .always, animated: true, pushController: { [weak navigationController] chatController, animated, completion in diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 8987ea9da8..557f3ff470 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -1451,7 +1451,8 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese simple: true, source: .generic, skipTermsOfService: true, - payload: nil + payload: nil, + verifyAgeCompletion: nil ) }) } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index a736019aad..3011f23546 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -201,7 +201,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr return } - let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Title, titleFont: .bold, subtitle: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Text, value: "", placeholder: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Placeholder, characterLimit: 20, displayCharacterLimit: true, apply: { [weak self] value in + let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Title, titleFont: .bold, subtitle: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Text, value: "", placeholder: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Placeholder, characterLimit: 12, displayCharacterLimit: true, apply: { [weak self] value in guard let self, let value else { return } @@ -264,7 +264,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr return } - let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: params.presentationData.strings.PeerInfo_Gifts_RenameCollection_Title, titleFont: .bold, value: collection.title, placeholder: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Placeholder, characterLimit: 20, displayCharacterLimit: true, apply: { [weak self] value in + let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: params.presentationData.strings.PeerInfo_Gifts_RenameCollection_Title, titleFont: .bold, value: collection.title, placeholder: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Placeholder, characterLimit: 12, displayCharacterLimit: true, apply: { [weak self] value in guard let self, let value else { return } diff --git a/submodules/TelegramUI/Sources/ApplicationContext.swift b/submodules/TelegramUI/Sources/ApplicationContext.swift index 51b4902c1e..52bfd8774d 100644 --- a/submodules/TelegramUI/Sources/ApplicationContext.swift +++ b/submodules/TelegramUI/Sources/ApplicationContext.swift @@ -900,7 +900,21 @@ final class AuthorizedApplicationContext { } if openAppIfAny, case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp), let parentController = self.rootController.viewControllers.last as? ViewController { - self.context.sharedContext.openWebApp(context: self.context, parentController: parentController, updatedPresentationData: nil, botPeer: peer, chatPeer: nil, threadId: nil, buttonText: "", url: "", simple: true, source: .generic, skipTermsOfService: true, payload: nil) + self.context.sharedContext.openWebApp( + context: self.context, + parentController: parentController, + updatedPresentationData: nil, + botPeer: peer, + chatPeer: nil, + threadId: nil, + buttonText: "", + url: "", + simple: true, + source: .generic, + skipTermsOfService: true, + payload: nil, + verifyAgeCompletion: nil + ) } else { self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: self.rootController, context: self.context, chatLocation: chatLocation, subject: alwaysKeepMessageId || isOutgoingMessage ? messageId.flatMap { .message(id: .id($0), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false) } : nil, activateInput: activateInput ? .text : nil)) } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift index 312ce0b606..083f47e8d5 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift @@ -27,7 +27,8 @@ func openWebAppImpl( simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, - payload: String? + payload: String?, + verifyAgeCompletion: ((Int) -> Void)? ) { if context.isFrozen { parentController.push(context.sharedContext.makeAccountFreezeInfoScreen(context: context)) @@ -246,7 +247,7 @@ func openWebAppImpl( navigationController = parentController.effectiveNavigationController } return navigationController ?? (context.sharedContext.mainWindow?.viewController as? NavigationController) - }) + }, verifyAgeCompletion: verifyAgeCompletion) controller.navigationPresentation = .flatModal parentController.push(controller) @@ -351,7 +352,21 @@ public extension ChatControllerImpl { } self.chatDisplayNode.dismissInput() - self.context.sharedContext.openWebApp(context: self.context, parentController: self, updatedPresentationData: self.updatedPresentationData, botPeer: EnginePeer(peer), chatPeer: EnginePeer(peer), threadId: self.chatLocation.threadId, buttonText: buttonText, url: url, simple: simple, source: source, skipTermsOfService: false, payload: nil) + self.context.sharedContext.openWebApp( + context: self.context, + parentController: self, + updatedPresentationData: self.updatedPresentationData, + botPeer: EnginePeer(peer), + chatPeer: EnginePeer(peer), + threadId: self.chatLocation.threadId, + buttonText: buttonText, + url: url, + simple: simple, + source: source, + skipTermsOfService: false, + payload: nil, + verifyAgeCompletion: nil + ) } fileprivate static func botRequestSwitchInline(context: AccountContext, controller: ChatControllerImpl?, peerId: EnginePeer.Id, botAddress: String, query: String, chatTypes: [ReplyMarkupButtonRequestPeerType]?, completion: @escaping () -> Void) -> Void { @@ -616,7 +631,21 @@ public extension ChatControllerImpl { } }) } else { - context.sharedContext.openWebApp(context: context, parentController: parentController, updatedPresentationData: updatedPresentationData, botPeer: botPeer, chatPeer: nil, threadId: nil, buttonText: "", url: "", simple: true, source: .generic, skipTermsOfService: false, payload: payload) + context.sharedContext.openWebApp( + context: context, + parentController: parentController, + updatedPresentationData: updatedPresentationData, + botPeer: botPeer, + chatPeer: nil, + threadId: nil, + buttonText: "", + url: "", + simple: true, + source: .generic, + skipTermsOfService: false, + payload: payload, + verifyAgeCompletion: nil + ) } } } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 82a1c85707..d1d891b2c3 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -8907,7 +8907,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G commit() }) } else { - self.context.sharedContext.openWebApp(context: self.context, parentController: self, updatedPresentationData: self.updatedPresentationData, botPeer: peer, chatPeer: nil, threadId: nil, buttonText: "", url: "", simple: true, source: .generic, skipTermsOfService: false, payload: botAppStart.payload) + self.context.sharedContext.openWebApp( + context: self.context, + parentController: self, + updatedPresentationData: self.updatedPresentationData, + botPeer: peer, + chatPeer: nil, + threadId: nil, + buttonText: "", + url: "", + simple: true, + source: .generic, + skipTermsOfService: false, + payload: botAppStart.payload, + verifyAgeCompletion: nil + ) commit() } }) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 48ad0ae251..dcb2d5e440 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -3882,8 +3882,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { return incomingMessagePrivacyScreen(context: context, value: value, exceptions: exceptions, update: update) } - public func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?) { - openWebAppImpl(context: context, parentController: parentController, updatedPresentationData: updatedPresentationData, botPeer: botPeer, chatPeer: chatPeer, threadId: threadId, buttonText: buttonText, url: url, simple: simple, source: source, skipTermsOfService: skipTermsOfService, payload: payload) + public func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?, verifyAgeCompletion: ((Int) -> Void)?) { + openWebAppImpl(context: context, parentController: parentController, updatedPresentationData: updatedPresentationData, botPeer: botPeer, chatPeer: chatPeer, threadId: threadId, buttonText: buttonText, url: url, simple: simple, source: source, skipTermsOfService: skipTermsOfService, payload: payload, verifyAgeCompletion: verifyAgeCompletion) } public func makeAffiliateProgramSetupScreenInitialData(context: AccountContext, peerId: EnginePeer.Id, mode: AffiliateProgramSetupScreenMode) -> Signal { diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 3445a10613..2d92f8bbe6 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -653,10 +653,14 @@ public final class WebAppController: ViewController, AttachmentContainable { self.animateTransitionIn() }) } - + @available(iOSApplicationExtension 15.0, iOS 15.0, *) func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) { - decisionHandler(.prompt) + if self.controller?.isVerifyAgeBot == true && type == .camera { + decisionHandler(.grant) + } else { + decisionHandler(.prompt) + } } func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { @@ -1756,6 +1760,12 @@ public final class WebAppController: ViewController, AttachmentContainable { } case "web_app_hide_keyboard": self.view.window?.endEditing(true) + case "web_app_verify_age": + if let json, self.controller?.isVerifyAgeBot == true { + if let ageValue = json["age"] as? Double { + self.controller?.verifyAgeCompletion?(Int(ageValue)) + } + } default: break } @@ -3281,6 +3291,8 @@ public final class WebAppController: ViewController, AttachmentContainable { public var completion: () -> Void = {} public var requestSwitchInline: (String, [ReplyMarkupButtonRequestPeerType]?, @escaping () -> Void) -> Void = { _, _, _ in } + public var verifyAgeCompletion: ((Int) -> Void)? + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, params: WebAppParameters, replyToMessageId: MessageId?, threadId: Int64?) { self.context = context self.source = params.source @@ -3323,16 +3335,20 @@ public final class WebAppController: ViewController, AttachmentContainable { self.navigationItem.leftBarButtonItem?.action = #selector(self.cancelPressed) self.navigationItem.leftBarButtonItem?.target = self - self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: self.moreButtonNode) - self.navigationItem.rightBarButtonItem?.action = #selector(self.moreButtonPressed) - self.navigationItem.rightBarButtonItem?.target = self + if !self.isVerifyAgeBot { + self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: self.moreButtonNode) + self.navigationItem.rightBarButtonItem?.action = #selector(self.moreButtonPressed) + self.navigationItem.rightBarButtonItem?.target = self + } self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) - let titleView = WebAppTitleView(context: self.context, theme: self.presentationData.theme) - titleView.title = WebAppTitle(title: params.botName, counter: self.presentationData.strings.WebApp_Miniapp, isVerified: params.botVerified) - self.navigationItem.titleView = titleView - self.titleView = titleView + if !self.isVerifyAgeBot { + let titleView = WebAppTitleView(context: self.context, theme: self.presentationData.theme) + titleView.title = WebAppTitle(title: params.botName, counter: self.presentationData.strings.WebApp_Miniapp, isVerified: params.botVerified) + self.navigationItem.titleView = titleView + self.titleView = titleView + } self.moreButtonNode.action = { [weak self] _, gesture in if let strongSelf = self { @@ -3387,6 +3403,13 @@ public final class WebAppController: ViewController, AttachmentContainable { self.presentationDataDisposable?.dispose() } + private var isVerifyAgeBot: Bool { + if let ageBotUsername = self.context.currentAppConfiguration.with({ $0 }).data?["verify_age_bot_username"] as? String { + return self.botAddress == ageBotUsername + } + return false + } + public func beforeMaximize(navigationController: NavigationController, completion: @escaping () -> Void) { switch self.source { case .generic, .settings: @@ -3857,7 +3880,8 @@ public func standaloneWebAppController( willDismiss: @escaping () -> Void = {}, didDismiss: @escaping () -> Void = {}, getNavigationController: @escaping () -> NavigationController? = { return nil }, - getSourceRect: (() -> CGRect?)? = nil + getSourceRect: (() -> CGRect?)? = nil, + verifyAgeCompletion: ((Int) -> Void)? = nil ) -> ViewController { let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: params.peerId), buttons: [.standalone], initialButton: .standalone, fromMenu: params.source == .menu, hasTextInput: false, isFullSize: params.fullSize, makeEntityInputView: { return nil @@ -3868,6 +3892,7 @@ public func standaloneWebAppController( webAppController.completion = completion webAppController.getNavigationController = getNavigationController webAppController.requestSwitchInline = requestSwitchInline + webAppController.verifyAgeCompletion = verifyAgeCompletion present(webAppController, webAppController.mediaPickerContext) } controller.willDismiss = willDismiss diff --git a/versions.json b/versions.json index 059b1bfd9a..03d9138921 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "11.14", + "app": "11.13.3", "xcode": "16.2", "bazel": "8.2.1:22ff65b05869f6160e5157b1b425a14a62085d71d8baef571f462b8fe5a703a3", "macos": "15" From c9e8fe831a456fb324ec901b7103af3cb19e5964 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 21 Jul 2025 01:08:46 +0200 Subject: [PATCH 04/18] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 3 ++ .../Sources/AddGiftsScreen.swift | 1 + .../Sources/PeerInfoGiftsPaneNode.swift | 43 ++++++++++++------- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index c369488679..389d6884de 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -14700,6 +14700,9 @@ Sorry for the inconvenience."; "AccessDenied.AgeVerificationCamera" = "Telegram needs access to your camera for age verification.\n\nOpen your device's Settings > Privacy > Camera and set Telegram to ON."; +"PeerInfo.Gifts.Collections.All" = "All Gifts"; +"PeerInfo.Gifts.Collections.Add" = "Add Collection"; + "PeerInfo.Gifts.AddGiftsButton" = "Add Gifts"; "PeerInfo.Gifts.AddCollection" = "Add Collection"; diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift index 23cdd4f560..a1793154c8 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift @@ -114,6 +114,7 @@ final class AddGiftsScreenComponent: Component { var contentSize = CGSize(width: self.scrollView.bounds.width, height: contentHeight) contentSize.height += environment.safeInsets.bottom contentSize.height = max(contentSize.height, self.scrollView.bounds.size.height) + contentSize.height += 50.0 + 24.0 transition.setFrame(view: giftsListView, frame: CGRect(origin: CGPoint(), size: contentSize)) if self.scrollView.contentSize != contentSize { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index 3011f23546..8d334f5a33 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -523,13 +523,19 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr var topInset: CGFloat = 60.0 - if let collections = self.collections, !collections.isEmpty { + var canEditCollections = false + if self.peerId == self.context.account.peerId || self.canManage { + canEditCollections = true + } + + let hasNonEmptyCollections = self.collections?.contains(where: { $0.count > 0 }) ?? false + if let collections = self.collections, !collections.isEmpty && (hasNonEmptyCollections || canEditCollections) { var tabSelectorItems: [TabSelectorComponent.Item] = [] tabSelectorItems.append(TabSelectorComponent.Item( id: AnyHashable(GiftCollection.all.rawValue), - title: "All Gifts" + title: params.presentationData.strings.PeerInfo_Gifts_Collections_All )) - + var effectiveCollections: [StarGiftCollection] = collections if let reorderedCollectionIds = self.reorderedCollectionIds { var collectionMap: [Int32: StarGiftCollection] = [:] @@ -546,6 +552,9 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr } for collection in effectiveCollections { + if !canEditCollections && collection.count == 0 { + continue + } tabSelectorItems.append(TabSelectorComponent.Item( id: AnyHashable(GiftCollection.collection(collection.id).rawValue), content: .component(AnyComponent( @@ -565,19 +574,21 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr } )) } - - tabSelectorItems.append(TabSelectorComponent.Item( - id: AnyHashable(GiftCollection.create.rawValue), - content: .component(AnyComponent( - CollectionTabItemComponent( - context: self.context, - icon: .add, - title: "Add Collection", - theme: params.presentationData.theme - ) - )), - isReorderable: false - )) + + if canEditCollections { + tabSelectorItems.append(TabSelectorComponent.Item( + id: AnyHashable(GiftCollection.create.rawValue), + content: .component(AnyComponent( + CollectionTabItemComponent( + context: self.context, + icon: .add, + title: params.presentationData.strings.PeerInfo_Gifts_Collections_Add, + theme: params.presentationData.theme + ) + )), + isReorderable: false + )) + } let tabSelectorSize = self.tabSelector.update( transition: transition, From 28b03349526955571060be8be8bf2c2e8debba50 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 21 Jul 2025 13:39:37 +0200 Subject: [PATCH 05/18] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 6 ++ .../Sources/PremiumIntroScreen.swift | 8 +- .../Sources/PeerInfoScreen.swift | 15 ++- .../Sources/GiftsListView.swift | 7 ++ .../Sources/PeerInfoGiftsPaneNode.swift | 101 +++++++++++++----- .../Sources/TabSelectorComponent.swift | 8 ++ .../ChatControllerOpenTodoContextMenu.swift | 3 +- 7 files changed, 107 insertions(+), 41 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 389d6884de..ff8bcd7404 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -14731,6 +14731,12 @@ Sorry for the inconvenience."; "PeerInfo.Gifts.EmptyCollection.Text" = "Add some gifts to this collection."; "PeerInfo.Gifts.EmptyCollection.Action" = "Add to Collection"; +"PeerInfo.Gifts.AddedToCollection" = "The gift has been added to **%@**."; +"PeerInfo.Gifts.RemovedFromCollection" = "The gift has been removed from **%@**."; + +"PeerInfo.Gifts.AddedToCollectionUnique" = "**%1$@** has been added to **%2$@**."; +"PeerInfo.Gifts.RemovedFromCollectionUnique" = "**%1$@** has been removed from **%2$@**."; + "AddGifts.Title" = "Add Gifts"; "AddGifts.AddGifts_1" = "Add %@ Gift"; "AddGifts.AddGifts_any" = "Add %@ Gifts"; diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index 76fba73534..e0961e864b 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -3096,13 +3096,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { products.append(PremiumProduct(option: option, storeProduct: product)) } } - - //TODO:release - if let product = availableProducts.first(where: { $0.id.hasSuffix(".annual") }) { - let (currency, price) = product.priceCurrencyAndAmount - products.insert(PremiumProduct(option: PremiumPromoConfiguration.PremiumProductOption(isCurrent: false, months: 24, currency: currency, amount: price * 2, botUrl: "", transactionId: nil, availableForUpgrade: true, storeProductId: "org.telegram.telegramPremium.biannual"), storeProduct: product), at: 0) - } - + strongSelf.products = products strongSelf.isPremium = forceHasPremium || isPremium strongSelf.otherPeerName = otherPeerName diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 557f3ff470..43cd0843f6 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -11364,7 +11364,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let canReorderEquals = lhs.2 == rhs.2 return filterEquals && sortingEquals && canReorderEquals }) - |> map { [weak self, weak pane, weak giftsContext] filter, sorting, canReorder -> ContextController.Items in + |> map { [weak pane, weak giftsContext] filter, sorting, canReorder -> ContextController.Items in var items: [ContextMenuItem] = [] if hasVisibility { @@ -11389,13 +11389,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } }))) - items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_ShareCollection, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.default) - //TODO:release - self?.openShareLink(url: "https://t.me/") - }))) +// items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_ShareCollection, icon: { theme in +// return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) +// }, action: { [weak self] _, f in +// f(.default) +// self?.openShareLink(url: "https://t.me/") +// }))) } if canReorder { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift index b42e5eae26..2a52b933bc 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift @@ -408,6 +408,13 @@ final class GiftsListView: UIView { } return self.updateScrolling(interactive: interactive, topInset: topInset, visibleBounds: visibleBounds, transition: transition) } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + if let topInset = self.topInset, point.y < topInset { + return false + } + return super.point(inside: point, with: event) + } func updateScrolling(interactive: Bool = false, topInset: CGFloat, visibleBounds: CGRect, transition: ComponentTransition) -> CGFloat { self.topInset = topInset diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index 8d334f5a33..0c732eb347 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -184,7 +184,11 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr self.scrollNode.view.contentInsetAdjustmentBehavior = .never self.scrollNode.view.delegate = self - self.scrollNode.view.insertSubview(self.giftsListView, at: 0) + if let tabSelectorView = self.tabSelector.view { + self.scrollNode.view.insertSubview(self.giftsListView, aboveSubview: tabSelectorView) + } else { + self.scrollNode.view.insertSubview(self.giftsListView, at: 0) + } } private func item(at point: CGPoint) -> (AnyHashable, ComponentView)? { @@ -211,6 +215,10 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr } if let collection { self.setCurrentCollection(collection: .collection(collection.id)) + + if let tabSelectorView = self.tabSelector.view as? TabSelectorComponent.View { + tabSelectorView.scrollToEnd() + } } }) }) @@ -230,6 +238,10 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr self?.setCurrentCollection(collection: .all) let _ = self?.profileGiftsCollections.deleteCollection(id: id).start() + + if let tabSelectorView = self?.tabSelector.view as? TabSelectorComponent.View { + tabSelectorView.scrollToStart() + } }) ]), ActionSheetItemGroup(items: [ @@ -468,20 +480,21 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr } f(.default) - self.renameCollection(id: id) + Queue.mainQueue().after(0.15) { + self.renameCollection(id: id) + } }))) - items.append(.action(ContextMenuActionItem(text: params.presentationData.strings.PeerInfo_Gifts_ShareCollection, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.actionSheet.primaryTextColor) - }, action: { [weak self] _, f in - guard let self else { - return - } - f(.default) - - //TODO:release - let _ = self - }))) +// items.append(.action(ContextMenuActionItem(text: params.presentationData.strings.PeerInfo_Gifts_ShareCollection, icon: { theme in +// return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.actionSheet.primaryTextColor) +// }, action: { [weak self] _, f in +// guard let self else { +// return +// } +// f(.default) +// +// let _ = self +// }))) items.append(.action(ContextMenuActionItem(text: params.presentationData.strings.PeerInfo_Gifts_Reorder, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.actionSheet.primaryTextColor) @@ -502,7 +515,9 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr } f(.default) - self.deleteCollection(id: id) + Queue.mainQueue().after(0.15) { + self.deleteCollection(id: id) + } }))) let contextController = ContextController( @@ -640,7 +655,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr if let tabSelectorView = self.tabSelector.view { if tabSelectorView.superview == nil { tabSelectorView.alpha = 1.0 - self.scrollNode.view.addSubview(tabSelectorView) + self.scrollNode.view.insertSubview(tabSelectorView, at: 0) if !transition.animation.isImmediate { tabSelectorView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) @@ -957,10 +972,52 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr }, iconPosition: collection.icon == nil ? .left : .right, action: { [weak self] _, f in f(.default) + guard let self else { + return + } + if isAdded, let giftReference = gift.reference { - let _ = self?.profileGiftsCollections.removeGifts(id: collection.id, gifts: [giftReference]).start() + let _ = self.profileGiftsCollections.removeGifts(id: collection.id, gifts: [giftReference]).start() } else { - let _ = self?.profileGiftsCollections.addGifts(id: collection.id, gifts: [gift]).start() + let _ = self.profileGiftsCollections.addGifts(id: collection.id, gifts: [gift]).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 text: String + if let giftTitle { + if isAdded { + text = currentParams.presentationData.strings.PeerInfo_Gifts_RemovedFromCollectionUnique(giftTitle, collection.title).string + } else { + text = currentParams.presentationData.strings.PeerInfo_Gifts_AddedToCollectionUnique(giftTitle, collection.title).string + } + } else { + if isAdded { + text = currentParams.presentationData.strings.PeerInfo_Gifts_RemovedFromCollection(collection.title).string + } else { + text = currentParams.presentationData.strings.PeerInfo_Gifts_AddedToCollection(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), + action: { _ in return true } + ) + self.parentController?.present(undoController, in: .current) } }))) } @@ -1370,14 +1427,12 @@ private final class CollectionTabItemComponent: Component { let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: component.title, font: Font.semibold(14.0), textColor: .white)) + text: .plain(NSAttributedString(string: component.title, font: Font.semibold(14.0), textColor: component.theme.list.itemSecondaryTextColor)) )), environment: {}, containerSize: CGSize(width: availableSize.width, height: 100.0) ) - - let tintColor = component.theme.list.itemSecondaryTextColor - + var iconOffset: CGFloat = 0.0 var iconSize = CGSize() if let icon = component.icon { @@ -1411,7 +1466,7 @@ private final class CollectionTabItemComponent: Component { transition: .immediate, component: AnyComponent(BundleIconComponent( name: "Chat/Input/Media/PanelBadgeAdd", - tintColor: tintColor + tintColor: component.theme.list.itemSecondaryTextColor )), environment: {}, containerSize: CGSize(width: 100.0, height: 100.0) @@ -1447,8 +1502,6 @@ private final class CollectionTabItemComponent: Component { self.addSubview(titleView) } titleView.frame = titleFrame - - transition.setTintColor(layer: titleView.layer, color: tintColor) } let size: CGSize diff --git a/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift b/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift index 53502dd20c..ef0827e734 100644 --- a/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift +++ b/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift @@ -483,6 +483,14 @@ public final class TabSelectorComponent: Component { } } + public func scrollToStart() { + self.setContentOffset(.zero, animated: true) + } + + public func scrollToEnd() { + self.setContentOffset(CGPoint(x: self.contentSize.width - self.bounds.width, y: 0.0), animated: true) + } + func update(component: TabSelectorComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { let selectionColorUpdated = component.colors.selection != self.component?.colors.selection diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenTodoContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenTodoContextMenu.swift index 36860380ad..e2dcfb2857 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenTodoContextMenu.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenTodoContextMenu.swift @@ -128,8 +128,7 @@ extension ChatControllerImpl { } } - if "".isEmpty { - //TODO:release + if canReplyInChat(self.presentationInterfaceState, accountPeerId: self.context.account.peerId) { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Todo_ReplyToItem, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reply"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] c, _ in From 6ced6dacd687e2950026dcfc7b3dba790f876e38 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 21 Jul 2025 16:04:37 +0200 Subject: [PATCH 06/18] Various fixes --- .../Sources/AddGiftsScreen.swift | 6 ++++++ .../Sources/PeerInfoGiftsPaneNode.swift | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift index a1793154c8..63383fd9ea 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift @@ -133,6 +133,12 @@ final class AddGiftsScreenComponent: Component { giftsListView = current } else { giftsListView = GiftsListView(context: component.context, peerId: component.peerId, profileGifts: component.profileGifts, giftsCollections: nil, canSelect: true, ignoreCollection: component.collectionId, remainingSelectionCount: component.remainingCount) + giftsListView.onContentUpdated = { [weak self] in + guard let self else { + return + } + self.state?.updated(transition: .immediate) + } giftsListView.selectionUpdated = { [weak self] in guard let self else { return diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index 0c732eb347..219bcc9a2d 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -151,6 +151,15 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr super.init() + self.giftsListView.onContentUpdated = { [weak self] in + guard let self else { + return + } + if let params = self.currentParams { + self.update(size: params.size, topInset: params.topInset, sideInset: params.sideInset, bottomInset: params.bottomInset, deviceMetrics: params.deviceMetrics, visibleHeight: params.visibleHeight, isScrollingLockedAtTop: params.isScrollingLockedAtTop, expandProgress: params.expandProgress, navigationHeight: params.navigationHeight, presentationData: params.presentationData, synchronous: true, transition: .immediate) + } + } + self.addSubnode(self.backgroundNode) self.addSubnode(self.scrollNode) From 9dcbc8192342a3df8339c0199137324a012e059c Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 21 Jul 2025 16:46:14 +0200 Subject: [PATCH 07/18] Various fixes --- .../LegacyComponents/Sources/TGMediaAssetsController.m | 5 ++++- submodules/MediaPickerUI/Sources/MediaPickerScreen.swift | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m index 8c9f3a2d7b..dcdf83da92 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m @@ -871,7 +871,7 @@ if (_intent == TGMediaAssetsControllerSendMediaIntent && _selectionContext.allowGrouping) [[NSUserDefaults standardUserDefaults] setObject:@(!_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"]; - + return [TGMediaAssetsController resultSignalsForSelectionContext:_selectionContext editingContext:_editingContext intent:_intent currentItem:currentItem storeAssets:storeAssets convertToJpeg:false descriptionGenerator:descriptionGenerator saveEditedPhotos:_saveEditedPhotos]; } @@ -889,6 +889,9 @@ if (selectedItems.count == 0 && currentItem != nil) [selectedItems addObject:currentItem]; + if (intent == TGMediaAssetsControllerSendMediaIntent) + [[NSUserDefaults standardUserDefaults] setObject:@(editingContext.isHighQualityPhoto) forKey:@"TG_photoHighQuality_v0"]; + if (saveEditedPhotos && storeAssets && editingContext != nil) { NSMutableArray *fullSizeSignals = [[NSMutableArray alloc] init]; diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 3eeeb0943b..70114ae942 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -2159,6 +2159,12 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att strongSelf.controllerNode.dismissInput() } }, selectionState: selectionContext, editingState: editingContext ?? TGMediaEditingContext()) + + let highQualityPhoto = UserDefaults.standard.bool(forKey: "TG_photoHighQuality_v0") + if highQualityPhoto { + self.interaction?.editingState.setHighQualityPhoto(highQualityPhoto) + } + self.interaction?.selectionState?.grouping = true self.interaction?.editingState.sendPaidMessageStars = sendPaidMessageStars ?? 0 From 89c03bc96a36bde79f4b21cdca3ba16ee3b5ba89 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 21 Jul 2025 17:00:15 +0200 Subject: [PATCH 08/18] Hide gifts sortings in collections --- .../Sources/PeerInfoScreen.swift | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 43cd0843f6..a15d813cbf 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -11426,15 +11426,17 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro items.append(.separator) } - items.append(.action(ContextMenuActionItem(text: sorting == .date ? strings.PeerInfo_Gifts_SortByValue : strings.PeerInfo_Gifts_SortByDate, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: sorting == .date ? "Peer Info/SortValue" : "Peer Info/SortDate"), color: theme.contextMenu.primaryColor) - }, action: { [weak giftsContext] _, f in - f(.default) - - giftsContext?.updateSorting(sorting == .date ? .value : .date) - }))) + if case .all = pane.currentCollection { + items.append(.action(ContextMenuActionItem(text: sorting == .date ? strings.PeerInfo_Gifts_SortByValue : strings.PeerInfo_Gifts_SortByDate, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: sorting == .date ? "Peer Info/SortValue" : "Peer Info/SortDate"), color: theme.contextMenu.primaryColor) + }, action: { [weak giftsContext] _, f in + f(.default) + + giftsContext?.updateSorting(sorting == .date ? .value : .date) + }))) - items.append(.separator) + items.append(.separator) + } let toggleFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in var updatedFilter = filter From fd714756d2dca86a9c0f387f2d650a08b9ba7195 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 21 Jul 2025 17:42:40 +0200 Subject: [PATCH 09/18] Fix build --- .../PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index a15d813cbf..a1223f9d14 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -11426,7 +11426,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro items.append(.separator) } - if case .all = pane.currentCollection { + if let pane, case .all = pane.currentCollection { items.append(.action(ContextMenuActionItem(text: sorting == .date ? strings.PeerInfo_Gifts_SortByValue : strings.PeerInfo_Gifts_SortByDate, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: sorting == .date ? "Peer Info/SortValue" : "Peer Info/SortDate"), color: theme.contextMenu.primaryColor) }, action: { [weak giftsContext] _, f in From 9fbd857506ca11f712c11d0154744132635a96e6 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 22 Jul 2025 19:20:26 +0200 Subject: [PATCH 10/18] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 3 + .../DataAndStorageSettingsController.swift | 6 - .../TelegramEngine/Payments/StarGifts.swift | 22 ++ .../Payments/StarGiftsCollections.swift | 13 +- .../Sources/AgeVerificationScreen.swift | 197 +++++++----------- .../PeerInfoScreen/Sources/PeerInfoData.swift | 4 +- .../Sources/PeerInfoGiftsPaneNode.swift | 44 +++- 7 files changed, 157 insertions(+), 132 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/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/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) } }))) } From da930a44adf8a94014523610dc4a728f1f4dd6ba Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 23 Jul 2025 21:41:19 +0200 Subject: [PATCH 11/18] Fix crash --- .../StatisticsUI/Sources/RevenueWithdrawalController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From b9a2af1e971b2f2107374ddb5f9b68133789012a Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 23 Jul 2025 21:41:59 +0200 Subject: [PATCH 12/18] Bump version --- versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 55e96a5a302b53736f82e162e7e267d3350ae81c Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 23 Jul 2025 22:37:01 +0200 Subject: [PATCH 13/18] Fix add gifts load more --- .../Sources/AddGiftsScreen.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift index 63383fd9ea..78bd998430 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.scrollNode.view.contentSize.height - self.scrollNode.view.contentOffset.y - self.scrollNode.view.frame.height) + if interactive, bottomContentOffset < 200.0 { + self.giftsListView.loadMore() + } } func update(component: AddGiftsScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { From 8777f5da8fb27b4293d31fc230458a1a46017897 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 23 Jul 2025 22:57:23 +0200 Subject: [PATCH 14/18] Fix build --- .../PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift index 78bd998430..1337347ae8 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift @@ -121,9 +121,9 @@ final class AddGiftsScreenComponent: Component { self.scrollView.contentSize = contentSize } - let bottomContentOffset = max(0.0, self.scrollNode.view.contentSize.height - self.scrollNode.view.contentOffset.y - self.scrollNode.view.frame.height) + 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() + self.giftsListView?.loadMore() } } From 3178a7472a5bebce0cb9ea2376a752572383dc18 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 24 Jul 2025 04:41:10 +0200 Subject: [PATCH 15/18] 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 24aea171329eb6752cc938720e3ee2ee6483b795 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 24 Jul 2025 05:07:17 +0200 Subject: [PATCH 16/18] 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 6617a0c45fc29e8fe7f0fb2d84141d36871ee91a Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 24 Jul 2025 13:15:15 +0200 Subject: [PATCH 17/18] Update API --- submodules/TelegramApi/Sources/Api0.swift | 15 +- submodules/TelegramApi/Sources/Api10.swift | 26 ++-- submodules/TelegramApi/Sources/Api15.swift | 20 +-- submodules/TelegramApi/Sources/Api24.swift | 24 +-- submodules/TelegramApi/Sources/Api26.swift | 80 +++++++++- submodules/TelegramApi/Sources/Api37.swift | 110 +++++++------- submodules/TelegramApi/Sources/Api38.swift | 52 +++++++ submodules/TelegramApi/Sources/Api39.swift | 143 +++++++++++++++++- .../ApiUtils/TelegramMediaAction.swift | 4 +- .../Sources/State/AppUpdate.swift | 7 + .../Sources/State/ManagedRecentStickers.swift | 3 +- .../Sources/State/Serialization.swift | 2 +- .../SyncCore_TelegramMediaAction.swift | 18 ++- .../TelegramEngine/Messages/Stories.swift | 11 +- .../Payments/BotPaymentForm.swift | 10 +- .../TelegramEngine/Payments/StarGifts.swift | 93 +++++++----- .../Payments/TelegramEnginePayments.swift | 4 +- .../Sources/ServiceMessageStrings.swift | 18 ++- .../Sources/GiftStoreScreen.swift | 3 +- .../Sources/GiftViewScreen.swift | 89 ++++++----- .../Sources/MediaEditorScreen.swift | 2 +- .../Sources/GiftsListView.swift | 9 +- .../Sources/UserApperanceScreen.swift | 3 +- .../Sources/StarsWithdrawalScreen.swift | 4 +- 24 files changed, 544 insertions(+), 206 deletions(-) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 0bf257604c..22d1e280d3 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -388,7 +388,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-625298705] = { return Api.InputInvoice.parse_inputInvoicePremiumGiftStars($0) } dict[-1020867857] = { return Api.InputInvoice.parse_inputInvoiceSlug($0) } dict[-396206446] = { return Api.InputInvoice.parse_inputInvoiceStarGift($0) } - dict[1674298252] = { return Api.InputInvoice.parse_inputInvoiceStarGiftResale($0) } + dict[-1012968668] = { return Api.InputInvoice.parse_inputInvoiceStarGiftResale($0) } dict[1247763417] = { return Api.InputInvoice.parse_inputInvoiceStarGiftTransfer($0) } dict[1300335965] = { return Api.InputInvoice.parse_inputInvoiceStarGiftUpgrade($0) } dict[1710230755] = { return Api.InputInvoice.parse_inputInvoiceStars($0) } @@ -603,7 +603,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1348510708] = { return Api.MessageAction.parse_messageActionSetChatWallPaper($0) } dict[1007897979] = { return Api.MessageAction.parse_messageActionSetMessagesTTL($0) } dict[1192749220] = { return Api.MessageAction.parse_messageActionStarGift($0) } - dict[775611918] = { return Api.MessageAction.parse_messageActionStarGiftUnique($0) } + dict[888627955] = { return Api.MessageAction.parse_messageActionStarGiftUnique($0) } dict[1474192222] = { return Api.MessageAction.parse_messageActionSuggestProfilePhoto($0) } dict[-293988970] = { return Api.MessageAction.parse_messageActionSuggestedPostApproval($0) } dict[1777932024] = { return Api.MessageAction.parse_messageActionSuggestedPostRefund($0) } @@ -944,7 +944,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) } dict[-963180333] = { return Api.SponsoredPeer.parse_sponsoredPeer($0) } dict[12386139] = { return Api.StarGift.parse_starGift($0) } - dict[-164136786] = { return Api.StarGift.parse_starGiftUnique($0) } + dict[975654224] = { return Api.StarGift.parse_starGiftUnique($0) } dict[-650279524] = { return Api.StarGiftAttribute.parse_starGiftAttributeBackdrop($0) } dict[970559507] = { return Api.StarGiftAttribute.parse_starGiftAttributeModel($0) } dict[-524291476] = { return Api.StarGiftAttribute.parse_starGiftAttributeOriginalDetails($0) } @@ -992,8 +992,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[872932635] = { return Api.StickerSetCovered.parse_stickerSetMultiCovered($0) } dict[2008112412] = { return Api.StickerSetCovered.parse_stickerSetNoCovered($0) } dict[1898850301] = { return Api.StoriesStealthMode.parse_storiesStealthMode($0) } + dict[-1826262950] = { return Api.StoryAlbum.parse_storyAlbum($0) } dict[-1205411504] = { return Api.StoryFwdHeader.parse_storyFwdHeader($0) } - dict[2041735716] = { return Api.StoryItem.parse_storyItem($0) } + dict[-302947087] = { return Api.StoryItem.parse_storyItem($0) } dict[1374088783] = { return Api.StoryItem.parse_storyItemDeleted($0) } dict[-5388013] = { return Api.StoryItem.parse_storyItemSkipped($0) } dict[1620104917] = { return Api.StoryReaction.parse_storyReaction($0) } @@ -1476,6 +1477,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[172975040] = { return Api.storage.FileType.parse_filePng($0) } dict[-1432995067] = { return Api.storage.FileType.parse_fileUnknown($0) } dict[276907596] = { return Api.storage.FileType.parse_fileWebp($0) } + dict[-1013417414] = { return Api.stories.Albums.parse_albums($0) } + dict[1448008427] = { return Api.stories.Albums.parse_albumsNotModified($0) } dict[1862033025] = { return Api.stories.AllStories.parse_allStories($0) } dict[291044926] = { return Api.stories.AllStories.parse_allStoriesNotModified($0) } dict[-1014513586] = { return Api.stories.CanSendStoryCount.parse_canSendStoryCount($0) } @@ -2196,6 +2199,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.StoriesStealthMode: _1.serialize(buffer, boxed) + case let _1 as Api.StoryAlbum: + _1.serialize(buffer, boxed) case let _1 as Api.StoryFwdHeader: _1.serialize(buffer, boxed) case let _1 as Api.StoryItem: @@ -2614,6 +2619,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.storage.FileType: _1.serialize(buffer, boxed) + case let _1 as Api.stories.Albums: + _1.serialize(buffer, boxed) case let _1 as Api.stories.AllStories: _1.serialize(buffer, boxed) case let _1 as Api.stories.CanSendStoryCount: diff --git a/submodules/TelegramApi/Sources/Api10.swift b/submodules/TelegramApi/Sources/Api10.swift index 95b7d1d12a..1de673b91a 100644 --- a/submodules/TelegramApi/Sources/Api10.swift +++ b/submodules/TelegramApi/Sources/Api10.swift @@ -255,7 +255,7 @@ public extension Api { case inputInvoicePremiumGiftStars(flags: Int32, userId: Api.InputUser, months: Int32, message: Api.TextWithEntities?) case inputInvoiceSlug(slug: String) case inputInvoiceStarGift(flags: Int32, peer: Api.InputPeer, giftId: Int64, message: Api.TextWithEntities?) - case inputInvoiceStarGiftResale(slug: String, toId: Api.InputPeer) + case inputInvoiceStarGiftResale(flags: Int32, slug: String, toId: Api.InputPeer) case inputInvoiceStarGiftTransfer(stargift: Api.InputSavedStarGift, toId: Api.InputPeer) case inputInvoiceStarGiftUpgrade(flags: Int32, stargift: Api.InputSavedStarGift) case inputInvoiceStars(purpose: Api.InputStorePaymentPurpose) @@ -313,10 +313,11 @@ public extension Api { serializeInt64(giftId, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 1) != 0 {message!.serialize(buffer, true)} break - case .inputInvoiceStarGiftResale(let slug, let toId): + case .inputInvoiceStarGiftResale(let flags, let slug, let toId): if boxed { - buffer.appendInt32(1674298252) + buffer.appendInt32(-1012968668) } + serializeInt32(flags, buffer: buffer, boxed: false) serializeString(slug, buffer: buffer, boxed: false) toId.serialize(buffer, true) break @@ -359,8 +360,8 @@ public extension Api { return ("inputInvoiceSlug", [("slug", slug as Any)]) case .inputInvoiceStarGift(let flags, let peer, let giftId, let message): return ("inputInvoiceStarGift", [("flags", flags as Any), ("peer", peer as Any), ("giftId", giftId as Any), ("message", message as Any)]) - case .inputInvoiceStarGiftResale(let slug, let toId): - return ("inputInvoiceStarGiftResale", [("slug", slug as Any), ("toId", toId as Any)]) + case .inputInvoiceStarGiftResale(let flags, let slug, let toId): + return ("inputInvoiceStarGiftResale", [("flags", flags as Any), ("slug", slug as Any), ("toId", toId as Any)]) case .inputInvoiceStarGiftTransfer(let stargift, let toId): return ("inputInvoiceStarGiftTransfer", [("stargift", stargift as Any), ("toId", toId as Any)]) case .inputInvoiceStarGiftUpgrade(let flags, let stargift): @@ -491,16 +492,19 @@ public extension Api { } } public static func parse_inputInvoiceStarGiftResale(_ reader: BufferReader) -> InputInvoice? { - var _1: String? - _1 = parseString(reader) - var _2: Api.InputPeer? + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: Api.InputPeer? if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.InputPeer + _3 = Api.parse(reader, signature: signature) as? Api.InputPeer } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputInvoice.inputInvoiceStarGiftResale(slug: _1!, toId: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.InputInvoice.inputInvoiceStarGiftResale(flags: _1!, slug: _2!, toId: _3!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api15.swift b/submodules/TelegramApi/Sources/Api15.swift index 5c2505e379..508eff99e8 100644 --- a/submodules/TelegramApi/Sources/Api15.swift +++ b/submodules/TelegramApi/Sources/Api15.swift @@ -394,7 +394,7 @@ public extension Api { case messageActionSetChatWallPaper(flags: Int32, wallpaper: Api.WallPaper) case messageActionSetMessagesTTL(flags: Int32, period: Int32, autoSettingFrom: Int64?) case messageActionStarGift(flags: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, convertStars: Int64?, upgradeMsgId: Int32?, upgradeStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?) - case messageActionStarGiftUnique(flags: Int32, gift: Api.StarGift, canExportAt: Int32?, transferStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?, resaleStars: Int64?, canTransferAt: Int32?, canResellAt: Int32?) + case messageActionStarGiftUnique(flags: Int32, gift: Api.StarGift, canExportAt: Int32?, transferStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?, resaleAmount: Api.StarsAmount?, canTransferAt: Int32?, canResellAt: Int32?) case messageActionSuggestProfilePhoto(photo: Api.Photo) case messageActionSuggestedPostApproval(flags: Int32, rejectComment: String?, scheduleDate: Int32?, price: Api.StarsAmount?) case messageActionSuggestedPostRefund(flags: Int32) @@ -797,9 +797,9 @@ public extension Api { if Int(flags) & Int(1 << 12) != 0 {peer!.serialize(buffer, true)} if Int(flags) & Int(1 << 12) != 0 {serializeInt64(savedId!, buffer: buffer, boxed: false)} break - case .messageActionStarGiftUnique(let flags, let gift, let canExportAt, let transferStars, let fromId, let peer, let savedId, let resaleStars, let canTransferAt, let canResellAt): + case .messageActionStarGiftUnique(let flags, let gift, let canExportAt, let transferStars, let fromId, let peer, let savedId, let resaleAmount, let canTransferAt, let canResellAt): if boxed { - buffer.appendInt32(775611918) + buffer.appendInt32(888627955) } serializeInt32(flags, buffer: buffer, boxed: false) gift.serialize(buffer, true) @@ -808,7 +808,7 @@ public extension Api { if Int(flags) & Int(1 << 6) != 0 {fromId!.serialize(buffer, true)} if Int(flags) & Int(1 << 7) != 0 {peer!.serialize(buffer, true)} if Int(flags) & Int(1 << 7) != 0 {serializeInt64(savedId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 8) != 0 {serializeInt64(resaleStars!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 8) != 0 {resaleAmount!.serialize(buffer, true)} if Int(flags) & Int(1 << 9) != 0 {serializeInt32(canTransferAt!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 10) != 0 {serializeInt32(canResellAt!, buffer: buffer, boxed: false)} break @@ -993,8 +993,8 @@ public extension Api { return ("messageActionSetMessagesTTL", [("flags", flags as Any), ("period", period as Any), ("autoSettingFrom", autoSettingFrom as Any)]) case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeMsgId, let upgradeStars, let fromId, let peer, let savedId): return ("messageActionStarGift", [("flags", flags as Any), ("gift", gift as Any), ("message", message as Any), ("convertStars", convertStars as Any), ("upgradeMsgId", upgradeMsgId as Any), ("upgradeStars", upgradeStars as Any), ("fromId", fromId as Any), ("peer", peer as Any), ("savedId", savedId as Any)]) - case .messageActionStarGiftUnique(let flags, let gift, let canExportAt, let transferStars, let fromId, let peer, let savedId, let resaleStars, let canTransferAt, let canResellAt): - return ("messageActionStarGiftUnique", [("flags", flags as Any), ("gift", gift as Any), ("canExportAt", canExportAt as Any), ("transferStars", transferStars as Any), ("fromId", fromId as Any), ("peer", peer as Any), ("savedId", savedId as Any), ("resaleStars", resaleStars as Any), ("canTransferAt", canTransferAt as Any), ("canResellAt", canResellAt as Any)]) + case .messageActionStarGiftUnique(let flags, let gift, let canExportAt, let transferStars, let fromId, let peer, let savedId, let resaleAmount, let canTransferAt, let canResellAt): + return ("messageActionStarGiftUnique", [("flags", flags as Any), ("gift", gift as Any), ("canExportAt", canExportAt as Any), ("transferStars", transferStars as Any), ("fromId", fromId as Any), ("peer", peer as Any), ("savedId", savedId as Any), ("resaleAmount", resaleAmount as Any), ("canTransferAt", canTransferAt as Any), ("canResellAt", canResellAt as Any)]) case .messageActionSuggestProfilePhoto(let photo): return ("messageActionSuggestProfilePhoto", [("photo", photo as Any)]) case .messageActionSuggestedPostApproval(let flags, let rejectComment, let scheduleDate, let price): @@ -1792,8 +1792,10 @@ public extension Api { } } var _7: Int64? if Int(_1!) & Int(1 << 7) != 0 {_7 = reader.readInt64() } - var _8: Int64? - if Int(_1!) & Int(1 << 8) != 0 {_8 = reader.readInt64() } + var _8: Api.StarsAmount? + if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.StarsAmount + } } var _9: Int32? if Int(_1!) & Int(1 << 9) != 0 {_9 = reader.readInt32() } var _10: Int32? @@ -1809,7 +1811,7 @@ public extension Api { let _c9 = (Int(_1!) & Int(1 << 9) == 0) || _9 != nil let _c10 = (Int(_1!) & Int(1 << 10) == 0) || _10 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.MessageAction.messageActionStarGiftUnique(flags: _1!, gift: _2!, canExportAt: _3, transferStars: _4, fromId: _5, peer: _6, savedId: _7, resaleStars: _8, canTransferAt: _9, canResellAt: _10) + return Api.MessageAction.messageActionStarGiftUnique(flags: _1!, gift: _2!, canExportAt: _3, transferStars: _4, fromId: _5, peer: _6, savedId: _7, resaleAmount: _8, canTransferAt: _9, canResellAt: _10) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api24.swift b/submodules/TelegramApi/Sources/Api24.swift index 204da6c177..cab7fede4f 100644 --- a/submodules/TelegramApi/Sources/Api24.swift +++ b/submodules/TelegramApi/Sources/Api24.swift @@ -637,7 +637,7 @@ public extension Api { public extension Api { enum StarGift: TypeConstructorDescription { case starGift(flags: Int32, id: Int64, sticker: Api.Document, stars: Int64, availabilityRemains: Int32?, availabilityTotal: Int32?, availabilityResale: Int64?, convertStars: Int64, firstSaleDate: Int32?, lastSaleDate: Int32?, upgradeStars: Int64?, resellMinStars: Int64?, title: String?, releasedBy: Api.Peer?, perUserTotal: Int32?, perUserRemains: Int32?) - case starGiftUnique(flags: Int32, id: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, ownerAddress: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32, giftAddress: String?, resellStars: Int64?, releasedBy: Api.Peer?) + case starGiftUnique(flags: Int32, id: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, ownerAddress: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32, giftAddress: String?, resellAmount: [Api.StarsAmount]?, releasedBy: Api.Peer?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -662,9 +662,9 @@ public extension Api { if Int(flags) & Int(1 << 8) != 0 {serializeInt32(perUserTotal!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 8) != 0 {serializeInt32(perUserRemains!, buffer: buffer, boxed: false)} break - case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellStars, let releasedBy): + case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy): if boxed { - buffer.appendInt32(-164136786) + buffer.appendInt32(975654224) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false) @@ -682,7 +682,11 @@ public extension Api { serializeInt32(availabilityIssued, buffer: buffer, boxed: false) serializeInt32(availabilityTotal, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 3) != 0 {serializeString(giftAddress!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeInt64(resellStars!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(resellAmount!.count)) + for item in resellAmount! { + item.serialize(buffer, true) + }} if Int(flags) & Int(1 << 5) != 0 {releasedBy!.serialize(buffer, true)} break } @@ -692,8 +696,8 @@ public extension Api { switch self { case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let availabilityResale, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars, let resellMinStars, let title, let releasedBy, let perUserTotal, let perUserRemains): return ("starGift", [("flags", flags as Any), ("id", id as Any), ("sticker", sticker as Any), ("stars", stars as Any), ("availabilityRemains", availabilityRemains as Any), ("availabilityTotal", availabilityTotal as Any), ("availabilityResale", availabilityResale as Any), ("convertStars", convertStars as Any), ("firstSaleDate", firstSaleDate as Any), ("lastSaleDate", lastSaleDate as Any), ("upgradeStars", upgradeStars as Any), ("resellMinStars", resellMinStars as Any), ("title", title as Any), ("releasedBy", releasedBy as Any), ("perUserTotal", perUserTotal as Any), ("perUserRemains", perUserRemains as Any)]) - case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellStars, let releasedBy): - return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("ownerAddress", ownerAddress as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any), ("giftAddress", giftAddress as Any), ("resellStars", resellStars as Any), ("releasedBy", releasedBy as Any)]) + case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy): + return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("ownerAddress", ownerAddress as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any), ("giftAddress", giftAddress as Any), ("resellAmount", resellAmount as Any), ("releasedBy", releasedBy as Any)]) } } @@ -786,8 +790,10 @@ public extension Api { _11 = reader.readInt32() var _12: String? if Int(_1!) & Int(1 << 3) != 0 {_12 = parseString(reader) } - var _13: Int64? - if Int(_1!) & Int(1 << 4) != 0 {_13 = reader.readInt64() } + var _13: [Api.StarsAmount]? + if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { + _13 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsAmount.self) + } } var _14: Api.Peer? if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() { _14 = Api.parse(reader, signature: signature) as? Api.Peer @@ -807,7 +813,7 @@ public extension Api { let _c13 = (Int(_1!) & Int(1 << 4) == 0) || _13 != nil let _c14 = (Int(_1!) & Int(1 << 5) == 0) || _14 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 { - return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, title: _3!, slug: _4!, num: _5!, ownerId: _6, ownerName: _7, ownerAddress: _8, attributes: _9!, availabilityIssued: _10!, availabilityTotal: _11!, giftAddress: _12, resellStars: _13, releasedBy: _14) + return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, title: _3!, slug: _4!, num: _5!, ownerId: _6, ownerName: _7, ownerAddress: _8, attributes: _9!, availabilityIssued: _10!, availabilityTotal: _11!, giftAddress: _12, resellAmount: _13, releasedBy: _14) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api26.swift b/submodules/TelegramApi/Sources/Api26.swift index dff9d15574..c2d30c953f 100644 --- a/submodules/TelegramApi/Sources/Api26.swift +++ b/submodules/TelegramApi/Sources/Api26.swift @@ -324,6 +324,62 @@ public extension Api { } } +public extension Api { + enum StoryAlbum: TypeConstructorDescription { + case storyAlbum(flags: Int32, albumId: Int32, title: String, iconPhoto: Api.Photo?, iconVideo: Api.Document?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .storyAlbum(let flags, let albumId, let title, let iconPhoto, let iconVideo): + if boxed { + buffer.appendInt32(-1826262950) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(albumId, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {iconPhoto!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {iconVideo!.serialize(buffer, true)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .storyAlbum(let flags, let albumId, let title, let iconPhoto, let iconVideo): + return ("storyAlbum", [("flags", flags as Any), ("albumId", albumId as Any), ("title", title as Any), ("iconPhoto", iconPhoto as Any), ("iconVideo", iconVideo as Any)]) + } + } + + public static func parse_storyAlbum(_ reader: BufferReader) -> StoryAlbum? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: String? + _3 = parseString(reader) + var _4: Api.Photo? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.Photo + } } + var _5: Api.Document? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.Document + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.StoryAlbum.storyAlbum(flags: _1!, albumId: _2!, title: _3!, iconPhoto: _4, iconVideo: _5) + } + else { + return nil + } + } + + } +} public extension Api { enum StoryFwdHeader: TypeConstructorDescription { case storyFwdHeader(flags: Int32, from: Api.Peer?, fromName: String?, storyId: Int32?) @@ -376,15 +432,15 @@ public extension Api { } public extension Api { indirect enum StoryItem: TypeConstructorDescription { - case storyItem(flags: Int32, id: Int32, date: Int32, fromId: Api.Peer?, fwdFrom: Api.StoryFwdHeader?, expireDate: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, mediaAreas: [Api.MediaArea]?, privacy: [Api.PrivacyRule]?, views: Api.StoryViews?, sentReaction: Api.Reaction?) + case storyItem(flags: Int32, id: Int32, date: Int32, fromId: Api.Peer?, fwdFrom: Api.StoryFwdHeader?, expireDate: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, mediaAreas: [Api.MediaArea]?, privacy: [Api.PrivacyRule]?, views: Api.StoryViews?, sentReaction: Api.Reaction?, albums: [Int32]?) case storyItemDeleted(id: Int32) case storyItemSkipped(flags: Int32, id: Int32, date: Int32, expireDate: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction): + case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction, let albums): if boxed { - buffer.appendInt32(2041735716) + buffer.appendInt32(-302947087) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false) @@ -411,6 +467,11 @@ public extension Api { }} if Int(flags) & Int(1 << 3) != 0 {views!.serialize(buffer, true)} if Int(flags) & Int(1 << 15) != 0 {sentReaction!.serialize(buffer, true)} + if Int(flags) & Int(1 << 19) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(albums!.count)) + for item in albums! { + serializeInt32(item, buffer: buffer, boxed: false) + }} break case .storyItemDeleted(let id): if boxed { @@ -432,8 +493,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction): - return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("fromId", fromId as Any), ("fwdFrom", fwdFrom as Any), ("expireDate", expireDate as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("mediaAreas", mediaAreas as Any), ("privacy", privacy as Any), ("views", views as Any), ("sentReaction", sentReaction as Any)]) + case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction, let albums): + return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("fromId", fromId as Any), ("fwdFrom", fwdFrom as Any), ("expireDate", expireDate as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("mediaAreas", mediaAreas as Any), ("privacy", privacy as Any), ("views", views as Any), ("sentReaction", sentReaction as Any), ("albums", albums as Any)]) case .storyItemDeleted(let id): return ("storyItemDeleted", [("id", id as Any)]) case .storyItemSkipped(let flags, let id, let date, let expireDate): @@ -484,6 +545,10 @@ public extension Api { if Int(_1!) & Int(1 << 15) != 0 {if let signature = reader.readInt32() { _13 = Api.parse(reader, signature: signature) as? Api.Reaction } } + var _14: [Int32]? + if Int(_1!) & Int(1 << 19) != 0 {if let _ = reader.readInt32() { + _14 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -497,8 +562,9 @@ public extension Api { let _c11 = (Int(_1!) & Int(1 << 2) == 0) || _11 != nil let _c12 = (Int(_1!) & Int(1 << 3) == 0) || _12 != nil let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 { - return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, fromId: _4, fwdFrom: _5, expireDate: _6!, caption: _7, entities: _8, media: _9!, mediaAreas: _10, privacy: _11, views: _12, sentReaction: _13) + let _c14 = (Int(_1!) & Int(1 << 19) == 0) || _14 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 { + return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, fromId: _4, fwdFrom: _5, expireDate: _6!, caption: _7, entities: _8, media: _9!, mediaAreas: _10, privacy: _11, views: _12, sentReaction: _13, albums: _14) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api37.swift b/submodules/TelegramApi/Sources/Api37.swift index 62bcb3429a..cbba07a113 100644 --- a/submodules/TelegramApi/Sources/Api37.swift +++ b/submodules/TelegramApi/Sources/Api37.swift @@ -482,6 +482,64 @@ public extension Api.storage { } } +public extension Api.stories { + enum Albums: TypeConstructorDescription { + case albums(hash: Int64, albums: [Api.StoryAlbum]) + case albumsNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .albums(let hash, let albums): + if boxed { + buffer.appendInt32(-1013417414) + } + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(albums.count)) + for item in albums { + item.serialize(buffer, true) + } + break + case .albumsNotModified: + if boxed { + buffer.appendInt32(1448008427) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .albums(let hash, let albums): + return ("albums", [("hash", hash as Any), ("albums", albums as Any)]) + case .albumsNotModified: + return ("albumsNotModified", []) + } + } + + public static func parse_albums(_ reader: BufferReader) -> Albums? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.StoryAlbum]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryAlbum.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.stories.Albums.albums(hash: _1!, albums: _2!) + } + else { + return nil + } + } + public static func parse_albumsNotModified(_ reader: BufferReader) -> Albums? { + return Api.stories.Albums.albumsNotModified + } + + } +} public extension Api.stories { enum AllStories: TypeConstructorDescription { case allStories(flags: Int32, count: Int32, state: String, peerStories: [Api.PeerStories], chats: [Api.Chat], users: [Api.User], stealthMode: Api.StoriesStealthMode) @@ -1428,55 +1486,3 @@ public extension Api.updates { } } -public extension Api.updates { - enum State: TypeConstructorDescription { - case state(pts: Int32, qts: Int32, date: Int32, seq: Int32, unreadCount: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .state(let pts, let qts, let date, let seq, let unreadCount): - if boxed { - buffer.appendInt32(-1519637954) - } - serializeInt32(pts, buffer: buffer, boxed: false) - serializeInt32(qts, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt32(seq, buffer: buffer, boxed: false) - serializeInt32(unreadCount, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .state(let pts, let qts, let date, let seq, let unreadCount): - return ("state", [("pts", pts as Any), ("qts", qts as Any), ("date", date as Any), ("seq", seq as Any), ("unreadCount", unreadCount as Any)]) - } - } - - public static func parse_state(_ reader: BufferReader) -> State? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.updates.State.state(pts: _1!, qts: _2!, date: _3!, seq: _4!, unreadCount: _5!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api38.swift b/submodules/TelegramApi/Sources/Api38.swift index e76c07649a..281a495dbf 100644 --- a/submodules/TelegramApi/Sources/Api38.swift +++ b/submodules/TelegramApi/Sources/Api38.swift @@ -1,3 +1,55 @@ +public extension Api.updates { + enum State: TypeConstructorDescription { + case state(pts: Int32, qts: Int32, date: Int32, seq: Int32, unreadCount: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .state(let pts, let qts, let date, let seq, let unreadCount): + if boxed { + buffer.appendInt32(-1519637954) + } + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(qts, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt32(seq, buffer: buffer, boxed: false) + serializeInt32(unreadCount, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .state(let pts, let qts, let date, let seq, let unreadCount): + return ("state", [("pts", pts as Any), ("qts", qts as Any), ("date", date as Any), ("seq", seq as Any), ("unreadCount", unreadCount as Any)]) + } + } + + public static func parse_state(_ reader: BufferReader) -> State? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.updates.State.state(pts: _1!, qts: _2!, date: _3!, seq: _4!, unreadCount: _5!) + } + else { + return nil + } + } + + } +} public extension Api.upload { enum CdnFile: TypeConstructorDescription { case cdnFile(bytes: Buffer) diff --git a/submodules/TelegramApi/Sources/Api39.swift b/submodules/TelegramApi/Sources/Api39.swift index 43e04f36f2..7df8e9d44c 100644 --- a/submodules/TelegramApi/Sources/Api39.swift +++ b/submodules/TelegramApi/Sources/Api39.swift @@ -10058,12 +10058,12 @@ public extension Api.functions.payments { } } public extension Api.functions.payments { - static func updateStarGiftPrice(stargift: Api.InputSavedStarGift, resellStars: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func updateStarGiftPrice(stargift: Api.InputSavedStarGift, resellAmount: Api.StarsAmount) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(1001301217) + buffer.appendInt32(-306287413) stargift.serialize(buffer, true) - serializeInt64(resellStars, buffer: buffer, boxed: false) - return (FunctionDescription(name: "payments.updateStarGiftPrice", parameters: [("stargift", String(describing: stargift)), ("resellStars", String(describing: resellStars))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + resellAmount.serialize(buffer, true) + return (FunctionDescription(name: "payments.updateStarGiftPrice", parameters: [("stargift", String(describing: stargift)), ("resellAmount", String(describing: resellAmount))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) var result: Api.Updates? if let signature = reader.readInt32() { @@ -11371,6 +11371,43 @@ public extension Api.functions.stories { }) } } +public extension Api.functions.stories { + static func createAlbum(peer: Api.InputPeer, title: String, stories: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1553754395) + peer.serialize(buffer, true) + serializeString(title, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stories.count)) + for item in stories { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "stories.createAlbum", parameters: [("peer", String(describing: peer)), ("title", String(describing: title)), ("stories", String(describing: stories))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.StoryAlbum? in + let reader = BufferReader(buffer) + var result: Api.StoryAlbum? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.StoryAlbum + } + return result + }) + } +} +public extension Api.functions.stories { + static func deleteAlbum(peer: Api.InputPeer, albumId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1925949744) + peer.serialize(buffer, true) + serializeInt32(albumId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stories.deleteAlbum", parameters: [("peer", String(describing: peer)), ("albumId", String(describing: albumId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} public extension Api.functions.stories { static func deleteStories(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { let buffer = Buffer() @@ -11441,6 +11478,40 @@ public extension Api.functions.stories { }) } } +public extension Api.functions.stories { + static func getAlbumStories(peer: Api.InputPeer, albumId: Int32, offset: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1400869535) + peer.serialize(buffer, true) + serializeInt32(albumId, buffer: buffer, boxed: false) + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stories.getAlbumStories", parameters: [("peer", String(describing: peer)), ("albumId", String(describing: albumId)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Stories? in + let reader = BufferReader(buffer) + var result: Api.stories.Stories? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stories.Stories + } + return result + }) + } +} +public extension Api.functions.stories { + static func getAlbums(peer: Api.InputPeer, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(632548039) + peer.serialize(buffer, true) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stories.getAlbums", parameters: [("peer", String(describing: peer)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Albums? in + let reader = BufferReader(buffer) + var result: Api.stories.Albums? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stories.Albums + } + return result + }) + } +} public extension Api.functions.stories { static func getAllReadPeerStories() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -11671,6 +11742,26 @@ public extension Api.functions.stories { }) } } +public extension Api.functions.stories { + static func reorderAlbums(peer: Api.InputPeer, order: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2060059687) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "stories.reorderAlbums", parameters: [("peer", String(describing: peer)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} public extension Api.functions.stories { static func report(peer: Api.InputPeer, id: [Int32], option: Buffer, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -11732,9 +11823,9 @@ public extension Api.functions.stories { } } public extension Api.functions.stories { - static func sendStory(flags: Int32, peer: Api.InputPeer, media: Api.InputMedia, mediaAreas: [Api.MediaArea]?, caption: String?, entities: [Api.MessageEntity]?, privacyRules: [Api.InputPrivacyRule], randomId: Int64, period: Int32?, fwdFromId: Api.InputPeer?, fwdFromStory: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func sendStory(flags: Int32, peer: Api.InputPeer, media: Api.InputMedia, mediaAreas: [Api.MediaArea]?, caption: String?, entities: [Api.MessageEntity]?, privacyRules: [Api.InputPrivacyRule], randomId: Int64, period: Int32?, fwdFromId: Api.InputPeer?, fwdFromStory: Int32?, albums: [Int32]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-454661813) + buffer.appendInt32(1937752812) serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) media.serialize(buffer, true) @@ -11758,7 +11849,12 @@ public extension Api.functions.stories { if Int(flags) & Int(1 << 3) != 0 {serializeInt32(period!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 6) != 0 {fwdFromId!.serialize(buffer, true)} if Int(flags) & Int(1 << 6) != 0 {serializeInt32(fwdFromStory!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "stories.sendStory", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("media", String(describing: media)), ("mediaAreas", String(describing: mediaAreas)), ("caption", String(describing: caption)), ("entities", String(describing: entities)), ("privacyRules", String(describing: privacyRules)), ("randomId", String(describing: randomId)), ("period", String(describing: period)), ("fwdFromId", String(describing: fwdFromId)), ("fwdFromStory", String(describing: fwdFromStory))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + if Int(flags) & Int(1 << 8) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(albums!.count)) + for item in albums! { + serializeInt32(item, buffer: buffer, boxed: false) + }} + return (FunctionDescription(name: "stories.sendStory", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("media", String(describing: media)), ("mediaAreas", String(describing: mediaAreas)), ("caption", String(describing: caption)), ("entities", String(describing: entities)), ("privacyRules", String(describing: privacyRules)), ("randomId", String(describing: randomId)), ("period", String(describing: period)), ("fwdFromId", String(describing: fwdFromId)), ("fwdFromStory", String(describing: fwdFromStory)), ("albums", String(describing: albums))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) var result: Api.Updates? if let signature = reader.readInt32() { @@ -11840,6 +11936,39 @@ public extension Api.functions.stories { }) } } +public extension Api.functions.stories { + static func updateAlbum(flags: Int32, peer: Api.InputPeer, albumId: Int32, title: String?, deleteStories: [Int32]?, addStories: [Int32]?, order: [Int32]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1582455222) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(albumId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(deleteStories!.count)) + for item in deleteStories! { + serializeInt32(item, buffer: buffer, boxed: false) + }} + if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(addStories!.count)) + for item in addStories! { + serializeInt32(item, buffer: buffer, boxed: false) + }} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order!.count)) + for item in order! { + serializeInt32(item, buffer: buffer, boxed: false) + }} + return (FunctionDescription(name: "stories.updateAlbum", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("albumId", String(describing: albumId)), ("title", String(describing: title)), ("deleteStories", String(describing: deleteStories)), ("addStories", String(describing: addStories)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.StoryAlbum? in + let reader = BufferReader(buffer) + var result: Api.StoryAlbum? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.StoryAlbum + } + return result + }) + } +} public extension Api.functions.updates { static func getChannelDifference(flags: Int32, channel: Api.InputChannel, filter: Api.ChannelMessagesFilter, pts: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift index c3c28bd13e..e29a337285 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift @@ -192,11 +192,11 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe return nil } return TelegramMediaAction(action: .starGift(gift: gift, convertStars: convertStars, text: text, entities: entities, nameHidden: (flags & (1 << 0)) != 0, savedToProfile: (flags & (1 << 2)) != 0, converted: (flags & (1 << 3)) != 0, upgraded: (flags & (1 << 5)) != 0, canUpgrade: (flags & (1 << 10)) != 0, upgradeStars: upgradeStars, isRefunded: (flags & (1 << 9)) != 0, upgradeMessageId: upgradeMessageId, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId)) - case let .messageActionStarGiftUnique(flags, apiGift, canExportAt, transferStars, fromId, peer, savedId, resaleStars, canTransferDate, canResaleDate): + case let .messageActionStarGiftUnique(flags, apiGift, canExportAt, transferStars, fromId, peer, savedId, resaleAmount, canTransferDate, canResaleDate): guard let gift = StarGift(apiStarGift: apiGift) else { return nil } - return TelegramMediaAction(action: .starGiftUnique(gift: gift, isUpgrade: (flags & (1 << 0)) != 0, isTransferred: (flags & (1 << 1)) != 0, savedToProfile: (flags & (1 << 2)) != 0, canExportDate: canExportAt, transferStars: transferStars, isRefunded: (flags & (1 << 5)) != 0, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId, resaleStars: resaleStars, canTransferDate: canTransferDate, canResaleDate: canResaleDate)) + return TelegramMediaAction(action: .starGiftUnique(gift: gift, isUpgrade: (flags & (1 << 0)) != 0, isTransferred: (flags & (1 << 1)) != 0, savedToProfile: (flags & (1 << 2)) != 0, canExportDate: canExportAt, transferStars: transferStars, isRefunded: (flags & (1 << 5)) != 0, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId, resaleAmount: resaleAmount.flatMap { CurrencyAmount(apiAmount: $0) }, canTransferDate: canTransferDate, canResaleDate: canResaleDate)) case let .messageActionPaidMessagesRefunded(count, stars): return TelegramMediaAction(action: .paidMessagesRefunded(count: count, stars: stars)) case let .messageActionPaidMessagesPrice(flags, stars): diff --git a/submodules/TelegramCore/Sources/State/AppUpdate.swift b/submodules/TelegramCore/Sources/State/AppUpdate.swift index b7f99f5037..753bad030c 100644 --- a/submodules/TelegramCore/Sources/State/AppUpdate.swift +++ b/submodules/TelegramCore/Sources/State/AppUpdate.swift @@ -10,6 +10,13 @@ public struct AppUpdateInfo: Equatable { public let version: String public let text: String public let entities: [MessageTextEntity] + + public init(blocking: Bool, version: String, text: String, entities: [MessageTextEntity]) { + self.blocking = blocking + self.version = version + self.text = text + self.entities = entities + } } extension AppUpdateInfo { diff --git a/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift b/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift index b7af0258ec..c8e63e28ad 100644 --- a/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift +++ b/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift @@ -354,7 +354,8 @@ func managedUniqueStarGifts(accountPeerId: PeerId, postbox: Postbox, network: Ne ], availability: StarGift.UniqueGift.Availability(issued: 0, total: 0), giftAddress: nil, - resellStars: nil, + resellAmounts: nil, + resellForTonOnly: false, releasedBy: nil ) if let entry = CodableEntry(RecentStarGiftItem(gift)) { diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index ddd61c801e..1cda543eba 100644 --- a/submodules/TelegramCore/Sources/State/Serialization.swift +++ b/submodules/TelegramCore/Sources/State/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 210 + return 211 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift index f935eac984..51fd2c771d 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift @@ -244,7 +244,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case giftStars(currency: String, amount: Int64, count: Int64, cryptoCurrency: String?, cryptoAmount: Int64?, transactionId: String?) case prizeStars(amount: Int64, isUnclaimed: Bool, boostPeerId: PeerId?, transactionId: String?, giveawayMessageId: MessageId?) case starGift(gift: StarGift, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, canUpgrade: Bool, upgradeStars: Int64?, isRefunded: Bool, upgradeMessageId: Int32?, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?) - case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?, resaleStars: Int64?, canTransferDate: Int32?, canResaleDate: Int32?) + case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?, resaleAmount: CurrencyAmount?, canTransferDate: Int32?, canResaleDate: Int32?) case paidMessagesRefunded(count: Int32, stars: Int64) case paidMessagesPriceEdited(stars: Int64, broadcastMessagesAllowed: Bool) case conferenceCall(ConferenceCall) @@ -377,7 +377,13 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case 44: self = .starGift(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, convertStars: decoder.decodeOptionalInt64ForKey("convertStars"), text: decoder.decodeOptionalStringForKey("text"), entities: decoder.decodeOptionalObjectArrayWithDecoderForKey("entities"), nameHidden: decoder.decodeBoolForKey("nameHidden", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), converted: decoder.decodeBoolForKey("converted", orElse: false), upgraded: decoder.decodeBoolForKey("upgraded", orElse: false), canUpgrade: decoder.decodeBoolForKey("canUpgrade", orElse: false), upgradeStars: decoder.decodeOptionalInt64ForKey("upgradeStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), upgradeMessageId: decoder.decodeOptionalInt32ForKey("upgradeMessageId"), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId")) case 45: - self = .starGiftUnique(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, isUpgrade: decoder.decodeBoolForKey("isUpgrade", orElse: false), isTransferred: decoder.decodeBoolForKey("isTransferred", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), canExportDate: decoder.decodeOptionalInt32ForKey("canExportDate"), transferStars: decoder.decodeOptionalInt64ForKey("transferStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId"), resaleStars: decoder.decodeOptionalInt64ForKey("resaleStars"), canTransferDate: decoder.decodeOptionalInt32ForKey("canTransferDate"), canResaleDate: decoder.decodeOptionalInt32ForKey("canResaleDate")) + var resaleAmount: CurrencyAmount? + if let amount = decoder.decodeCodable(CurrencyAmount.self, forKey: "resaleAmount") { + resaleAmount = amount + } else if let stars = decoder.decodeOptionalInt64ForKey("resaleStars") { + resaleAmount = CurrencyAmount(amount: StarsAmount(value: stars, nanos: 0), currency: .stars) + } + self = .starGiftUnique(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, isUpgrade: decoder.decodeBoolForKey("isUpgrade", orElse: false), isTransferred: decoder.decodeBoolForKey("isTransferred", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), canExportDate: decoder.decodeOptionalInt32ForKey("canExportDate"), transferStars: decoder.decodeOptionalInt64ForKey("transferStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId"), resaleAmount: resaleAmount, canTransferDate: decoder.decodeOptionalInt32ForKey("canTransferDate"), canResaleDate: decoder.decodeOptionalInt32ForKey("canResaleDate")) case 46: self = .paidMessagesRefunded(count: decoder.decodeInt32ForKey("count", orElse: 0), stars: decoder.decodeInt64ForKey("stars", orElse: 0)) case 47: @@ -745,7 +751,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "savedId") } - case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded, peerId, senderId, savedId, resaleStars, canTransferDate, canResaleDate): + case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded, peerId, senderId, savedId, resaleAmount, canTransferDate, canResaleDate): encoder.encodeInt32(45, forKey: "_rawValue") encoder.encodeObject(gift, forKey: "gift") encoder.encodeBool(isUpgrade, forKey: "isUpgrade") @@ -777,10 +783,10 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "savedId") } - if let resaleStars { - encoder.encodeInt64(resaleStars, forKey: "resaleStars") + if let resaleAmount { + encoder.encodeCodable(resaleAmount, forKey: "resaleAmount") } else { - encoder.encodeNil(forKey: "resaleStars") + encoder.encodeNil(forKey: "resaleAmount") } if let canTransferDate { encoder.encodeInt32(canTransferDate, forKey: "canTransferDate") diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index 59e1183adb..9f04c8492a 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -1235,7 +1235,8 @@ func _internal_uploadStoryImpl( randomId: randomId, period: Int32(period), fwdFromId: fwdFromId, - fwdFromStory: fwdFromStory + fwdFromStory: fwdFromStory, + albums: nil )) |> map(Optional.init) |> `catch` { _ -> Signal in @@ -1259,7 +1260,7 @@ func _internal_uploadStoryImpl( for update in updates.allUpdates { if case let .updateStory(_, story) = update { switch story { - case let .storyItem(_, idValue, _, fromId, _, _, _, _, media, _, _, _, _): + case let .storyItem(_, idValue, _, fromId, _, _, _, _, media, _, _, _, _, _): if let parsedStory = Stories.StoredItem(apiStoryItem: story, peerId: toPeerId, transaction: transaction) { var items = transaction.getStoryItems(peerId: toPeerId) var updatedItems: [Stories.Item] = [] @@ -1648,7 +1649,7 @@ func _internal_editStory(account: Account, peerId: PeerId, id: Int32, media: Eng for update in updates.allUpdates { if case let .updateStory(_, story) = update { switch story { - case let .storyItem(_, _, _, _, _, _, _, _, media, _, _, _, _): + case let .storyItem(_, _, _, _, _, _, _, _, media, _, _, _, _, _): let parsedMedia = textMediaAndExpirationTimerFromApiMedia(media, account.peerId).media if let parsedMedia = parsedMedia, let originalMedia = originalMedia { applyMediaResourceChanges(from: originalMedia, to: parsedMedia, postbox: account.postbox, force: false, skipPreviews: updatingCoverTime) @@ -2009,7 +2010,7 @@ func _internal_updatePinnedToTopStories(account: Account, peerId: PeerId, ids: [ extension Api.StoryItem { var id: Int32 { switch self { - case let .storyItem(_, id, _, _, _, _, _, _, _, _, _, _, _): + case let .storyItem(_, id, _, _, _, _, _, _, _, _, _, _, _, _): return id case let .storyItemDeleted(id): return id @@ -2072,7 +2073,7 @@ extension Stories.Item.ForwardInfo { extension Stories.StoredItem { init?(apiStoryItem: Api.StoryItem, existingItem: Stories.Item? = nil, peerId: PeerId, transaction: Transaction) { switch apiStoryItem { - case let .storyItem(flags, id, date, fromId, forwardFrom, expireDate, caption, entities, media, mediaAreas, privacy, views, sentReaction): + case let .storyItem(flags, id, date, fromId, forwardFrom, expireDate, caption, entities, media, mediaAreas, privacy, views, sentReaction, _): let parsedMedia = textMediaAndExpirationTimerFromApiMedia(media, peerId).media if let parsedMedia = parsedMedia { var parsedPrivacy: Stories.Item.Privacy? diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift index 173c4b50c5..066228e8d8 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift @@ -17,7 +17,7 @@ public enum BotPaymentInvoiceSource { case starGiftUpgrade(keepOriginalInfo: Bool, reference: StarGiftReference) case starGiftTransfer(reference: StarGiftReference, toPeerId: EnginePeer.Id) case premiumGift(peerId: EnginePeer.Id, option: CachedPremiumGiftOption, text: String?, entities: [MessageTextEntity]?) - case starGiftResale(slug: String, toPeerId: EnginePeer.Id) + case starGiftResale(slug: String, toPeerId: EnginePeer.Id, ton: Bool) } public struct BotPaymentInvoiceFields: OptionSet { @@ -403,11 +403,15 @@ func _internal_parseInputInvoice(transaction: Transaction, source: BotPaymentInv message = .textWithEntities(text: text, entities: entities.flatMap { apiEntitiesFromMessageTextEntities($0, associatedPeers: SimpleDictionary()) } ?? []) } return .inputInvoicePremiumGiftStars(flags: flags, userId: inputUser, months: option.months, message: message) - case let .starGiftResale(slug, toPeerId): + case let .starGiftResale(slug, toPeerId, ton): guard let peer = transaction.getPeer(toPeerId), let inputPeer = apiInputPeer(peer) else { return nil } - return .inputInvoiceStarGiftResale(slug: slug, toId: inputPeer) + var flags: Int32 = 0 + if ton { + flags |= 1 << 0 + } + return .inputInvoiceStarGiftResale(flags: flags, slug: slug, toId: inputPeer) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index c36f1a4c14..86ae3847b7 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -301,6 +301,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding { case availability case giftAddress case resellStars + case resellAmounts + case resellForTonOnly case releasedBy } @@ -554,10 +556,11 @@ public enum StarGift: Equatable, Codable, PostboxCoding { public let attributes: [Attribute] public let availability: Availability public let giftAddress: String? - public let resellStars: Int64? + public let resellAmounts: [CurrencyAmount]? + public let resellForTonOnly: Bool public let releasedBy: EnginePeer.Id? - public init(id: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellStars: Int64?, releasedBy: EnginePeer.Id?) { + public init(id: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?) { self.id = id self.title = title self.number = number @@ -566,7 +569,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding { self.attributes = attributes self.availability = availability self.giftAddress = giftAddress - self.resellStars = resellStars + self.resellAmounts = resellAmounts + self.resellForTonOnly = resellForTonOnly self.releasedBy = releasedBy } @@ -588,7 +592,14 @@ public enum StarGift: Equatable, Codable, PostboxCoding { self.attributes = try container.decode([UniqueGift.Attribute].self, forKey: .attributes) self.availability = try container.decode(UniqueGift.Availability.self, forKey: .availability) self.giftAddress = try container.decodeIfPresent(String.self, forKey: .giftAddress) - self.resellStars = try container.decodeIfPresent(Int64.self, forKey: .resellStars) + if let resellAmounts = try container.decodeIfPresent([CurrencyAmount].self, forKey: .resellAmounts) { + self.resellAmounts = resellAmounts + } else if let resellStars = try container.decodeIfPresent(Int64.self, forKey: .resellStars) { + self.resellAmounts = [CurrencyAmount(amount: StarsAmount(value: resellStars, nanos: 0), currency: .stars)] + } else { + self.resellAmounts = [] + } + self.resellForTonOnly = try container.decodeIfPresent(Bool.self, forKey: .resellForTonOnly) ?? false self.releasedBy = try container.decodeIfPresent(EnginePeer.Id.self, forKey: .releasedBy) } @@ -609,7 +620,14 @@ public enum StarGift: Equatable, Codable, PostboxCoding { self.attributes = (try? decoder.decodeObjectArrayWithCustomDecoderForKey(CodingKeys.attributes.rawValue, decoder: { UniqueGift.Attribute(decoder: $0) })) ?? [] self.availability = decoder.decodeObjectForKey(CodingKeys.availability.rawValue, decoder: { UniqueGift.Availability(decoder: $0) }) as! UniqueGift.Availability self.giftAddress = decoder.decodeOptionalStringForKey(CodingKeys.giftAddress.rawValue) - self.resellStars = decoder.decodeOptionalInt64ForKey(CodingKeys.resellStars.rawValue) + if let resellAmounts = decoder.decodeCodable([CurrencyAmount].self, forKey: CodingKeys.resellAmounts.rawValue) { + self.resellAmounts = resellAmounts + } else if let resellStars = decoder.decodeOptionalInt64ForKey(CodingKeys.resellStars.rawValue) { + self.resellAmounts = [CurrencyAmount(amount: StarsAmount(value: resellStars, nanos: 0), currency: .stars)] + } else { + self.resellAmounts = nil + } + self.resellForTonOnly = decoder.decodeBoolForKey(CodingKeys.resellForTonOnly.rawValue, orElse: false) self.releasedBy = decoder.decodeOptionalInt64ForKey(CodingKeys.releasedBy.rawValue).flatMap { EnginePeer.Id($0) } } @@ -630,7 +648,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding { try container.encode(self.attributes, forKey: .attributes) try container.encode(self.availability, forKey: .availability) try container.encodeIfPresent(self.giftAddress, forKey: .giftAddress) - try container.encodeIfPresent(self.resellStars, forKey: .resellStars) + try container.encodeIfPresent(self.resellAmounts, forKey: .resellAmounts) + try container.encode(self.resellForTonOnly, forKey: .resellForTonOnly) try container.encodeIfPresent(self.releasedBy, forKey: .releasedBy) } @@ -654,11 +673,12 @@ public enum StarGift: Equatable, Codable, PostboxCoding { } else { encoder.encodeNil(forKey: CodingKeys.giftAddress.rawValue) } - if let resellStars = self.resellStars { - encoder.encodeInt64(resellStars, forKey: CodingKeys.resellStars.rawValue) + if let resellAmounts = self.resellAmounts { + encoder.encodeCodable(resellAmounts, forKey: CodingKeys.resellAmounts.rawValue) } else { - encoder.encodeNil(forKey: CodingKeys.resellStars.rawValue) + encoder.encodeNil(forKey: CodingKeys.resellAmounts.rawValue) } + encoder.encodeBool(self.resellForTonOnly, forKey: CodingKeys.resellForTonOnly.rawValue) if let releasedBy = self.releasedBy { encoder.encodeInt64(releasedBy.toInt64(), forKey: CodingKeys.releasedBy.rawValue) } else { @@ -666,7 +686,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding { } } - public func withResellStars(_ resellStars: Int64?) -> UniqueGift { + public func withResellAmounts(_ resellAmounts: [CurrencyAmount]?) -> UniqueGift { return UniqueGift( id: self.id, title: self.title, @@ -676,7 +696,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding { attributes: self.attributes, availability: self.availability, giftAddress: self.giftAddress, - resellStars: resellStars, + resellAmounts: resellAmounts, + resellForTonOnly: self.resellForTonOnly, releasedBy: self.releasedBy ) } @@ -787,7 +808,7 @@ extension StarGift { return nil } self = .generic(StarGift.Gift(id: id, title: title, file: file, price: stars, convertStars: convertStars, availability: availability, soldOut: soldOut, flags: flags, upgradeStars: upgradeStars, releasedBy: releasedBy?.peerId, perUserLimit: perUserLimit)) - case let .starGiftUnique(_, id, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, resellStars, releasedBy): + case let .starGiftUnique(flags, id, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, resellAmounts, releasedBy): let owner: StarGift.UniqueGift.Owner if let ownerAddress { owner = .address(ownerAddress) @@ -798,7 +819,8 @@ extension StarGift { } else { return nil } - self = .unique(StarGift.UniqueGift(id: id, title: title, number: num, slug: slug, owner: owner, attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) }, availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal), giftAddress: giftAddress, resellStars: resellStars, releasedBy: releasedBy?.peerId)) + let resellAmounts = resellAmounts?.compactMap { CurrencyAmount(apiAmount: $0) } + self = .unique(StarGift.UniqueGift(id: id, title: title, number: num, slug: slug, owner: owner, attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) }, availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal), giftAddress: giftAddress, resellAmounts: resellAmounts, resellForTonOnly: (flags & (1 << 7)) != 0, releasedBy: releasedBy?.peerId)) } } } @@ -948,8 +970,8 @@ public enum UpgradeStarGiftError { case generic } -func _internal_buyStarGift(account: Account, slug: String, peerId: EnginePeer.Id, price: Int64?) -> Signal { - let source: BotPaymentInvoiceSource = .starGiftResale(slug: slug, toPeerId: peerId) +func _internal_buyStarGift(account: Account, slug: String, peerId: EnginePeer.Id, price: CurrencyAmount?) -> Signal { + let source: BotPaymentInvoiceSource = .starGiftResale(slug: slug, toPeerId: peerId, ton: price?.currency == .ton) return _internal_fetchBotPaymentForm(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, source: source, themeParams: nil) |> map(Optional.init) |> `catch` { error -> Signal in @@ -960,7 +982,7 @@ func _internal_buyStarGift(account: Account, slug: String, peerId: EnginePeer.Id } |> mapToSignal { paymentForm in if let paymentForm { - if let paymentPrice = paymentForm.invoice.prices.first?.amount, let price, paymentPrice > price { + if let paymentPrice = paymentForm.invoice.prices.first?.amount, let price, paymentPrice > price.amount.value { return .fail(.priceChanged(paymentPrice)) } return _internal_sendStarsPaymentForm(account: account, formId: paymentForm.id, source: source) @@ -1594,15 +1616,15 @@ private final class ProfileGiftsContextImpl { return _internal_transferStarGift(account: self.account, prepaid: prepaid, reference: reference, peerId: peerId) } - func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64?) -> Signal { - var listingPrice: Int64? + func buyStarGift(slug: String, peerId: EnginePeer.Id, price: CurrencyAmount?) -> Signal { + var listingPrice: CurrencyAmount? if let gift = self.gifts.first(where: { gift in if case let .unique(uniqueGift) = gift.gift, uniqueGift.slug == slug { return true } return false }), case let .unique(uniqueGift) = gift.gift { - listingPrice = uniqueGift.resellStars + listingPrice = uniqueGift.resellAmounts?.first(where: { $0.currency == .stars }) } if listingPrice == nil { @@ -1612,7 +1634,7 @@ private final class ProfileGiftsContextImpl { } return false }), case let .unique(uniqueGift) = gift.gift { - listingPrice = uniqueGift.resellStars + listingPrice = uniqueGift.resellAmounts?.first(where: { $0.currency == .stars }) } } @@ -1776,7 +1798,7 @@ private final class ProfileGiftsContextImpl { } } - func updateStarGiftResellPrice(reference: StarGiftReference, price: Int64?, id: Int64?) -> Signal { + func updateStarGiftResellPrice(reference: StarGiftReference, price: CurrencyAmount?, id: Int64?) -> Signal { return Signal { [weak self] subscriber in guard let self else { return EmptyDisposable @@ -1806,7 +1828,7 @@ private final class ProfileGiftsContextImpl { return false }) { if case let .unique(uniqueGift) = self.gifts[index].gift { - let updatedUniqueGift = uniqueGift.withResellStars(price) + let updatedUniqueGift = uniqueGift.withResellAmounts(price.flatMap { [$0] }) let updatedGift = self.gifts[index].withGift(.unique(updatedUniqueGift)) self.gifts[index] = updatedGift } @@ -1829,7 +1851,7 @@ private final class ProfileGiftsContextImpl { return false }) { if case let .unique(uniqueGift) = self.filteredGifts[index].gift { - let updatedUniqueGift = uniqueGift.withResellStars(price) + let updatedUniqueGift = uniqueGift.withResellAmounts(price.flatMap { [$0] }) let updatedGift = self.filteredGifts[index].withGift(.unique(updatedUniqueGift)) self.filteredGifts[index] = updatedGift } @@ -2254,7 +2276,7 @@ public final class ProfileGiftsContext { } } - public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64? = nil) -> Signal { + public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: CurrencyAmount? = nil) -> Signal { return Signal { subscriber in let disposable = MetaDisposable() self.impl.with { impl in @@ -2322,7 +2344,7 @@ public final class ProfileGiftsContext { } } - public func updateStarGiftResellPrice(reference: StarGiftReference, price: Int64?, id: Int64? = nil) -> Signal { + public func updateStarGiftResellPrice(reference: StarGiftReference, price: CurrencyAmount?, id: Int64? = nil) -> Signal { return Signal { subscriber in let disposable = MetaDisposable() self.impl.with { impl in @@ -2668,7 +2690,7 @@ func _internal_toggleStarGiftsNotifications(account: Account, peerId: EnginePeer } } -func _internal_updateStarGiftResalePrice(account: Account, reference: StarGiftReference, price: Int64?) -> Signal { +func _internal_updateStarGiftResalePrice(account: Account, reference: StarGiftReference, price: CurrencyAmount?) -> Signal { return account.postbox.transaction { transaction in return reference.apiStarGiftReference(transaction: transaction) } @@ -2677,7 +2699,8 @@ func _internal_updateStarGiftResalePrice(account: Account, reference: StarGiftRe guard let starGift else { return .complete() } - return account.network.request(Api.functions.payments.updateStarGiftPrice(stargift: starGift, resellStars: price ?? 0)) + let apiAmount = (price ?? CurrencyAmount(amount: .zero, currency: .stars)).apiAmount + return account.network.request(Api.functions.payments.updateStarGiftPrice(stargift: starGift, resellAmount: apiAmount)) |> mapError { error -> UpdateStarGiftPriceError in if error.errorDescription.hasPrefix("STARGIFT_RESELL_TOO_EARLY_") { let timeout = String(error.errorDescription[error.errorDescription.index(error.errorDescription.startIndex, offsetBy: "STARGIFT_RESELL_TOO_EARLY_".count)...]) @@ -2840,7 +2863,7 @@ private final class ResaleGiftsContextImpl { var mappedGifts: [StarGift] = [] for gift in gifts { - if let mappedGift = StarGift(apiStarGift: gift), case let .unique(uniqueGift) = mappedGift, let resellStars = uniqueGift.resellStars, resellStars > 0 { + if let mappedGift = StarGift(apiStarGift: gift), case let .unique(uniqueGift) = mappedGift, let resellAmount = uniqueGift.resellAmounts?.first, resellAmount.amount.value > 0 { mappedGifts.append(mappedGift) } } @@ -2904,15 +2927,15 @@ private final class ResaleGiftsContextImpl { self.loadMore() } - func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64?) -> Signal { - var listingPrice: Int64? + func buyStarGift(slug: String, peerId: EnginePeer.Id, price: CurrencyAmount?) -> Signal { + var listingPrice: CurrencyAmount? if let gift = self.gifts.first(where: { gift in if case let .unique(uniqueGift) = gift, uniqueGift.slug == slug { return true } return false }), case let .unique(uniqueGift) = gift { - listingPrice = uniqueGift.resellStars + listingPrice = uniqueGift.resellAmounts?.first(where: { $0.currency == .stars }) } return _internal_buyStarGift(account: self.account, slug: slug, peerId: peerId, price: price ?? listingPrice) @@ -2935,7 +2958,7 @@ private final class ResaleGiftsContextImpl { } } - func updateStarGiftResellPrice(slug: String, price: Int64?) -> Signal { + func updateStarGiftResellPrice(slug: String, price: CurrencyAmount?) -> Signal { return Signal { [weak self] subscriber in guard let self else { return EmptyDisposable @@ -2958,7 +2981,7 @@ private final class ResaleGiftsContextImpl { }) { if let price { if case let .unique(uniqueGift) = self.gifts[index] { - self.gifts[index] = .unique(uniqueGift.withResellStars(price)) + self.gifts[index] = .unique(uniqueGift.withResellAmounts([price])) } } else { self.gifts.remove(at: index) @@ -3062,7 +3085,7 @@ public final class ResaleGiftsContext { } } - public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64? = nil) -> Signal { + public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: CurrencyAmount? = nil) -> Signal { return Signal { subscriber in let disposable = MetaDisposable() self.impl.with { impl in @@ -3076,7 +3099,7 @@ public final class ResaleGiftsContext { } } - public func updateStarGiftResellPrice(slug: String, price: Int64?) -> Signal { + public func updateStarGiftResellPrice(slug: String, price: CurrencyAmount?) -> Signal { return Signal { subscriber in let disposable = MetaDisposable() self.impl.with { impl in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift index 2ab196f326..4723234c4c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift @@ -129,7 +129,7 @@ public extension TelegramEngine { return _internal_transferStarGift(account: self.account, prepaid: prepaid, reference: reference, peerId: peerId) } - public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: Int64?) -> Signal { + public func buyStarGift(slug: String, peerId: EnginePeer.Id, price: CurrencyAmount?) -> Signal { return _internal_buyStarGift(account: self.account, slug: slug, peerId: peerId, price: price) } @@ -157,7 +157,7 @@ public extension TelegramEngine { return _internal_toggleStarGiftsNotifications(account: self.account, peerId: peerId, enabled: enabled) } - public func updateStarGiftResalePrice(reference: StarGiftReference, price: Int64?) -> Signal { + public func updateStarGiftResalePrice(reference: StarGiftReference, price: CurrencyAmount?) -> Signal { return _internal_updateStarGiftResalePrice(account: self.account, reference: reference, price: price) } diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index cb5b9cfe4e..de7e93229d 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -1204,7 +1204,14 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, attributedString = NSAttributedString(string: strings.Notification_StarsGift_SentSomeone, font: titleFont, textColor: primaryTextColor) } else if message.author?.id == accountPeerId { if let resaleStars { - let starsString = strings.Notification_StarsGift_Bought_Stars(Int32(resaleStars)) + let starsString: String + switch resaleStars.currency { + case .stars: + starsString = strings.Notification_StarsGift_Bought_Stars(Int32(resaleStars.amount.value)) + case .ton: + //TODO:localize + starsString = "\(Int32(resaleStars.amount.value)) TON" + } if message.id.peerId == accountPeerId { attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_BoughtForYouself(starsString)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) } else { @@ -1237,7 +1244,14 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, } else { var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) if let resaleStars { - let starsString = strings.Notification_StarsGift_Bought_Stars(Int32(resaleStars)) + let starsString: String + switch resaleStars.currency { + case .stars: + starsString = strings.Notification_StarsGift_Bought_Stars(Int32(resaleStars.amount.value)) + case .ton: + //TODO:localize + starsString = "\(Int32(resaleStars.amount.value)) TON" + } let giftTitle = "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, dateTimeFormat.groupingSeparator))" attributes[1] = boldAttributes attributes[2] = boldAttributes diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift index 7d6660d633..0244ada798 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift @@ -233,7 +233,8 @@ final class GiftStoreScreenComponent: Component { color: ribbonColor ) - let subject: GiftItemComponent.Subject = .uniqueGift(gift: uniqueGift, price: "# \(presentationStringsFormattedNumber(Int32(uniqueGift.resellStars ?? 0), environment.dateTimeFormat.groupingSeparator))") + //TODO:release + let subject: GiftItemComponent.Subject = .uniqueGift(gift: uniqueGift, price: "# \(presentationStringsFormattedNumber(Int32(uniqueGift.resellAmounts?.first(where: { $0.currency == .stars })?.amount.value ?? 0), environment.dateTimeFormat.groupingSeparator))") let _ = visibleItem.update( transition: itemTransition, component: AnyComponent( diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 9ff2dc3ff3..adcffc2135 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -176,8 +176,8 @@ private final class GiftViewSheetContent: CombinedComponent { } } - if let _ = arguments.resellStars { - self.buyFormDisposable = (context.engine.payments.fetchBotPaymentForm(source: .starGiftResale(slug: gift.slug, toPeerId: context.account.peerId), themeParams: nil) + if let _ = arguments.resellAmounts { + self.buyFormDisposable = (context.engine.payments.fetchBotPaymentForm(source: .starGiftResale(slug: gift.slug, toPeerId: context.account.peerId, ton: false), themeParams: nil) |> deliverOnMainQueue).start(next: { [weak self] paymentForm in guard let self else { return @@ -289,8 +289,8 @@ private final class GiftViewSheetContent: CombinedComponent { var minRequiredAmount = StarsAmount(value: 100, nanos: 0) var canUpgrade = false - if let resellStars = self.subject.arguments?.resellStars { - minRequiredAmount = StarsAmount(value: resellStars, nanos: 0) + if let resellStars = self.subject.arguments?.resellAmounts?.first(where: { $0.currency == .stars }) { + minRequiredAmount = resellStars.amount } else if let arguments = self.subject.arguments, arguments.canUpgrade && arguments.upgradeStars == nil { canUpgrade = true } @@ -816,7 +816,7 @@ private final class GiftViewSheetContent: CombinedComponent { let giftTitle = "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, presentationData.dateTimeFormat.groupingSeparator))" let reference = arguments.reference ?? .slug(slug: gift.slug) - if let resellStars = gift.resellStars, resellStars > 0, !update { + if let resellStars = gift.resellAmounts?.first, resellStars.amount.value > 0, !update { let alertController = textAlertController( context: context, title: presentationData.strings.Gift_View_Resale_Unlist_Title, @@ -835,9 +835,9 @@ private final class GiftViewSheetContent: CombinedComponent { } switch self.subject { case let .profileGift(peerId, currentSubject): - self.subject = .profileGift(peerId, currentSubject.withGift(.unique(gift.withResellStars(nil)))) + self.subject = .profileGift(peerId, currentSubject.withGift(.unique(gift.withResellAmounts(nil)))) case let .uniqueGift(_, recipientPeerId): - self.subject = .uniqueGift(gift.withResellStars(nil), recipientPeerId) + self.subject = .uniqueGift(gift.withResellAmounts(nil), recipientPeerId) default: break } @@ -876,6 +876,8 @@ private final class GiftViewSheetContent: CombinedComponent { return } + let price = CurrencyAmount(amount: StarsAmount(value: price, nanos: 0), currency: .stars) + let _ = ((controller.updateResellStars?(price) ?? context.engine.payments.updateStarGiftResalePrice(reference: reference, price: price)) |> deliverOnMainQueue).startStandalone(error: { [weak self, weak controller] error in guard let self else { @@ -911,9 +913,9 @@ private final class GiftViewSheetContent: CombinedComponent { switch self.subject { case let .profileGift(peerId, currentSubject): - self.subject = .profileGift(peerId, currentSubject.withGift(.unique(gift.withResellStars(price)))) + self.subject = .profileGift(peerId, currentSubject.withGift(.unique(gift.withResellAmounts([price])))) case let .uniqueGift(_, recipientPeerId): - self.subject = .uniqueGift(gift.withResellStars(price), recipientPeerId) + self.subject = .uniqueGift(gift.withResellAmounts([price]), recipientPeerId) default: break } @@ -921,7 +923,7 @@ private final class GiftViewSheetContent: CombinedComponent { var text = presentationData.strings.Gift_View_Resale_List_Success(giftTitle).string if update { - let starsString = presentationData.strings.Gift_View_Resale_Relist_Success_Stars(Int32(price)) + let starsString = presentationData.strings.Gift_View_Resale_Relist_Success_Stars(Int32(price.amount.value)) text = presentationData.strings.Gift_View_Resale_Relist_Success(giftTitle, starsString).string } @@ -1022,7 +1024,7 @@ private final class GiftViewSheetContent: CombinedComponent { }))) } - if case let .unique(gift) = arguments.gift, let resellStars = gift.resellStars, resellStars > 0 { + if case let .unique(gift) = arguments.gift, let resellAmount = gift.resellAmounts?.first, resellAmount.amount.value > 0 { if arguments.reference != nil || gift.owner.peerId == context.account.peerId { items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_View_Context_ChangePrice, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PriceTag"), color: theme.contextMenu.primaryColor) @@ -1066,7 +1068,7 @@ private final class GiftViewSheetContent: CombinedComponent { } } - if let _ = arguments.resellStars, case let .uniqueGift(uniqueGift, recipientPeerId) = subject, let _ = recipientPeerId { + if let _ = arguments.resellAmounts, case let .uniqueGift(uniqueGift, recipientPeerId) = subject, let _ = recipientPeerId { items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_View_Context_ViewInProfile, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Peer Info/ShowIcon"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in @@ -1163,8 +1165,8 @@ private final class GiftViewSheetContent: CombinedComponent { } } - func commitBuy(acceptedPrice: Int64? = nil, skipConfirmation: Bool = false) { - guard let resellStars = self.subject.arguments?.resellStars, let starsContext = self.context.starsContext, let starsState = starsContext.currentState, case let .unique(uniqueGift) = self.subject.arguments?.gift else { + func commitBuy(acceptedPrice: CurrencyAmount? = nil, skipConfirmation: Bool = false) { + guard let resellAmount = self.subject.arguments?.resellAmounts?.first, let starsContext = self.context.starsContext, let starsState = starsContext.currentState, case let .unique(uniqueGift) = self.subject.arguments?.gift else { return } @@ -1201,7 +1203,7 @@ private final class GiftViewSheetContent: CombinedComponent { self.inProgress = true self.updated() - let buyGiftImpl: ((String, EnginePeer.Id, Int64?) -> Signal) + let buyGiftImpl: ((String, EnginePeer.Id, CurrencyAmount?) -> Signal) if let buyGift = controller.buyGift { buyGiftImpl = { slug, peerId, price in return buyGift(slug, peerId, price) @@ -1218,7 +1220,7 @@ private final class GiftViewSheetContent: CombinedComponent { } } - self.buyDisposable = (buyGiftImpl(uniqueGift.slug, recipientPeerId, acceptedPrice ?? resellStars) + self.buyDisposable = (buyGiftImpl(uniqueGift.slug, recipientPeerId, acceptedPrice ?? resellAmount) |> deliverOnMainQueue).start( error: { [weak self] error in guard let self, let controller = self.getController() else { @@ -1230,8 +1232,9 @@ private final class GiftViewSheetContent: CombinedComponent { switch error { case let .priceChanged(newPrice): + //TODO:release let errorTitle = presentationData.strings.Gift_Buy_ErrorPriceChanged_Title - let originalPriceString = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text_Stars(Int32(resellStars)) + let originalPriceString = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text_Stars(Int32(resellAmount.amount.value)) let newPriceString = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text_Stars(Int32(newPrice)) let errorText = presentationData.strings.Gift_Buy_ErrorPriceChanged_Text(originalPriceString, newPriceString).string @@ -1244,7 +1247,8 @@ private final class GiftViewSheetContent: CombinedComponent { guard let self else { return } - self.commitBuy(acceptedPrice: newPrice, skipConfirmation: true) + //TODO:release + self.commitBuy(acceptedPrice: CurrencyAmount(amount: StarsAmount(value: newPrice, nanos: 0), currency: .ton), skipConfirmation: true) }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { }) @@ -1398,7 +1402,8 @@ private final class GiftViewSheetContent: CombinedComponent { return } let text: String - let starsString = presentationData.strings.Gift_Buy_Confirm_Text_Stars(Int32(resellStars)) + //TODO:release + let starsString = presentationData.strings.Gift_Buy_Confirm_Text_Stars(Int32(resellAmount.amount.value)) if recipientPeerId == self.context.account.peerId { text = presentationData.strings.Gift_Buy_Confirm_Text(giftTitle, starsString).string @@ -1410,7 +1415,7 @@ private final class GiftViewSheetContent: CombinedComponent { title: presentationData.strings.Gift_Buy_Confirm_Title, text: text, actions: [ - TextAlertAction(type: .defaultAction, title: presentationData.strings.Gift_Buy_Confirm_BuyFor(Int32(resellStars)), action: { + TextAlertAction(type: .defaultAction, title: presentationData.strings.Gift_Buy_Confirm_BuyFor(Int32(resellAmount.amount.value)), action: { action() }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { @@ -2766,8 +2771,8 @@ private final class GiftViewSheetContent: CombinedComponent { component: PlainButtonComponent( content: AnyComponent( HeaderButtonComponent( - title: uniqueGift.resellStars == nil ? strings.Gift_View_Sell : strings.Gift_View_Unlist, - iconName: uniqueGift.resellStars == nil ? "Premium/Collectible/Sell" : "Premium/Collectible/Unlist" + title: uniqueGift.resellAmounts == nil ? strings.Gift_View_Sell : strings.Gift_View_Unlist, + iconName: uniqueGift.resellAmounts == nil ? "Premium/Collectible/Sell" : "Premium/Collectible/Unlist" ) ), effectAlignment: .center, @@ -3131,17 +3136,18 @@ private final class GiftViewSheetContent: CombinedComponent { originY += table.size.height + 23.0 } - var resellStars: Int64? + var resellStars: CurrencyAmount? var selling = false if let uniqueGift { - resellStars = uniqueGift.resellStars + //TODO:release + resellStars = uniqueGift.resellAmounts?.first(where: { $0.currency == .ton }) if let resellStars { if incoming || ownerPeerId == component.context.account.peerId { let priceButton = priceButton.update( component: PlainButtonComponent( content: AnyComponent( - PriceButtonComponent(price: presentationStringsFormattedNumber(Int32(resellStars), environment.dateTimeFormat.groupingSeparator)) + PriceButtonComponent(price: presentationStringsFormattedNumber(Int32(resellStars.amount.value), environment.dateTimeFormat.groupingSeparator)) ), effectAlignment: .center, action: { [weak state] in @@ -3476,10 +3482,11 @@ private final class GiftViewSheetContent: CombinedComponent { if state.cachedStarImage == nil || state.cachedStarImage?.1 !== theme { state.cachedStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: theme.list.itemCheckColors.foregroundColor)!, theme) } - var upgradeString = strings.Gift_View_BuyFor - upgradeString += " # \(presentationStringsFormattedNumber(Int32(resellStars), environment.dateTimeFormat.groupingSeparator))" + var buyString = strings.Gift_View_BuyFor + //TODO:release + buyString += " # \(presentationStringsFormattedNumber(Int32(resellStars.amount.value), environment.dateTimeFormat.groupingSeparator))" - let buttonTitle = subject.arguments?.upgradeStars != nil ? strings.Gift_Upgrade_Confirm : upgradeString + let buttonTitle = subject.arguments?.upgradeStars != nil ? strings.Gift_Upgrade_Confirm : buyString let buttonAttributedString = NSMutableAttributedString(string: buttonTitle, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center) if let range = buttonAttributedString.string.range(of: "#"), let starImage = state.cachedStarImage?.0 { buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string)) @@ -3659,7 +3666,7 @@ public class GiftViewScreen: ViewControllerComponentContainer { case upgradePreview([StarGift.UniqueGift.Attribute], String) case wearPreview(StarGift.UniqueGift) - var arguments: (peerId: EnginePeer.Id?, fromPeerId: EnginePeer.Id?, fromPeerName: String?, messageId: EngineMessage.Id?, reference: StarGiftReference?, incoming: Bool, gift: StarGift, date: Int32, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, pinnedToTop: Bool?, converted: Bool, upgraded: Bool, refunded: Bool, canUpgrade: Bool, upgradeStars: Int64?, transferStars: Int64?, resellStars: Int64?, canExportDate: Int32?, upgradeMessageId: Int32?, canTransferDate: Int32?, canResaleDate: Int32?)? { + var arguments: (peerId: EnginePeer.Id?, fromPeerId: EnginePeer.Id?, fromPeerName: String?, messageId: EngineMessage.Id?, reference: StarGiftReference?, incoming: Bool, gift: StarGift, date: Int32, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, pinnedToTop: Bool?, converted: Bool, upgraded: Bool, refunded: Bool, canUpgrade: Bool, upgradeStars: Int64?, transferStars: Int64?, resellAmounts: [CurrencyAmount]?, canExportDate: Int32?, upgradeMessageId: Int32?, canTransferDate: Int32?, canResaleDate: Int32?)? { switch self { case let .message(message): if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction { @@ -3692,27 +3699,27 @@ public class GiftViewScreen: ViewControllerComponentContainer { incoming = message.flags.contains(.Incoming) } - var resellStars: Int64? + var resellAmounts: [CurrencyAmount]? if case let .unique(uniqueGift) = gift { - resellStars = uniqueGift.resellStars + resellAmounts = uniqueGift.resellAmounts } - return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, incoming, gift, message.timestamp, nil, nil, nil, false, savedToProfile, nil, false, false, false, false, nil, transferStars, resellStars, canExportDate, nil, canTransferDate, canResaleDate) + return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, incoming, gift, message.timestamp, nil, nil, nil, false, savedToProfile, nil, false, false, false, false, nil, transferStars, resellAmounts, canExportDate, nil, canTransferDate, canResaleDate) default: return nil } } case let .uniqueGift(gift, _), let .wearPreview(gift): - return (nil, nil, nil, nil, nil, false, .unique(gift), 0, nil, nil, nil, false, false, nil, false, false, false, false, nil, nil, gift.resellStars, nil, nil, nil, nil) + return (nil, nil, nil, nil, nil, false, .unique(gift), 0, nil, nil, nil, false, false, nil, false, false, false, false, nil, nil, gift.resellAmounts, nil, nil, nil, nil) case let .profileGift(peerId, gift): var messageId: EngineMessage.Id? if case let .message(messageIdValue) = gift.reference { messageId = messageIdValue } - var resellStars: Int64? + var resellAmounts: [CurrencyAmount]? if case let .unique(uniqueGift) = gift.gift { - resellStars = uniqueGift.resellStars + resellAmounts = uniqueGift.resellAmounts } - return (peerId, gift.fromPeer?.id, gift.fromPeer?.compactDisplayTitle, messageId, gift.reference, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, gift.pinnedToTop, false, false, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, resellStars, gift.canExportDate, nil, gift.canTransferDate, gift.canResaleDate) + return (peerId, gift.fromPeer?.id, gift.fromPeer?.compactDisplayTitle, messageId, gift.reference, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, gift.pinnedToTop, false, false, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, resellAmounts, gift.canExportDate, nil, gift.canTransferDate, gift.canResaleDate) case .soldOutGift: return nil case .upgradePreview: @@ -3736,8 +3743,8 @@ public class GiftViewScreen: ViewControllerComponentContainer { fileprivate let convertToStars: (() -> Void)? fileprivate let transferGift: ((Bool, EnginePeer.Id) -> Signal)? fileprivate let upgradeGift: ((Int64?, Bool) -> Signal)? - fileprivate let buyGift: ((String, EnginePeer.Id, Int64?) -> Signal)? - fileprivate let updateResellStars: ((Int64?) -> Signal)? + fileprivate let buyGift: ((String, EnginePeer.Id, CurrencyAmount?) -> Signal)? + fileprivate let updateResellStars: ((CurrencyAmount?) -> Signal)? fileprivate let togglePinnedToTop: ((Bool) -> Bool)? fileprivate let shareStory: ((StarGift.UniqueGift) -> Void)? @@ -3753,8 +3760,8 @@ public class GiftViewScreen: ViewControllerComponentContainer { convertToStars: (() -> Void)? = nil, transferGift: ((Bool, EnginePeer.Id) -> Signal)? = nil, upgradeGift: ((Int64?, Bool) -> Signal)? = nil, - buyGift: ((String, EnginePeer.Id, Int64?) -> Signal)? = nil, - updateResellStars: ((Int64?) -> Signal)? = nil, + buyGift: ((String, EnginePeer.Id, CurrencyAmount?) -> Signal)? = nil, + updateResellStars: ((CurrencyAmount?) -> Signal)? = nil, togglePinnedToTop: ((Bool) -> Bool)? = nil, shareStory: ((StarGift.UniqueGift) -> Void)? = nil ) { @@ -3813,7 +3820,7 @@ public class GiftViewScreen: ViewControllerComponentContainer { self.view.disablesInteractiveModalDismiss = true - if let arguments = self.subject.arguments, let _ = self.subject.arguments?.resellStars { + if let arguments = self.subject.arguments, let _ = self.subject.arguments?.resellAmounts { if case let .unique(uniqueGift) = arguments.gift, case .peerId(self.context.account.peerId) = uniqueGift.owner { } else { self.showBalance = true diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index f4bc125538..6a5eeb7683 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -3729,7 +3729,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID } } else if case let .gift(gift) = subject { isGift = true - let media: [Media] = [TelegramMediaAction(action: .starGiftUnique(gift: .unique(gift), isUpgrade: false, isTransferred: false, savedToProfile: false, canExportDate: nil, transferStars: nil, isRefunded: false, peerId: nil, senderId: nil, savedId: nil, resaleStars: nil, canTransferDate: nil, canResaleDate: nil))] + let media: [Media] = [TelegramMediaAction(action: .starGiftUnique(gift: .unique(gift), isUpgrade: false, isTransferred: false, savedToProfile: false, canExportDate: nil, transferStars: nil, isRefunded: false, peerId: nil, senderId: nil, savedId: nil, resaleAmount: nil, canTransferDate: nil, canResaleDate: nil))] let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: self.context.account.peerId, namespace: Namespaces.Message.Cloud, id: -1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: media, peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) messages = .single([message]) } else { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift index 2a52b933bc..e9818b62e5 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift @@ -483,7 +483,7 @@ final class GiftsListView: UIView { let peer: GiftItemComponent.Peer? let subject: GiftItemComponent.Subject - var resellPrice: Int64? + var resellAmount: CurrencyAmount? switch product.gift { case let .generic(gift): @@ -498,9 +498,9 @@ final class GiftsListView: UIView { case let .unique(gift): subject = .uniqueGift(gift: gift, price: nil) peer = nil - resellPrice = gift.resellStars + resellAmount = gift.resellAmounts?.first - if let _ = resellPrice { + if let _ = resellAmount { ribbonText = params.presentationData.strings.PeerInfo_Gifts_Sale ribbonFont = .larger ribbonColor = .green @@ -533,6 +533,7 @@ final class GiftsListView: UIView { itemAlpha = 0.3 } + //TODO:release let _ = visibleItem.update( transition: itemTransition, component: AnyComponent( @@ -543,7 +544,7 @@ final class GiftsListView: UIView { peer: peer, subject: subject, ribbon: ribbonText.flatMap { GiftItemComponent.Ribbon(text: $0, font: ribbonFont, color: ribbonColor, outline: ribbonOutline) }, - resellPrice: resellPrice, + resellPrice: resellAmount?.amount.value, isHidden: !product.savedToProfile, isSelected: self.selectedItemIds.contains(itemReferenceId), isPinned: !self.canSelect && product.pinnedToTop, diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/UserApperanceScreen.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/UserApperanceScreen.swift index a627e17bb6..049f96e0a4 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/UserApperanceScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/UserApperanceScreen.swift @@ -460,7 +460,8 @@ final class UserAppearanceScreenComponent: Component { ], availability: StarGift.UniqueGift.Availability(issued: 0, total: 0), giftAddress: nil, - resellStars: nil, + resellAmounts: nil, + resellForTonOnly: false, releasedBy: nil ) signal = component.context.engine.accountData.setStarGiftStatus(starGift: gift, expirationDate: emojiStatus.expirationDate) diff --git a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift index 59ccbe39e3..a8998381f7 100644 --- a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift @@ -942,8 +942,8 @@ private final class SheetContent: CombinedComponent { if case let .starGiftResell(giftToMatch, update, _) = self.mode { if update { - if let resellStars = giftToMatch.resellStars { - self.amount = StarsAmount(value: resellStars, nanos: 0) + if let resellStars = giftToMatch.resellAmounts?.first(where: { $0.currency == .stars }) { + self.amount = resellStars.amount } } else { let _ = (context.engine.payments.cachedStarGifts() From 8d5dc7ff600836a88d23777def61e556d15eb7cd Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 24 Jul 2025 13:15:34 +0200 Subject: [PATCH 18/18] 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) }