From 2a5d0497a27a9dfc331b1b80e21bd4b6a354eac4 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 7 Mar 2025 17:53:00 +0100 Subject: [PATCH 01/13] Update localization (cherry picked from commit d7a8f0dcc36c247e7899974506442a9eb79af2b6) --- .../Telegram-iOS/en.lproj/Localizable.strings | 30 ++++++++++++++++--- .../Sources/PremiumLimitScreen.swift | 12 ++++++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 3df82e28b0..38c3ab5315 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -3703,6 +3703,18 @@ Unused sets are archived when you add more."; "NotificationsSound.Pulse" = "Pulse"; "NotificationsSound.Synth" = "Synth"; +"NotificationsSound.Rebound" = "Rebound"; +"NotificationsSound.Antic" = "Antic"; +"NotificationsSound.Cheers" = "Cheers"; +"NotificationsSound.Droplet" = "Droplet"; +"NotificationsSound.Handoff" = "Handoff"; +"NotificationsSound.Milestone" = "Milestone"; +"NotificationsSound.Passage" = "Passage"; +"NotificationsSound.Portal" = "Portal"; +"NotificationsSound.Rattle" = "Rattle"; +"NotificationsSound.Slide" = "Slide"; +"NotificationsSound.Welcome" = "Welcome"; + "NotificationsSound.Tritone" = "Tri-tone"; "NotificationsSound.Tremolo" = "Tremolo"; "NotificationsSound.Alert" = "Alert"; @@ -9813,10 +9825,6 @@ Sorry for the inconvenience."; "Premium.Stories.Format.Title" = "Links and Formatting"; "Premium.Stories.Format.Text" = "Add links and formatting in captions to your stories."; -"Premium.MaxExpiringStoriesText" = "You can post **%@** stories in **24** hours. Subscribe to **Telegram Premium** to increase this limit to **%@**."; -"Premium.MaxExpiringStoriesNoPremiumText" = "You have reached the limit of **%@** stories per **24** hours."; -"Premium.MaxExpiringStoriesFinalText" = "You have reached the limit of **%@** stories per **24** hours."; - "Premium.MaxStoriesWeeklyText" = "You can post **%@** stories in a week. Upgrade to **Telegram Premium** to increase this limit to **%@**."; "Premium.MaxStoriesWeeklyNoPremiumText" = "You have reached the limit of **%@** stories per week."; "Premium.MaxStoriesWeeklyFinalText" = "You have reached the limit of **%@** stories per week."; @@ -13992,3 +14000,17 @@ Sorry for the inconvenience."; "Conversation.VideoTimeLinkCopied" = "Link with start time at %@ copied to clipboard."; "Share.VideoStartAt" = "Start at %@"; "SendStarReactions.SubtitleFrom" = "from %@"; + +"Premium.MaxExpiringStoriesTextNumberFormat_1" = "**%d** story"; +"Premium.MaxExpiringStoriesTextNumberFormat_any" = "**%d** stories"; +"Premium.MaxExpiringStoriesTextPremiumNumberFormat_1" = "**%d** story"; +"Premium.MaxExpiringStoriesTextPremiumNumberFormat_any" = "**%d** stories"; +"Premium.MaxExpiringStoriesTextFormat" = "You can post %@ in **24** hours. Subscribe to **Telegram Premium** to increase this limit to **%@**."; + +"Premium.MaxExpiringStoriesNoPremiumTextNumberFormat_1" = "**%d** story"; +"Premium.MaxExpiringStoriesNoPremiumTextNumberFormat_any" = "**%d** stories"; +"Premium.MaxExpiringStoriesNoPremiumTextFormat" = "You have reached the limit of %@ per **24** hours."; + +"Premium.MaxExpiringStoriesFinalTextNumberFormat_1" = "**%d** story"; +"Premium.MaxExpiringStoriesFinalTextNumberFormat_any" = "**%d** stories"; +"Premium.MaxExpiringStoriesFinalTextFormat" = "You have reached the limit of %@ stories per **24** hours."; diff --git a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift index b2ab0fa0d4..a313b56cbc 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift @@ -1088,7 +1088,14 @@ private final class LimitSheetContent: CombinedComponent { let premiumLimit = state.premiumLimits.maxExpiringStoriesCount iconName = "Premium/Stories" badgeText = "\(limit)" - string = component.count >= premiumLimit ? strings.Premium_MaxExpiringStoriesFinalText("\(premiumLimit)").string : strings.Premium_MaxExpiringStoriesText("\(limit)", "\(premiumLimit)").string + if component.count >= premiumLimit { + let limitNumberString = strings.Premium_MaxExpiringStoriesFinalTextNumberFormat(Int32(premiumLimit)) + string = strings.Premium_MaxExpiringStoriesFinalTextFormat(limitNumberString).string + } else { + let limitNumberString = strings.Premium_MaxExpiringStoriesTextNumberFormat(Int32(limit)) + let premiumLimitNumberString = strings.Premium_MaxExpiringStoriesTextPremiumNumberFormat(Int32(premiumLimit)) + string = strings.Premium_MaxExpiringStoriesTextFormat(limitNumberString, premiumLimitNumberString).string + } defaultValue = "" premiumValue = component.count >= premiumLimit ? "" : "\(premiumLimit)" badgePosition = max(0.32, CGFloat(component.count) / CGFloat(premiumLimit)) @@ -1096,7 +1103,8 @@ private final class LimitSheetContent: CombinedComponent { if isPremiumDisabled { badgeText = "\(limit)" - string = strings.Premium_MaxExpiringStoriesNoPremiumText("\(limit)").string + let numberString = strings.Premium_MaxExpiringStoriesNoPremiumTextNumberFormat(Int32(limit)) + string = strings.Premium_MaxExpiringStoriesNoPremiumTextFormat(numberString).string } buttonAnimationName = nil case .storiesWeekly: From f75bea10024e34f274fe7bd6b1f28988c1fa44b8 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 7 Mar 2025 17:55:26 +0100 Subject: [PATCH 02/13] Bump version --- versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.json b/versions.json index ded113cd87..26fd2ba0c2 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "11.8", + "app": "11.8.1", "xcode": "16.2", "bazel": "7.3.1:981f82a470bad1349322b6f51c9c6ffa0aa291dab1014fac411543c12e661dff", "macos": "15" From edecdd7fec3d41035d6e461d01b884d42b8dd5ea Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 7 Mar 2025 21:58:30 +0400 Subject: [PATCH 03/13] Various fixes --- .../AuthorizationSequenceController.swift | 2 +- .../BrowserUI/Sources/BrowserWebContent.swift | 4 +- .../ChannelPermissionsController.swift | 34 ++++++------ .../IncomingMessagePrivacyScreen.swift | 2 +- .../TelegramEngine/Payments/StarGifts.swift | 49 ++++++++++++++--- .../Sources/ServiceMessageStrings.swift | 2 +- .../Sources/ChatMessageBubbleItemNode.swift | 24 +++++++-- .../ChatMessageCommentFooterContentNode.swift | 2 +- .../ChatMessageGiftBubbleContentNode.swift | 7 +-- .../Sources/ChatSendStarsScreen.swift | 19 ++++++- .../Sources/GiftTransferAlertController.swift | 4 +- .../Sources/GiftViewScreen.swift | 34 +++++++++--- .../Sources/GiftWithdrawAlertController.swift | 2 +- .../Sources/MessageInputPanelComponent.swift | 2 +- .../Sources/MessagePriceItem.swift | 15 ++++-- .../Sources/PeerInfoGiftsCoverComponent.swift | 2 +- .../Sources/PeerInfoHeaderNode.swift | 1 + .../Sources/PeerInfoScreen.swift | 6 ++- .../GiftContextPreviewController.swift | 2 +- .../Sources/PeerInfoGiftsPaneNode.swift | 52 +++++++------------ .../Sources/StarsTransactionScreen.swift | 2 +- .../StarsTransactionsListPanelComponent.swift | 2 +- .../Sources/SharedAccountContext.swift | 2 +- submodules/WebUI/Sources/WebAppWebView.swift | 4 +- 24 files changed, 181 insertions(+), 94 deletions(-) diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift index cb4520a32f..293e7475b9 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift @@ -351,7 +351,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth |> deliverOnMainQueue).startStrict(error: { [weak self] error in if let self, case .alreadyInProgress = error { let formattedNumber = formatPhoneNumber(number) - let title = NSAttributedString(string: self.presentationData.strings.Login_Email_PremiumRequiredTitle, font: Font.semibold(self.presentationData.listsFontSize.baseDisplaySize), textColor: self.presentationData.theme.actionSheet.primaryTextColor) + let title = NSAttributedString(string: self.presentationData.strings.Login_Email_PremiumRequiredTitle, font: Font.semibold(self.presentationData.listsFontSize.baseDisplaySize), textColor: self.presentationData.theme.actionSheet.primaryTextColor, paragraphAlignment: .center) let text = parseMarkdownIntoAttributedString(self.presentationData.strings.Login_Email_PremiumRequiredText(formattedNumber).string, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in nil }), textAlignment: .center).mutableCopy() as! NSMutableAttributedString let alertController = textWithEntitiesAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: title, text: text, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_OK, action: { })]) diff --git a/submodules/BrowserUI/Sources/BrowserWebContent.swift b/submodules/BrowserUI/Sources/BrowserWebContent.swift index 4866b8d284..3ffa7a7d6a 100644 --- a/submodules/BrowserUI/Sources/BrowserWebContent.swift +++ b/submodules/BrowserUI/Sources/BrowserWebContent.swift @@ -1722,10 +1722,10 @@ function tgBrowserHandleMutations(mutations) { if (mutation.addedNodes && mutation.addedNodes.length > 0) { mutation.addedNodes.forEach((newNode) => { if (newNode.tagName === 'VIDEO') { - disableWebkitEnterFullscreen(newNode); + tgBrowserDisableWebkitEnterFullscreen(newNode); } if (newNode.querySelectorAll) { - newNode.querySelectorAll('video').forEach(disableWebkitEnterFullscreen); + newNode.querySelectorAll('video').forEach(tgBrowserDisableWebkitEnterFullscreen); } }); } diff --git a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift index 86c02ce352..e278d77c7a 100644 --- a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift @@ -37,10 +37,10 @@ private final class ChannelPermissionsControllerArguments { let openChannelExample: () -> Void let updateSlowmode: (Int32) -> Void let updateUnrestrictBoosters: (Int32) -> Void - let updateStarsAmount: (StarsAmount?) -> Void + let updateStarsAmount: (StarsAmount?, Bool) -> Void let toggleIsOptionExpanded: (TelegramChatBannedRightsFlags) -> Void - init(context: AccountContext, updatePermission: @escaping (TelegramChatBannedRightsFlags, Bool) -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, addPeer: @escaping () -> Void, removePeer: @escaping (EnginePeer.Id) -> Void, openPeer: @escaping (ChannelParticipant) -> Void, openPeerInfo: @escaping (EnginePeer) -> Void, openKicked: @escaping () -> Void, presentRestrictedPermissionAlert: @escaping (TelegramChatBannedRightsFlags) -> Void, presentConversionToBroadcastGroup: @escaping () -> Void, openChannelExample: @escaping () -> Void, updateSlowmode: @escaping (Int32) -> Void, updateUnrestrictBoosters: @escaping (Int32) -> Void, updateStarsAmount: @escaping (StarsAmount?) -> Void, toggleIsOptionExpanded: @escaping (TelegramChatBannedRightsFlags) -> Void) { + init(context: AccountContext, updatePermission: @escaping (TelegramChatBannedRightsFlags, Bool) -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, addPeer: @escaping () -> Void, removePeer: @escaping (EnginePeer.Id) -> Void, openPeer: @escaping (ChannelParticipant) -> Void, openPeerInfo: @escaping (EnginePeer) -> Void, openKicked: @escaping () -> Void, presentRestrictedPermissionAlert: @escaping (TelegramChatBannedRightsFlags) -> Void, presentConversionToBroadcastGroup: @escaping () -> Void, openChannelExample: @escaping () -> Void, updateSlowmode: @escaping (Int32) -> Void, updateUnrestrictBoosters: @escaping (Int32) -> Void, updateStarsAmount: @escaping (StarsAmount?, Bool) -> Void, toggleIsOptionExpanded: @escaping (TelegramChatBannedRightsFlags) -> Void) { self.context = context self.updatePermission = updatePermission self.addPeer = addPeer @@ -418,15 +418,15 @@ private enum ChannelPermissionsEntry: ItemListNodeEntry { } case let .chargeForMessages(_, title, value): return ItemListSwitchItem(presentationData: presentationData, title: title, value: value, sectionId: self.section, style: .blocks, updated: { value in - arguments.updateStarsAmount(value ? StarsAmount(value: 400, nanos: 0) : nil) + arguments.updateStarsAmount(value ? StarsAmount(value: 400, nanos: 0) : nil, true) }) case let .chargeForMessagesInfo(_, value): return ItemListTextItem(presentationData: presentationData, text: .plain(value), sectionId: self.section) case let .messagePriceHeader(_, value): return ItemListSectionHeaderItem(presentationData: presentationData, text: value, sectionId: self.section) case let .messagePrice(_, value, maxValue, price): - return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, isEnabled: true, minValue: 1, maxValue: maxValue, value: value, price: price, sectionId: self.section, updated: { value in - arguments.updateStarsAmount(StarsAmount(value: value, nanos: 0)) + return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, isEnabled: true, minValue: 1, maxValue: maxValue, value: value, price: price, sectionId: self.section, updated: { value, apply in + arguments.updateStarsAmount(StarsAmount(value: value, nanos: 0), apply) }) case let .messagePriceInfo(_, value): return ItemListTextItem(presentationData: presentationData, text: .plain(value), sectionId: self.section) @@ -1248,23 +1248,25 @@ public func channelPermissionsController(context: AccountContext, updatedPresent updateUnrestrictBoostersDisposable.set((context.engine.peers.updateChannelBoostsToUnlockRestrictions(peerId: view.peerId, boosts: value) |> deliverOnMainQueue).start()) }) - }, updateStarsAmount: { value in + }, updateStarsAmount: { value, apply in updateState { state in var state = state state.modifiedStarsAmount = value return state } - let _ = (peerView.get() - |> take(1) - |> deliverOnMainQueue).start(next: { view in - var effectiveValue = value - if value?.value == 0 { - effectiveValue = nil - } - updateSendPaidMessageStarsDisposable.set((context.engine.peers.updateChannelPaidMessagesStars(peerId: view.peerId, stars: effectiveValue) - |> deliverOnMainQueue).start()) - }) + if apply { + let _ = (peerView.get() + |> take(1) + |> deliverOnMainQueue).start(next: { view in + var effectiveValue = value + if value?.value == 0 { + effectiveValue = nil + } + updateSendPaidMessageStarsDisposable.set((context.engine.peers.updateChannelPaidMessagesStars(peerId: view.peerId, stars: effectiveValue) + |> deliverOnMainQueue).start()) + }) + } }, toggleIsOptionExpanded: { flags in updateState { state in var state = state diff --git a/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift b/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift index 3ab7c7b914..090e6672ed 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift @@ -149,7 +149,7 @@ private enum GlobalAutoremoveEntry: ItemListNodeEntry { case .priceHeader: return ItemListSectionHeaderItem(presentationData: presentationData, text: presentationData.strings.Privacy_Messages_MessagePrice, sectionId: self.section) case let .price(value, maxValue, price, isEnabled): - return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, isEnabled: isEnabled, minValue: 1, maxValue: maxValue, value: value, price: price, sectionId: self.section, updated: { value in + return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, isEnabled: isEnabled, minValue: 1, maxValue: maxValue, value: value, price: price, sectionId: self.section, updated: { value, _ in arguments.updateValue(.paidMessages(StarsAmount(value: value, nanos: 0))) }, openPremiumInfo: { arguments.openPremiumInfo() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index 203b53e543..72c1af1dc3 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -1016,7 +1016,10 @@ private final class ProfileGiftsContextImpl { let sorting = self.sorting let isFiltered = self.filter != .All || self.sorting != .date - + if !isFiltered { + self.filteredGifts = [] + self.filteredCount = nil + } let dataState = isFiltered ? self.filteredDataState : self.dataState if case let .ready(true, initialNextOffset) = dataState { @@ -1202,7 +1205,6 @@ private final class ProfileGiftsContextImpl { } } let existingGifts = Set(pinnedGifts.compactMap { $0.reference }) - var updatedGifts: [ProfileGiftsContext.State.StarGift] = [] for gift in self.gifts { if let reference = gift.reference, existingGifts.contains(reference) { @@ -1216,18 +1218,44 @@ private final class ProfileGiftsContextImpl { updatedGifts.insert(contentsOf: pinnedGifts, at: 0) self.gifts = updatedGifts - if let index = self.filteredGifts.firstIndex(where: { $0.reference == reference }) { - self.filteredGifts[index] = self.filteredGifts[index].withPinnedToTop(pinnedToTop) + var effectiveReferences = pinnedGifts.compactMap { $0.reference } + if !self.filteredGifts.isEmpty { + var filteredPinnedGifts = self.filteredGifts.filter { $0.pinnedToTop } + if var gift = self.filteredGifts.first(where: { $0.reference == reference }) { + gift = gift.withPinnedToTop(pinnedToTop) + if pinnedToTop { + if !gift.savedToProfile { + gift = gift.withSavedToProfile(true) + } + filteredPinnedGifts.append(gift) + } else { + filteredPinnedGifts.removeAll(where: { $0.reference == reference }) + } + } + let existingFilteredGifts = Set(filteredPinnedGifts.compactMap { $0.reference }) + var updatedFilteredGifts: [ProfileGiftsContext.State.StarGift] = [] + for gift in self.filteredGifts { + if let reference = gift.reference, existingFilteredGifts.contains(reference) { + continue + } + updatedFilteredGifts.append(gift) + } + updatedFilteredGifts.sort { lhs, rhs in + lhs.date > rhs.date + } + updatedFilteredGifts.insert(contentsOf: filteredPinnedGifts, at: 0) + self.filteredGifts = updatedFilteredGifts + + effectiveReferences = filteredPinnedGifts.compactMap { $0.reference } } + self.pushState() - var signal = _internal_updateStarGiftsPinnedToTop(account: self.account, peerId: self.peerId, references: pinnedGifts.compactMap { $0.reference }) - + var signal = _internal_updateStarGiftsPinnedToTop(account: self.account, peerId: self.peerId, references: effectiveReferences) if saveToProfile { signal = _internal_updateStarGiftAddedToProfile(account: self.account, reference: reference, added: true) |> then(signal) } - self.actionDisposable.set( (signal |> deliverOn(self.queue)).startStrict(completed: { [weak self] in self?.reload() @@ -1279,7 +1307,6 @@ private final class ProfileGiftsContextImpl { |> ignoreValues |> then(signal) } - self.actionDisposable.set( (signal |> deliverOn(self.queue)).startStrict(completed: { [weak self] in self?.reload() @@ -1349,6 +1376,9 @@ private final class ProfileGiftsContextImpl { } func updateFilter(_ filter: ProfileGiftsContext.Filters) { + guard self.filter != filter else { + return + } self.filter = filter self.filteredDataState = .ready(canLoadMore: true, nextOffset: nil) self.pushState() @@ -1357,6 +1387,9 @@ private final class ProfileGiftsContextImpl { } func updateSorting(_ sorting: ProfileGiftsContext.Sorting) { + guard self.sorting != sorting else { + return + } self.sorting = sorting self.filteredDataState = .ready(canLoadMore: true, nextOffset: nil) self.pushState() diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index fdf187168a..89a13d50e5 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -1148,7 +1148,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _, peerId, senderId, _): if case let .unique(gift) = gift { if !forAdditionalServiceMessage && !"".isEmpty { - attributedString = NSAttributedString(string: "\(gift.title) #\(gift.number)", font: titleFont, textColor: primaryTextColor) + attributedString = NSAttributedString(string: "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, dateTimeFormat.groupingSeparator))", font: titleFont, textColor: primaryTextColor) } else if let messagePeer = message.peers[message.id.peerId] { var peerName = EnginePeer(messagePeer).compactDisplayTitle var peerIds: [(Int, EnginePeer.Id?)] = [(0, messagePeer.id)] diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index df4b6cc9fa..ffa7a26e05 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -2968,7 +2968,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } else { let contentProperties = contentPropertiesAndLayouts[i].3 - if i == 0 && !headerSize.height.isZero { + if (i == 0 || (i == 1 && detachedContentNodesHeight > 0)) && !headerSize.height.isZero { if contentGroupId == nil { contentNodesHeight += properties.headerSpacing } @@ -2981,7 +2981,15 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if !contentContainerNodeFrames.isEmpty { overlapOffset = currentContainerGroupOverlap } - let containerFrame = CGRect(x: 0.0, y: headerSize.height + totalContentNodesHeight - contentNodesHeight - overlapOffset, width: maxContentWidth, height: contentNodesHeight) + var containerContentNodesOrigin = contentNodesHeight + var containerContentNodesHeight = contentNodesHeight + if detachedContentNodesHeight > 0 { + if contentContainerNodeFrames.isEmpty { + containerContentNodesHeight -= detachedContentNodesHeight - 4.0 + containerContentNodesOrigin -= detachedContentNodesHeight - 4.0 + } + } + let containerFrame = CGRect(x: 0.0, y: headerSize.height + totalContentNodesHeight - containerContentNodesOrigin - overlapOffset, width: maxContentWidth, height: containerContentNodesHeight) contentContainerNodeFrames.append((containerGroupId, containerFrame, currentItemSelection, currentContainerGroupOverlap)) if !overlapOffset.isZero { @@ -2997,7 +3005,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } var contentNodeOriginY = contentNodesHeight - if detachedContentNodesHeight > 0 { + if detachedContentNodesHeight > 0, contentContainerNodeFrames.isEmpty { contentNodeOriginY -= detachedContentNodesHeight - 4.0 } @@ -3025,7 +3033,15 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if !contentContainerNodeFrames.isEmpty { overlapOffset = currentContainerGroupOverlap } - contentContainerNodeFrames.append((containerGroupId, CGRect(x: 0.0, y: headerSize.height + totalContentNodesHeight - contentNodesHeight - overlapOffset, width: maxContentWidth, height: contentNodesHeight), currentItemSelection, currentContainerGroupOverlap)) + var containerContentNodesOrigin = contentNodesHeight + var containerContentNodesHeight = contentNodesHeight + if detachedContentNodesHeight > 0 { + if contentContainerNodeFrames.isEmpty { + containerContentNodesHeight -= detachedContentNodesHeight - 4.0 + containerContentNodesOrigin -= detachedContentNodesHeight - 4.0 + } + } + contentContainerNodeFrames.append((containerGroupId, CGRect(x: 0.0, y: headerSize.height + totalContentNodesHeight - containerContentNodesOrigin - overlapOffset, width: maxContentWidth, height: containerContentNodesHeight), currentItemSelection, currentContainerGroupOverlap)) if !overlapOffset.isZero { totalContentNodesHeight -= currentContainerGroupOverlap } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageCommentFooterContentNode/Sources/ChatMessageCommentFooterContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageCommentFooterContentNode/Sources/ChatMessageCommentFooterContentNode.swift index 5bd1a66e68..99d4e8c196 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageCommentFooterContentNode/Sources/ChatMessageCommentFooterContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageCommentFooterContentNode/Sources/ChatMessageCommentFooterContentNode.swift @@ -213,7 +213,7 @@ public final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContent } else { textLeftInset = 15.0 + imageSize * min(1.0, CGFloat(replyPeers.count)) + (imageSpacing) * max(0.0, min(2.0, CGFloat(replyPeers.count - 1))) } - let textRightInset: CGFloat = 36.0 + let textRightInset: CGFloat = 24.0 let textConstrainedSize = CGSize(width: min(maxTextWidth, constrainedSize.width - horizontalInset - textLeftInset - textRightInset), height: constrainedSize.height) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift index 5a38e4a5e2..bf9eb2c51a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift @@ -354,8 +354,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { let attributedString = attributedServiceMessageString(theme: item.presentationData.theme, strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, message: EngineMessage(item.message), accountPeerId: item.context.account.peerId) - let primaryTextColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper).primaryText - + var primaryTextColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper).primaryText + var months: Int32 = 3 var animationName: String = "" var animationFile: TelegramMediaFile? @@ -582,7 +582,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { } else { title = isStoryEntity ? uniqueGift.title : item.presentationData.strings.Notification_StarGift_Title(authorName).string } - text = isStoryEntity ? "**\(item.presentationData.strings.Notification_StarGift_Collectible) #\(uniqueGift.number)**" : "**\(uniqueGift.title) #\(uniqueGift.number)**" + text = isStoryEntity ? "**\(item.presentationData.strings.Notification_StarGift_Collectible) #\(presentationStringsFormattedNumber(uniqueGift.number, item.presentationData.dateTimeFormat.groupingSeparator))**" : "**\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, item.presentationData.dateTimeFormat.groupingSeparator))**" ribbonTitle = isStoryEntity ? "" : item.presentationData.strings.Notification_StarGift_Gift buttonTitle = isStoryEntity ? "" : item.presentationData.strings.Notification_StarGift_View modelTitle = item.presentationData.strings.Notification_StarGift_Model @@ -599,6 +599,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { uniqueSecondBackgroundColor = UIColor(rgb: UInt32(bitPattern: innerColor)) uniquePatternColor = UIColor(rgb: UInt32(bitPattern: patternColor)) backdropValue = name + primaryTextColor = UIColor(rgb: 0xffffff) subtitleColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.3) case let .pattern(name, file, _): symbolValue = name diff --git a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift index 3f3e47ca50..f3793bdce4 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift @@ -1441,14 +1441,31 @@ private final class ChatSendStarsScreenComponent: Component { } let sideInset: CGFloat = floor((availableSize.width - fillingSize) * 0.5) + 16.0 + let context = component.context let balanceSize = self.balanceOverlay.update( transition: .immediate, component: AnyComponent( StarsBalanceOverlayComponent( context: component.context, theme: environment.theme, - action: { + action: { [weak self] in + guard let self, let starsContext = context.starsContext, let navigationController = self.environment?.controller()?.navigationController as? NavigationController else { + return + } + self.environment?.controller()?.dismiss() + let _ = (context.engine.payments.starsTopUpOptions() + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { options in + let controller = context.sharedContext.makeStarsPurchaseScreen( + context: context, + starsContext: starsContext, + options: options, + purpose: .generic, + completion: { _ in } + ) + navigationController.pushViewController(controller) + }) } ) ), diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftTransferAlertController.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftTransferAlertController.swift index a9ff187f51..ffa0d3ffb8 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftTransferAlertController.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftTransferAlertController.swift @@ -259,10 +259,10 @@ public func giftTransferAlertController(context: AccountContext, gift: StarGift. let text: String let buttonText: String if transferStars > 0 { - text = strings.Gift_Transfer_Confirmation_Text("\(gift.title) #\(gift.number)", peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), strings.Gift_Transfer_Confirmation_Text_Stars(Int32(transferStars))).string + text = strings.Gift_Transfer_Confirmation_Text("\(gift.title) #\(presentationStringsFormattedNumber(gift.number, presentationData.dateTimeFormat.groupingSeparator))", peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), strings.Gift_Transfer_Confirmation_Text_Stars(Int32(transferStars))).string buttonText = "\(strings.Gift_Transfer_Confirmation_Transfer) $ \(transferStars)" } else { - text = strings.Gift_Transfer_Confirmation_TextFree("\(gift.title) #\(gift.number)", peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string + text = strings.Gift_Transfer_Confirmation_TextFree("\(gift.title) #\(presentationStringsFormattedNumber(gift.number, presentationData.dateTimeFormat.groupingSeparator))", peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string buttonText = strings.Gift_Transfer_Confirmation_TransferFree } diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 0beadb7f97..9d2c630241 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -635,7 +635,8 @@ private final class GiftViewSheetContent: CombinedComponent { if case .wearPreview = component.subject { giftTitle = uniqueGift.title } else { - giftTitle = "\(uniqueGift.title) #\(uniqueGift.number)" + + giftTitle = "\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))" } let wearTitle = wearTitle.update( @@ -1018,7 +1019,7 @@ private final class GiftViewSheetContent: CombinedComponent { var descriptionText: String if let uniqueGift { titleString = uniqueGift.title - descriptionText = "\(strings.Gift_Unique_Collectible) #\(uniqueGift.number)" + descriptionText = "\(strings.Gift_Unique_Collectible) #\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))" } else if soldOut { descriptionText = strings.Gift_View_UnavailableDescription } else if upgraded { @@ -1480,7 +1481,7 @@ private final class GiftViewSheetContent: CombinedComponent { if isWearing { state.commitTakeOff() - component.showAttributeInfo(statusTag, strings.Gift_View_TookOff("\(uniqueGift.title) #\(uniqueGift.number)").string) + component.showAttributeInfo(statusTag, strings.Gift_View_TookOff("\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))").string) } else { if let controller = controller() as? GiftViewScreen { controller.dismissAllTooltips() @@ -1507,7 +1508,7 @@ private final class GiftViewSheetContent: CombinedComponent { state.requestWearPreview() } else { state.commitWear(uniqueGift) - component.showAttributeInfo(statusTag, strings.Gift_View_PutOn("\(uniqueGift.title) #\(uniqueGift.number)").string) + component.showAttributeInfo(statusTag, strings.Gift_View_PutOn("\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))").string) } }) } @@ -2090,7 +2091,7 @@ private final class GiftViewSheetContent: CombinedComponent { component.cancel(true) } else { Queue.mainQueue().after(0.2) { - component.showAttributeInfo(statusTag, strings.Gift_View_PutOn("\(uniqueGift.title) #\(uniqueGift.number)").string) + component.showAttributeInfo(statusTag, strings.Gift_View_PutOn("\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))").string) } } } @@ -3091,15 +3092,32 @@ public class GiftViewScreen: ViewControllerComponentContainer { super.containerLayoutUpdated(layout, transition: transition) if self.showBalance { + let context = self.context let insets = layout.insets(options: .statusBar) let balanceSize = self.balanceOverlay.update( transition: .immediate, component: AnyComponent( StarsBalanceOverlayComponent( - context: self.context, - theme: self.context.sharedContext.currentPresentationData.with { $0 }.theme, - action: { + context: context, + theme: context.sharedContext.currentPresentationData.with { $0 }.theme, + action: { [weak self] in + guard let self, let starsContext = context.starsContext, let navigationController = self.navigationController as? NavigationController else { + return + } + self.dismissAnimated() + let _ = (context.engine.payments.starsTopUpOptions() + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { options in + let controller = context.sharedContext.makeStarsPurchaseScreen( + context: context, + starsContext: starsContext, + options: options, + purpose: .generic, + completion: { _ in } + ) + navigationController.pushViewController(controller) + }) } ) ), diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift index 6048815011..6fb6a9d67f 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift @@ -274,7 +274,7 @@ public func giftWithdrawAlertController(context: AccountContext, gift: StarGift. let strings = presentationData.strings let title = strings.Gift_Withdraw_Title - let text = strings.Gift_Withdraw_Text("\(gift.title) #\(gift.number)").string + let text = strings.Gift_Withdraw_Text("\(gift.title) #\(presentationStringsFormattedNumber(gift.number, presentationData.dateTimeFormat.groupingSeparator))").string let buttonText = strings.Gift_Withdraw_Proceed var dismissImpl: ((Bool) -> Void)? diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift index 290e126b5e..01a4e252f0 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift @@ -681,7 +681,7 @@ public final class MessageInputPanelComponent: Component { if self.contextQueryPeer == nil, let peerId = component.chatLocation?.peerId { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in - guard let self, peer?.addressName != nil else { + guard let self, let peer, case .channel = peer, peer.addressName != nil else { return } self.contextQueryPeer = peer diff --git a/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift b/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift index 598a80f7e0..eafde47eeb 100644 --- a/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift @@ -25,10 +25,10 @@ public final class MessagePriceItem: ListViewItem, ItemListItem { let value: Int64 let price: String public let sectionId: ItemListSectionId - let updated: (Int64) -> Void + let updated: (Int64, Bool) -> Void let openPremiumInfo: (() -> Void)? - public init(theme: PresentationTheme, strings: PresentationStrings, isEnabled: Bool, minValue: Int64, maxValue: Int64, value: Int64, price: String, sectionId: ItemListSectionId, updated: @escaping (Int64) -> Void, openPremiumInfo: (() -> Void)? = nil) { + public init(theme: PresentationTheme, strings: PresentationStrings, isEnabled: Bool, minValue: Int64, maxValue: Int64, value: Int64, price: String, sectionId: ItemListSectionId, updated: @escaping (Int64, Bool) -> Void, openPremiumInfo: (() -> Void)? = nil) { self.theme = theme self.strings = strings self.isEnabled = isEnabled @@ -259,7 +259,7 @@ private class MessagePriceItemNode: ListViewItemNode { if let strongSelf = self { strongSelf.item = item strongSelf.layoutParams = params - + strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor @@ -342,6 +342,13 @@ private class MessagePriceItemNode: ListViewItemNode { } sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + 18.0, y: 36.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 18.0 * 2.0, height: 44.0)) + + sliderView.interactionEnded = { [weak self] in + guard let self else { + return + } + self.item?.updated(Int64(self.amount.realValue), true) + } } strongSelf.lockIconNode.isHidden = item.isEnabled @@ -436,7 +443,7 @@ private class MessagePriceItemNode: ListViewItemNode { } self.amount = updatedAmount - self.item?.updated(Int64(self.amount.realValue)) + self.item?.updated(Int64(self.amount.realValue), false) } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoGiftsCoverComponent.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoGiftsCoverComponent.swift index 7bd70ad117..6c81f40c57 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoGiftsCoverComponent.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoGiftsCoverComponent.swift @@ -579,7 +579,7 @@ private class GiftIconLayer: SimpleLayer { } override func layoutSublayers() { - self.shadowLayer.frame = CGRect(origin: .zero, size: self.bounds.size).insetBy(dx: -4.0, dy: -4.0) + self.shadowLayer.frame = CGRect(origin: .zero, size: self.bounds.size).insetBy(dx: -8.0, dy: -8.0) self.animationLayer.frame = CGRect(origin: .zero, size: self.bounds.size) } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index d019cf9ef8..1c0c8c45df 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -255,6 +255,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.buttonsContainerNode.clipsToBounds = true self.buttonsBackgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: true, enableSaturation: false) + self.buttonsBackgroundNode.isUserInteractionEnabled = false self.buttonsContainerNode.addSubnode(self.buttonsBackgroundNode) self.buttonsMaskView = UIView() self.buttonsBackgroundNode.view.mask = self.buttonsMaskView diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 4cd1b7c9e5..c70cee98c0 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -3728,7 +3728,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }, openAgeRestrictedMessageMedia: { _, _ in }, playMessageEffect: { _ in }, editMessageFactCheck: { _ in - }, sendGift: { _ in + }, sendGift: { [weak self] _ in + guard let self else { + return + } + self.openPremiumGift() }, openUniqueGift: { _ in }, openMessageFeeException: { }, requestMessageUpdate: { _, _ in diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftContextPreviewController.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftContextPreviewController.swift index f662997902..7c1e42f704 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftContextPreviewController.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftContextPreviewController.swift @@ -120,7 +120,7 @@ private final class GiftContextPreviewComponent: Component { let subtitleSize = self.subtitle.update( transition: .immediate, component: AnyComponent(MultilineTextComponent(text: .plain( - NSAttributedString(string: "\(environment.strings.Gift_Unique_Collectible) #\(uniqueGift.number)", font: Font.regular(13.0), textColor: vibrantColor) + NSAttributedString(string: "\(environment.strings.Gift_Unique_Collectible) #\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))", font: Font.regular(13.0), textColor: vibrantColor) ))), environment: {}, containerSize: availableSize diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index 12ecd981dc..e924b19dd6 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -240,6 +240,9 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr } public func beginReordering() { + self.profileGifts.updateFilter(.All) + self.profileGifts.updateSorting(.date) + if let parentController = self.parentController as? PeerInfoScreen { parentController.togglePaneIsReordering(isReordering: true) } else { @@ -469,7 +472,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr return } if self.isReordering { - if !product.pinnedToTop, let reference = product.reference, let items = self.starsProducts { + if case .unique = product.gift, !product.pinnedToTop, let reference = product.reference, let items = self.starsProducts { if self.pinnedReferences.count >= self.maxPinnedCount { self.parentController?.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.PeerInfo_Gifts_ToastPinLimit_Text(Int32(self.maxPinnedCount)), timeout: nil, customUndoText: nil), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) return @@ -564,7 +567,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr itemFrame = itemFrame.size.centered(around: reorderingItem.position) isReordering = true } - if itemView.layer.animation(forKey: "position") != nil && !isReordering { + if self.isReordering, itemView.layer.animation(forKey: "position") != nil && !isReordering { } else { itemTransition.setFrame(view: itemView, frame: itemFrame) } @@ -612,8 +615,6 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr var bottomScrollInset: CGFloat = 0.0 var contentHeight = ceil(CGFloat(starsProducts.count) / 3.0) * (starsOptionSize.height + optionSpacing) - optionSpacing + topInset + 16.0 - let transition = ComponentTransition.immediate - let size = params.size let sideInset = params.sideInset let bottomInset = params.bottomInset @@ -681,8 +682,9 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr scrollOffset -= bottomPanelHeight } - transition.setFrame(view: panelButton.view, frame: CGRect(origin: CGPoint(x: buttonSideInset, y: size.height - bottomInset - buttonSize.height - scrollOffset), size: buttonSize)) - transition.setAlpha(view: panelButton.view, alpha: panelAlpha) + let panelTransition = ComponentTransition.immediate + panelTransition.setFrame(view: panelButton.view, frame: CGRect(origin: CGPoint(x: buttonSideInset, y: size.height - bottomInset - buttonSize.height - scrollOffset), size: buttonSize)) + panelTransition.setAlpha(view: panelButton.view, alpha: panelAlpha) let _ = panelButton.updateLayout(width: buttonSize.width, transition: .immediate) if self.canManage { @@ -753,16 +755,16 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr self.view.addSubview(panelCheckView) } panelCheckView.frame = CGRect(origin: CGPoint(x: floor((size.width - panelCheckSize.width) / 2.0), y: size.height - bottomInset - panelCheckSize.height - 11.0 - scrollOffset), size: panelCheckSize) - transition.setAlpha(view: panelCheckView, alpha: panelAlpha) + panelTransition.setAlpha(view: panelCheckView, alpha: panelAlpha) } panelButton.isHidden = true } - transition.setFrame(view: panelBackground.view, frame: CGRect(x: 0.0, y: size.height - bottomPanelHeight - scrollOffset, width: size.width, height: bottomPanelHeight)) - transition.setAlpha(view: panelBackground.view, alpha: panelAlpha) + panelTransition.setFrame(view: panelBackground.view, frame: CGRect(x: 0.0, y: size.height - bottomPanelHeight - scrollOffset, width: size.width, height: bottomPanelHeight)) + panelTransition.setAlpha(view: panelBackground.view, alpha: panelAlpha) panelBackground.update(size: CGSize(width: size.width, height: bottomPanelHeight), transition: transition.containedViewLayoutTransition) - transition.setFrame(view: panelSeparator.view, frame: CGRect(x: 0.0, y: size.height - bottomPanelHeight - scrollOffset, width: size.width, height: UIScreenPixel)) - transition.setAlpha(view: panelSeparator.view, alpha: panelAlpha) + panelTransition.setFrame(view: panelSeparator.view, frame: CGRect(x: 0.0, y: size.height - bottomPanelHeight - scrollOffset, width: size.width, height: UIScreenPixel)) + panelTransition.setAlpha(view: panelSeparator.view, alpha: panelAlpha) let fadeTransition = ComponentTransition.easeInOut(duration: 0.25) if self.resultsAreEmpty { @@ -776,8 +778,8 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr self.emptyResultsClippingView.isHidden = false - transition.setFrame(view: self.emptyResultsClippingView, frame: CGRect(origin: CGPoint(x: 0.0, y: 48.0), size: self.scrollNode.frame.size)) - transition.setBounds(view: self.emptyResultsClippingView, bounds: CGRect(origin: CGPoint(x: 0.0, y: 48.0), size: self.scrollNode.frame.size)) + panelTransition.setFrame(view: self.emptyResultsClippingView, frame: CGRect(origin: CGPoint(x: 0.0, y: 48.0), size: self.scrollNode.frame.size)) + panelTransition.setBounds(view: self.emptyResultsClippingView, bounds: CGRect(origin: CGPoint(x: 0.0, y: 48.0), size: self.scrollNode.frame.size)) let emptyResultsTitleSize = self.emptyResultsTitle.update( transition: .immediate, @@ -840,7 +842,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr view.playOnce() } view.bounds = CGRect(origin: .zero, size: emptyResultsAnimationFrame.size) - transition.setPosition(view: view, position: emptyResultsAnimationFrame.center) + panelTransition.setPosition(view: view, position: emptyResultsAnimationFrame.center) } if let view = self.emptyResultsTitle.view { if view.superview == nil { @@ -849,7 +851,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr self.emptyResultsClippingView.addSubview(view) } view.bounds = CGRect(origin: .zero, size: emptyResultsTitleFrame.size) - transition.setPosition(view: view, position: emptyResultsTitleFrame.center) + panelTransition.setPosition(view: view, position: emptyResultsTitleFrame.center) } if let view = self.emptyResultsAction.view { if view.superview == nil { @@ -858,7 +860,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr self.emptyResultsClippingView.addSubview(view) } view.bounds = CGRect(origin: .zero, size: emptyResultsActionFrame.size) - transition.setPosition(view: view, position: emptyResultsActionFrame.center) + panelTransition.setPosition(view: view, position: emptyResultsActionFrame.center) } } else { if let view = self.emptyResultsAnimation.view { @@ -879,7 +881,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr } } - if self.peerId == self.context.account.peerId { + if self.peerId == self.context.account.peerId, !self.resultsAreEmpty { let footerText: ComponentView if let current = self.footerText { footerText = current @@ -941,21 +943,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr self.chatControllerInteraction.navigationController()?.pushViewController(controller) }) } else { - let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Birthday(id: self.peerId)) - |> deliverOnMainQueue).start(next: { birthday in - var hasBirthday = false - if let birthday { - hasBirthday = hasBirthdayToday(birthday: birthday) - } - let controller = self.context.sharedContext.makeGiftOptionsController( - context: self.context, - peerId: self.peerId, - premiumOptions: [], - hasBirthday: hasBirthday, - completion: nil - ) - self.chatControllerInteraction.navigationController()?.pushViewController(controller) - }) + self.chatControllerInteraction.sendGift(self.peerId) } } diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift index 39de43b8ec..b4066cab07 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift @@ -424,7 +424,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { isPaidMessage = true titleText = strings.Stars_Transaction_PaidMessage(transaction.paidMessageCount ?? 1) countOnTop = true - descriptionText = strings.Stars_Transaction_PaidMessage_Text(formatPermille(starrefCommissionPermille)).string + descriptionText = strings.Stars_Transaction_PaidMessage_Text(formatPermille(1000 - starrefCommissionPermille)).string } else if transaction.starrefPeerId == nil { titleText = strings.StarsTransaction_TitleCommission(formatPermille(starrefCommissionPermille)).string countOnTop = false diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift index b5f9e00149..f9aacdbe72 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift @@ -312,7 +312,7 @@ final class StarsTransactionsListPanelComponent: Component { itemSubtitle = environment.strings.Stars_Intro_Transaction_PaidMessage(item.paidMessageCount ?? 1) } else if let starGift = item.starGift { if item.flags.contains(.isStarGiftUpgrade), case let .unique(gift) = starGift { - itemTitle = "\(gift.title) #\(gift.number)" + itemTitle = "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, environment.dateTimeFormat.groupingSeparator))" itemSubtitle = environment.strings.Stars_Intro_Transaction_GiftUpgrade uniqueGift = gift } else { diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 95e775a17a..753e8f3c60 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2905,7 +2905,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { Queue.mainQueue().after(0.3) { let tooltipController = UndoOverlayController( presentationData: presentationData, - content: .forward(savedMessages: false, text: presentationData.strings.Gift_Transfer_Success("\(gift.title) #\(gift.number)", peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string), + content: .forward(savedMessages: false, text: presentationData.strings.Gift_Transfer_Success("\(gift.title) #\(presentationStringsFormattedNumber(gift.number, presentationData.dateTimeFormat.groupingSeparator))", peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string), elevatedLayout: false, action: { _ in return true } ) diff --git a/submodules/WebUI/Sources/WebAppWebView.swift b/submodules/WebUI/Sources/WebAppWebView.swift index f1be891cc9..ad3ab346ab 100644 --- a/submodules/WebUI/Sources/WebAppWebView.swift +++ b/submodules/WebUI/Sources/WebAppWebView.swift @@ -65,10 +65,10 @@ function tgBrowserHandleMutations(mutations) { if (mutation.addedNodes && mutation.addedNodes.length > 0) { mutation.addedNodes.forEach((newNode) => { if (newNode.tagName === 'VIDEO') { - disableWebkitEnterFullscreen(newNode); + tgBrowserDisableWebkitEnterFullscreen(newNode); } if (newNode.querySelectorAll) { - newNode.querySelectorAll('video').forEach(disableWebkitEnterFullscreen); + newNode.querySelectorAll('video').forEach(tgBrowserDisableWebkitEnterFullscreen); } }); } From af654744b0e2ad41e8cc6a17c84604d7e2611140 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 7 Mar 2025 23:26:30 +0400 Subject: [PATCH 04/13] Various fixes --- .../Sources/TelegramEngine/Payments/StarGifts.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index 72c1af1dc3..a197b0609c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -1210,6 +1210,10 @@ private final class ProfileGiftsContextImpl { if let reference = gift.reference, existingGifts.contains(reference) { continue } + var gift = gift + if gift.reference == reference { + gift = gift.withPinnedToTop(pinnedToTop) + } updatedGifts.append(gift) } updatedGifts.sort { lhs, rhs in @@ -1238,6 +1242,10 @@ private final class ProfileGiftsContextImpl { if let reference = gift.reference, existingFilteredGifts.contains(reference) { continue } + var gift = gift + if gift.reference == reference { + gift = gift.withPinnedToTop(pinnedToTop) + } updatedFilteredGifts.append(gift) } updatedFilteredGifts.sort { lhs, rhs in From 368f6695e1c229e8b8864c35a72d6357aa84c2c4 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 8 Mar 2025 00:05:30 +0400 Subject: [PATCH 05/13] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 4 + .../ContactMultiselectionController.swift | 5 +- .../IncomingMessagePrivacyScreen.swift | 7 +- ...ectivePrivacySettingsPeersController.swift | 5 +- .../Sources/StarsStatisticsScreen.swift | 184 ++++++++++++++---- .../Sources/StarsTransactionsScreen.swift | 110 +++++------ .../Stars/Stats.imageset/Contents.json | 12 ++ .../Premium/Stars/Stats.imageset/stats_24.pdf | Bin 0 -> 3775 bytes .../ContactMultiselectionControllerNode.swift | 3 + 9 files changed, 224 insertions(+), 106 deletions(-) create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/stats_24.pdf diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 38c3ab5315..581f26d896 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -13949,6 +13949,7 @@ Sorry for the inconvenience."; "Stars.Intro.BuyShort" = "Top Up"; "Stars.Intro.Withdraw" = "Withdraw"; +"Stars.Intro.Stats" = "Stats"; "Group.Info.Settings" = "Group Settings"; @@ -14014,3 +14015,6 @@ Sorry for the inconvenience."; "Premium.MaxExpiringStoriesFinalTextNumberFormat_1" = "**%d** story"; "Premium.MaxExpiringStoriesFinalTextNumberFormat_any" = "**%d** stories"; "Premium.MaxExpiringStoriesFinalTextFormat" = "You have reached the limit of %@ stories per **24** hours."; + +"Stars.AccountRevenue.Proceeds.Info" = "Stars from your total balance can be withdrawn as rewards 21 days after they are earned."; +"Stars.AccountRevenue.Withdraw.Info" = "You can collect rewards for Stars using Fragment. You cannot withdraw less than 1000 stars. [Learn More >]()"; diff --git a/submodules/AccountContext/Sources/ContactMultiselectionController.swift b/submodules/AccountContext/Sources/ContactMultiselectionController.swift index f790e0ba78..fc3ea9691c 100644 --- a/submodules/AccountContext/Sources/ContactMultiselectionController.swift +++ b/submodules/AccountContext/Sources/ContactMultiselectionController.swift @@ -48,6 +48,7 @@ public enum ContactMultiselectionControllerMode { public var onlyUsers: Bool public var disableChannels: Bool public var disableBots: Bool + public var disableContacts: Bool public init( title: String, @@ -59,7 +60,8 @@ public enum ContactMultiselectionControllerMode { displayPresence: Bool = false, onlyUsers: Bool = false, disableChannels: Bool = false, - disableBots: Bool = false + disableBots: Bool = false, + disableContacts: Bool = false ) { self.title = title self.searchPlaceholder = searchPlaceholder @@ -71,6 +73,7 @@ public enum ContactMultiselectionControllerMode { self.onlyUsers = onlyUsers self.disableChannels = disableChannels self.disableBots = disableBots + self.disableContacts = disableContacts } } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift b/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift index 090e6672ed..4f5ddfc578 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift @@ -281,8 +281,9 @@ public func incomingMessagePrivacyScreen(context: AccountContext, value: GlobalP chatListFilters: nil, onlyUsers: false, disableChannels: true, - disableBots: false - )), filters: [.excludeSelf])) + disableBots: true, + disableContacts: true + )))) addPeerDisposable.set((controller.result |> take(1) |> deliverOnMainQueue).start(next: { [weak controller] result in @@ -342,7 +343,7 @@ public func incomingMessagePrivacyScreen(context: AccountContext, value: GlobalP controller.navigationPresentation = .modal pushControllerImpl?(controller) } else { - let controller = selectivePrivacyPeersController(context: context, title: presentationData.strings.Privacy_Messages_Exceptions_Title, footer: presentationData.strings.Privacy_Messages_RemoveFeeInfo, initialPeers: peerIds, initialEnableForPremium: false, displayPremiumCategory: false, initialEnableForBots: false, displayBotsCategory: false, updated: { updatedPeerIds, _, _ in + let controller = selectivePrivacyPeersController(context: context, title: presentationData.strings.Privacy_Messages_Exceptions_Title, footer: presentationData.strings.Privacy_Messages_RemoveFeeInfo, hideContacts: true, initialPeers: peerIds, initialEnableForPremium: false, displayPremiumCategory: false, initialEnableForBots: false, displayBotsCategory: false, updated: { updatedPeerIds, _, _ in updateState { state in var updatedState = state updatedState.disableFor = updatedPeerIds diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift index 9d79acf509..e18a0ac730 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift @@ -322,7 +322,7 @@ private func selectivePrivacyPeersControllerEntries(presentationData: Presentati return entries } -public func selectivePrivacyPeersController(context: AccountContext, title: String, footer: String? = nil, initialPeers: [EnginePeer.Id: SelectivePrivacyPeer], initialEnableForPremium: Bool, displayPremiumCategory: Bool, initialEnableForBots: Bool, displayBotsCategory: Bool, updated: @escaping ([EnginePeer.Id: SelectivePrivacyPeer], Bool, Bool) -> Void) -> ViewController { +public func selectivePrivacyPeersController(context: AccountContext, title: String, footer: String? = nil, hideContacts: Bool = false, initialPeers: [EnginePeer.Id: SelectivePrivacyPeer], initialEnableForPremium: Bool, displayPremiumCategory: Bool, initialEnableForBots: Bool, displayBotsCategory: Bool, updated: @escaping ([EnginePeer.Id: SelectivePrivacyPeer], Bool, Bool) -> Void) -> ViewController { let initialState = SelectivePrivacyPeersControllerState(enableForPremium: initialEnableForPremium, enableForBots: initialEnableForBots, editing: false, peerIdWithRevealedOptions: nil) let statePromise = ValuePromise(initialState, ignoreRepeated: true) let stateValue = Atomic(value: initialState) @@ -428,7 +428,8 @@ public func selectivePrivacyPeersController(context: AccountContext, title: Stri chatListFilters: nil, onlyUsers: false, disableChannels: true, - disableBots: false + disableBots: hideContacts, + disableContacts: hideContacts )), alwaysEnabled: true)) addPeerDisposable.set((controller.result |> take(1) diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift index ea9e2ce6c5..626f027cb0 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift @@ -29,6 +29,7 @@ final class StarsStatisticsScreenComponent: Component { let peerId: EnginePeer.Id let revenueContext: StarsRevenueStatsContext let openTransaction: (StarsContext.State.Transaction) -> Void + let buy: () -> Void let withdraw: () -> Void let showTimeoutTooltip: (Int32) -> Void let buyAds: () -> Void @@ -38,6 +39,7 @@ final class StarsStatisticsScreenComponent: Component { peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext, openTransaction: @escaping (StarsContext.State.Transaction) -> Void, + buy: @escaping () -> Void, withdraw: @escaping () -> Void, showTimeoutTooltip: @escaping (Int32) -> Void, buyAds: @escaping () -> Void @@ -46,6 +48,7 @@ final class StarsStatisticsScreenComponent: Component { self.peerId = peerId self.revenueContext = revenueContext self.openTransaction = openTransaction + self.buy = buy self.withdraw = withdraw self.showTimeoutTooltip = showTimeoutTooltip self.buyAds = buyAds @@ -508,7 +511,7 @@ final class StarsStatisticsScreenComponent: Component { )), footer: AnyComponent(MultilineTextComponent( text: .plain(NSAttributedString( - string: strings.Stars_BotRevenue_Proceeds_Info, + string: component.peerId == component.context.account.peerId ? strings.Stars_AccountRevenue_Proceeds_Info : strings.Stars_BotRevenue_Proceeds_Info, font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor )), @@ -558,8 +561,8 @@ final class StarsStatisticsScreenComponent: Component { return (TelegramTextAttributes.URL, contents) }) - let balanceInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(strings.Stars_BotRevenue_Withdraw_Info, attributes: termsMarkdownAttributes, textAlignment: .natural - )) + let balanceRawString = component.peerId == component.context.account.peerId ? strings.Stars_AccountRevenue_Withdraw_Info : strings.Stars_BotRevenue_Withdraw_Info + let balanceInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(balanceRawString, attributes: termsMarkdownAttributes, textAlignment: .natural)) if self.cachedChevronImage == nil || self.cachedChevronImage?.1 !== environment.theme { self.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Contact List/SubtitleArrow"), color: environment.theme.list.itemAccentColor)!, environment.theme) } @@ -567,6 +570,96 @@ final class StarsStatisticsScreenComponent: Component { balanceInfoString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: balanceInfoString.string)) } + var balanceItems: [AnyComponentWithIdentity] = [] + if component.peerId == component.context.account.peerId { + let withdrawEnabled = self.starsState?.balances.withdrawEnabled ?? false + balanceItems = [ + AnyComponentWithIdentity(id: 0, component: AnyComponent( + StarsBalanceComponent( + theme: environment.theme, + strings: strings, + dateTimeFormat: environment.dateTimeFormat, + count: self.starsState?.balances.availableBalance ?? StarsAmount.zero, + rate: self.starsState?.usdRate ?? 0, + actionTitle: strings.Stars_Intro_BuyShort, + actionAvailable: true, + actionIsEnabled: true, + actionIcon: PresentationResourcesItemList.itemListRoundTopupIcon(environment.theme), + action: { [weak self] in + guard let self, let component = self.component else { + return + } + component.buy() + }, + secondaryActionTitle: withdrawEnabled ? strings.Stars_Intro_Withdraw : nil, + secondaryActionIcon: PresentationResourcesItemList.itemListRoundWithdrawIcon(environment.theme), + secondaryActionCooldownUntilTimestamp: self.starsState?.balances.nextWithdrawalTimestamp, + secondaryAction: { [weak self] in + guard let self, let component = self.component else { + return + } + var remainingCooldownSeconds: Int32 = 0 + if let cooldownUntilTimestamp = self.starsState?.balances.nextWithdrawalTimestamp { + remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) + remainingCooldownSeconds = max(0, remainingCooldownSeconds) + + if remainingCooldownSeconds > 0 { + component.showTimeoutTooltip(cooldownUntilTimestamp) + } else { + component.withdraw() + } + } else { + component.withdraw() + } + } + ) + )) + ] + } else { + balanceItems = [ + AnyComponentWithIdentity(id: 0, component: AnyComponent( + StarsBalanceComponent( + theme: environment.theme, + strings: strings, + dateTimeFormat: environment.dateTimeFormat, + count: self.starsState?.balances.availableBalance ?? StarsAmount.zero, + rate: self.starsState?.usdRate ?? 0, + actionTitle: strings.Stars_BotRevenue_Withdraw_WithdrawShort, + actionAvailable: true, + actionIsEnabled: self.starsState?.balances.withdrawEnabled ?? true, + actionCooldownUntilTimestamp: self.starsState?.balances.nextWithdrawalTimestamp, + actionIcon: PresentationResourcesItemList.itemListRoundTopupIcon(environment.theme), + action: { [weak self] in + guard let self, let component = self.component else { + return + } + var remainingCooldownSeconds: Int32 = 0 + if let cooldownUntilTimestamp = self.starsState?.balances.nextWithdrawalTimestamp { + remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) + remainingCooldownSeconds = max(0, remainingCooldownSeconds) + + if remainingCooldownSeconds > 0 { + component.showTimeoutTooltip(cooldownUntilTimestamp) + } else { + component.withdraw() + } + } else { + component.withdraw() + } + }, + secondaryActionTitle: strings.Stars_BotRevenue_Withdraw_BuyAds, + secondaryActionIcon: PresentationResourcesItemList.itemListRoundWithdrawIcon(environment.theme), + secondaryAction: { [weak self] in + guard let self, let component = self.component else { + return + } + component.buyAds() + } + ) + )) + ] + } + let balanceSize = self.balanceView.update( transition: .immediate, component: AnyComponent(ListSectionComponent( @@ -597,44 +690,7 @@ final class StarsStatisticsScreenComponent: Component { } } )), - items: [AnyComponentWithIdentity(id: 0, component: AnyComponent( - StarsBalanceComponent( - theme: environment.theme, - strings: strings, - dateTimeFormat: environment.dateTimeFormat, - count: self.starsState?.balances.availableBalance ?? StarsAmount.zero, - rate: self.starsState?.usdRate ?? 0, - actionTitle: strings.Stars_BotRevenue_Withdraw_WithdrawShort, - actionAvailable: true, - actionIsEnabled: self.starsState?.balances.withdrawEnabled ?? true, - actionCooldownUntilTimestamp: self.starsState?.balances.nextWithdrawalTimestamp, - action: { [weak self] in - guard let self, let component = self.component else { - return - } - var remainingCooldownSeconds: Int32 = 0 - if let cooldownUntilTimestamp = self.starsState?.balances.nextWithdrawalTimestamp { - remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) - remainingCooldownSeconds = max(0, remainingCooldownSeconds) - - if remainingCooldownSeconds > 0 { - component.showTimeoutTooltip(cooldownUntilTimestamp) - } else { - component.withdraw() - } - } else { - component.withdraw() - } - }, - secondaryActionTitle: strings.Stars_BotRevenue_Withdraw_BuyAds, - secondaryAction: { [weak self] in - guard let self, let component = self.component else { - return - } - component.buyAds() - } - ) - ))] + items: balanceItems )), environment: {}, containerSize: CGSize(width: availableSize.width - sideInsets, height: availableSize.height) @@ -799,11 +855,14 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { private weak var tooltipScreen: UndoOverlayController? private var timer: Foundation.Timer? + private let options = Promise<[StarsTopUpOption]>() + public init(context: AccountContext, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext) { self.context = context self.peerId = peerId self.revenueContext = revenueContext + var buyImpl: (() -> Void)? var withdrawImpl: (() -> Void)? var buyAdsImpl: (() -> Void)? var showTimeoutTooltipImpl: ((Int32) -> Void)? @@ -815,6 +874,9 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { openTransaction: { transaction in openTransactionImpl?(transaction) }, + buy: { + buyImpl?() + }, withdraw: { withdrawImpl?() }, @@ -842,6 +904,46 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { }) } + if peerId == context.account.peerId { + self.options.set(.single([]) |> then(context.engine.payments.starsTopUpOptions())) + } + + buyImpl = { [weak self] in + guard let self else { + return + } + let _ = (self.options.get() + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] options in + guard let self, let starsContext = context.starsContext else { + return + } + let controller = context.sharedContext.makeStarsPurchaseScreen(context: context, starsContext: starsContext, options: options, purpose: .generic, completion: { [weak self] stars in + guard let self else { + return + } + starsContext.add(balance: StarsAmount(value: stars, nanos: 0)) + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let resultController = UndoOverlayController( + presentationData: presentationData, + content: .universal( + animation: "StarsBuy", + scale: 0.066, + colors: [:], + title: presentationData.strings.Stars_Intro_PurchasedTitle, + text: presentationData.strings.Stars_Intro_PurchasedText(presentationData.strings.Stars_Intro_PurchasedText_Stars(Int32(stars))).string, + customUndoText: nil, + timeout: nil + ), + elevatedLayout: false, + action: { _ in return true}) + self.present(resultController, in: .window(.root)) + }) + self.push(controller) + }) + } + withdrawImpl = { [weak self] in guard let self else { return diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift index 6894f31d53..7bfd83869e 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift @@ -630,7 +630,7 @@ final class StarsTransactionsScreenComponent: Component { contentHeight += descriptionSize.height contentHeight += 29.0 - let withdrawAvailable = self.revenueState?.balances.withdrawEnabled ?? false + let withdrawAvailable = (self.revenueState?.balances.overallRevenue.value ?? 0) > 0 let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 }) let balanceSize = self.balanceView.update( @@ -656,26 +656,14 @@ final class StarsTransactionsScreenComponent: Component { } component.buy() }, - secondaryActionTitle: withdrawAvailable ? environment.strings.Stars_Intro_Withdraw : nil, - secondaryActionIcon: withdrawAvailable ? PresentationResourcesItemList.itemListRoundWithdrawIcon(environment.theme) : nil, + secondaryActionTitle: withdrawAvailable ? environment.strings.Stars_Intro_Stats : nil, + secondaryActionIcon: withdrawAvailable ? PresentationResourcesItemList.itemListStatsIcon(environment.theme) : nil, secondaryActionCooldownUntilTimestamp: self.revenueState?.balances.nextWithdrawalTimestamp, secondaryAction: withdrawAvailable ? { [weak self] in guard let self, let component = self.component else { return } - var remainingCooldownSeconds: Int32 = 0 - if let cooldownUntilTimestamp = self.revenueState?.balances.nextWithdrawalTimestamp { - remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) - remainingCooldownSeconds = max(0, remainingCooldownSeconds) - - if remainingCooldownSeconds > 0 { - component.showTimeoutTooltip(cooldownUntilTimestamp) - } else { - component.withdraw() - } - } else { - component.withdraw() - } + component.withdraw() } : nil, additionalAction: (premiumConfiguration.starsGiftsPurchaseAvailable && !premiumConfiguration.isPremiumDisabled) ? AnyComponent( Button( @@ -739,7 +727,7 @@ final class StarsTransactionsScreenComponent: Component { return } let _ = (component.context.sharedContext.makeAffiliateProgramSetupScreenInitialData(context: component.context, peerId: component.context.account.peerId, mode: .connectedPrograms) - |> deliverOnMainQueue).startStandalone(next: { [weak self] initialData in + |> deliverOnMainQueue).startStandalone(next: { [weak self] initialData in guard let self, let component = self.component else { return } @@ -1236,48 +1224,52 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer { guard let self else { return } - let _ = (context.engine.peers.checkStarsRevenueWithdrawalAvailability() - |> deliverOnMainQueue).start(error: { [weak self] error in - guard let self else { - return - } - switch error { - case .serverProvided: - return - case .requestPassword: - let _ = (self.starsRevenueStatsContext.state - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] state in - guard let self else { - return - } - let controller = self.context.sharedContext.makeStarsWithdrawalScreen(context: context, completion: { [weak self] amount in - guard let self else { - return - } - let controller = confirmStarsRevenueWithdrawalController(context: context, peerId: context.account.peerId, amount: amount, present: { [weak self] c, a in - self?.present(c, in: .window(.root)) - }, completion: { [weak self] url in - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {}) - - Queue.mainQueue().after(2.0) { - self?.starsRevenueStatsContext.reload() - } - }) - self.present(controller, in: .window(.root)) - }) - self.push(controller) - }) - default: - let controller = starsRevenueWithdrawalController(context: context, peerId: context.account.peerId, amount: 0, initialError: error, present: { [weak self] c, a in - self?.present(c, in: .window(.root)) - }, completion: { _ in - - }) - self.present(controller, in: .window(.root)) - } - }) + + let controller = self.context.sharedContext.makeStarsStatisticsScreen(context: context, peerId: context.account.peerId, revenueContext: self.starsRevenueStatsContext) + self.push(controller) + +// let _ = (context.engine.peers.checkStarsRevenueWithdrawalAvailability() +// |> deliverOnMainQueue).start(error: { [weak self] error in +// guard let self else { +// return +// } +// switch error { +// case .serverProvided: +// return +// case .requestPassword: +// let _ = (self.starsRevenueStatsContext.state +// |> take(1) +// |> deliverOnMainQueue).start(next: { [weak self] state in +// guard let self else { +// return +// } +// let controller = self.context.sharedContext.makeStarsWithdrawalScreen(context: context, completion: { [weak self] amount in +// guard let self else { +// return +// } +// let controller = confirmStarsRevenueWithdrawalController(context: context, peerId: context.account.peerId, amount: amount, present: { [weak self] c, a in +// self?.present(c, in: .window(.root)) +// }, completion: { [weak self] url in +// let presentationData = context.sharedContext.currentPresentationData.with { $0 } +// context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {}) +// +// Queue.mainQueue().after(2.0) { +// self?.starsRevenueStatsContext.reload() +// } +// }) +// self.present(controller, in: .window(.root)) +// }) +// self.push(controller) +// }) +// default: +// let controller = starsRevenueWithdrawalController(context: context, peerId: context.account.peerId, amount: 0, initialError: error, present: { [weak self] c, a in +// self?.present(c, in: .window(.root)) +// }, completion: { _ in +// +// }) +// self.present(controller, in: .window(.root)) +// } +// }) } showTimeoutTooltipImpl = { [weak self] cooldownUntilTimestamp in diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/Contents.json new file mode 100644 index 0000000000..a243b957ee --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "stats_24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/stats_24.pdf b/submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/stats_24.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c054ff1aa737d2f9b26cca187bfdf095fa86a060 GIT binary patch literal 3775 zcmai1c{o&k`!_L`iKIk`4w49CFvC!?j4j((vuiLiV#YFKv7tKoHN!`M}wxK9jG>ruA{v@XlfMJnc#x`sWHIfFA&`TIXFV` zCt?K1(=!2s?-GqR(cxe#Gy0h)7VH!itRq0z-!aNEOBr{;x z_8(}s=Q0WB(n2xWsWEFE@giEe?B_C>UeRL zbKFE`H^=vVMDs@iYdi<6TX)dkTe(SF>@qbuyqyzPbF-}ZY8gBN{Cye?f()mSKU8=^ zx?8w~I2k%Ej)h*>vXh4CT4qb>aRGfP-jMv1a4$sXX0;y9De@%Nn;QHB#~1f)f+@0@ z>-dGU8@hrfh;%_~Cu>y+jWn|1!Coak3=4h_ORw+{;Gz0zCx?}a4jXgmAUbPkJd#IbpqnZ@_VKtP|b>&Ouh{MJ5&F!@{H+K9MmNCOBT0g`3EeV)CA$!uCMBOG95 zF##lRGvhRw6Zu>iVZ!Hn1Z?(zHIvZn+?;H}Y_*^<=0j?a(MWUfkop7Lf zeTUx)^2Awm@M8r3x;&!mA>x0!et!__c+96qYZ#sC=SoM6I4)fE?Et@4Qi92F2J8=j z1b~Kb2#`bCQ@)Im1X?&%A<#$fruCP^mjpL1Z~1PqtB0Rg^Ace?K#Hu36w%-)i1&(< z)y@!>JP;ov^9b!E;(fCE`iNSKL9lMHrbrS|FX8r^V<6n#vg^bwVyc41rmm$(cCz)H zTTNt3c5u#wUYqe{%>p#L#Ny2p*D`9dO?4VkmkhoEI;r;)oheIm2#Q4yobr{@nk?QU z&=b^CmF6h!C>d(`BW>&U9`z1%cyYVQ$Zd3imk~x6W0O?wR(ZHIqtL39WV)2~y5WFP zFS^$@w&dnrqQQLf;E6+8Lq#>Eb{`P#9AD-9u3s(YqmI;~Y(SsHZJ?fKt@TDNkp@WH zMvZSRHRj-Ar_xSKJxd8wWRYCyGA{h>o*UpTa5ImB)O*J-4t@pY&619A$2t=~wXZ4s z_D^l4^*pO=;>JY%YMaV7eZES4C4WdC66YP{QHIXR^f{7TXBauG$mXI}&aGXr_-y{? zSvFa1S-30~I232(Ugp+#QOrHn&Ckuy&8*J84m>6{##`$+kUB54>JrG;nyp$~R4`j2 zN-Z0lAIhY{2ki^8i^fa8>)t5sei8TlBz0jhdvN%rWBEh%hYma8ppGzH;(4$3Tiw&G z1xc8OnSEp)vbU&(XgyWAD6lBHi`!XomN@G^+kD*W;2pi>Ug4DSJAaks;;!JL=B4FF z`lbxd`}b7zA8}rElToNb9Cb?M6S{7oecJhv#$%^gtpu&O$0zkeC?_bbX{Xa1D?e5w zSGreRtxT=huJo-Kuh^-9)y<5Vk4)I3tmp2#trWi0s`7m5@vT1E^Yf_5r?jz8e=XE} zclvxDT0{6N-)kTs#=Dew*l93By-(wzLK z2%A1fyZfXUTEkJZrf)?2ikrI{g#1#Q<{P{x$|v)h78+M3E>7kLWsr^q<}8!m;ND0q zdn^a{`#5>J_yrcU1h+1?2DYZQoFkg_Qe!g8&_J(LYWYWtVavN!Mh+pD{g{mEr3pKCS54N<5V2hp>g&W`y`w&X3t&%&%IpUY`wI z4P4y3$Du4xk3T&6zPj4|V6B9|*|2Nt)5h%X`VBm?I<*0gTOc4Uk&lst2T_EJl%c-K zWR<4LfS`~(ySx(wx9F7wQ5C8RSS2Vxdd$yjcsSVM$Jae~uMA%4kDBOQIAgVgUL${8 zY?y4-TGNVNKflhp&c5h39#m*mO|oBl-&XuROaHytH!;F`)o5tti~7|b&}&dH2sfHs z^D<(SW&OyOT^?E$J!4I^Dl=)kySyztRq%tn7%hJYPEfzE$#7`CE7Lhe78N z=gjWBp~#`tt)8vwdzP*Bx7H}$Gv4#D(+4O0_WAjV!;N@`$X}B;W6h`~_?@}0W)~TY z3d&82tEUx|q>p^fTqmD^pI5e~IgWrW(Guv*lWP$#cbAUn8y-UQ_ouXQ%NV z;$8h6N9oI_r|vy>@OJnXY{F74`oMXuvuW@1TwHDn?<_RIzjB_xjoW)3zrMUUMFFK( zIV?5$lyw)q>$FI}`)N4mUT*WVmMI_d5A7Ah=4``Yg|0|IR2s4H>Z!- zF-m4{%Ng#S(qQb?hx9X(J1xjVF!-9yYT#N`r=mL{;bC3KI9yO+d&B-obLRTDqc2o* zHhQ*3RwTBbuVhyAH}qp24ccl$I5sxD5Lo{;6;7v)HCO7VO2JQ`_z>(Dn|RM8i0bh? z$39C)&B~GFqG}E**{SBLr)Q?_WE;nxW>us3GngL@juVDth4uy2Al0Yy!J+2@4mY|u zTbvr>0|K5*xLlvA5-PmAqA=1Eq79;`&v zXmDTH^#(13ufDp=BT?tPzGrPPCWdo++S3b1M>ESuN1;7ZXfgxw><@4}D^(PN#k>4u zgfxlHIw5ku>E|we`~}PZhHU|;o~EV-#uw`X(7CE9U{4RT>#XqGiq2g%2_6I=GcSxY zc9+U(!T@>{QyLAWGtpl@@IQS1C#glyL;T5QL9I!!1PvHFDkxus*9!R9f1wQ7aZMDZ z!F}}DqiwOPF{f+wSJ{O6*ZUNap7n@fi+=x_Q?|8d zhla2+V;-e>m2A{|_URvN;pM86lIzN&Q~v(7JLig)ErOFRT=y2}9E)qn?2q|=zBGf! z#b9qTE(8^$e^VxPHOwHW@9v4*JKl8?4r1CRPcn^H_`$1YZ-g`)@N3)cT?tkcfpF?U zn`@s7E6Oz#oy@erh&&3h`xZW{m+%b{E`=K=|E#H{MsrEu`W1_27v_F0Em2z zsRHy0==bkeL_eA@>|go&(vbg%g#05Cy+GgJAEYPtcj1~CBF2Mo;ZIkR57reT3&3O{ zf7efc0th%<9uCm(p9dxj>`DOsCnSeJ(h{Yge;`Gg31&ElKma+E#0Y_|i Date: Sat, 8 Mar 2025 00:16:23 +0400 Subject: [PATCH 06/13] Various fixes --- .../StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift | 2 -- .../Sources/StarsTransactionsScreen.swift | 1 - 2 files changed, 3 deletions(-) diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift index 626f027cb0..f7fcdc9a88 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift @@ -628,7 +628,6 @@ final class StarsStatisticsScreenComponent: Component { actionAvailable: true, actionIsEnabled: self.starsState?.balances.withdrawEnabled ?? true, actionCooldownUntilTimestamp: self.starsState?.balances.nextWithdrawalTimestamp, - actionIcon: PresentationResourcesItemList.itemListRoundTopupIcon(environment.theme), action: { [weak self] in guard let self, let component = self.component else { return @@ -648,7 +647,6 @@ final class StarsStatisticsScreenComponent: Component { } }, secondaryActionTitle: strings.Stars_BotRevenue_Withdraw_BuyAds, - secondaryActionIcon: PresentationResourcesItemList.itemListRoundWithdrawIcon(environment.theme), secondaryAction: { [weak self] in guard let self, let component = self.component else { return diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift index 7bfd83869e..d3656812ce 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift @@ -658,7 +658,6 @@ final class StarsTransactionsScreenComponent: Component { }, secondaryActionTitle: withdrawAvailable ? environment.strings.Stars_Intro_Stats : nil, secondaryActionIcon: withdrawAvailable ? PresentationResourcesItemList.itemListStatsIcon(environment.theme) : nil, - secondaryActionCooldownUntilTimestamp: self.revenueState?.balances.nextWithdrawalTimestamp, secondaryAction: withdrawAvailable ? { [weak self] in guard let self, let component = self.component else { return From fd46692b9580b3d50f608d2f33d32b944e9b32e6 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 8 Mar 2025 00:18:08 +0400 Subject: [PATCH 07/13] Fix build --- .../Sources/Resources/PresentationResourceKey.swift | 1 + .../Sources/Resources/PresentationResourcesItemList.swift | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index 2e719e4694..60233f2ca0 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -79,6 +79,7 @@ public enum PresentationResourceKey: Int32 { case itemListPremiumIcon case itemListRoundTopupIcon case itemListRoundWithdrawIcon + case itemListStatsIcon case statsReactionsIcon case statsForwardsIcon diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift index 73f170cecf..677ba5637c 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift @@ -465,4 +465,10 @@ public struct PresentationResourcesItemList { }) }) } + + public static func itemListStatsIcon(_ theme: PresentationTheme) -> UIImage? { + return theme.image(PresentationResourceKey.itemListStatsIcon.rawValue, { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Premium/Stars/Stats"), color: .white)?.withRenderingMode(.alwaysTemplate) + }) + } } From 79f621390eeea2e1f15792627bd4d499d20292a9 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 8 Mar 2025 00:47:41 +0400 Subject: [PATCH 08/13] Fix paid message resend --- .../PendingMessages/EnqueueMessage.swift | 22 ++++++++++++++++++- .../Sources/ChatUserInfoItem.swift | 6 ++--- .../Chat/ChatControllerPaidMessage.swift | 6 +++-- .../TelegramUI/Sources/ChatController.swift | 22 ++++++++++++++----- 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift index 8c66c43bae..8758b43e1c 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift @@ -404,6 +404,19 @@ public func resendMessages(account: Account, messageIds: [MessageId]) -> Signal< return account.postbox.transaction { transaction -> Void in var removeMessageIds: [MessageId] = [] for (peerId, ids) in messagesIdsGroupedByPeerId(messageIds) { + var sendPaidMessageStars: StarsAmount? + let peer = transaction.getPeer(peerId) + if let user = peer as? TelegramUser, user.flags.contains(.requireStars) { + if let cachedUserData = transaction.getPeerCachedData(peerId: user.id) as? CachedUserData { + sendPaidMessageStars = cachedUserData.sendPaidMessageStars + } + } else if let channel = peer as? TelegramChannel { + if channel.flags.contains(.isCreator) || channel.adminRights != nil { + } else { + sendPaidMessageStars = channel.sendPaidMessageStars + } + } + var messages: [EnqueueMessage] = [] for id in ids { if let message = transaction.getMessage(id), !message.flags.contains(.Incoming) { @@ -425,9 +438,16 @@ public func resendMessages(account: Account, messageIds: [MessageId]) -> Signal< } else if let attribute = attribute as? ForwardSourceInfoAttribute { forwardSource = attribute.messageId } else { - filteredAttributes.append(attribute) + if attribute is PaidStarsMessageAttribute { + } else { + filteredAttributes.append(attribute) + } } } + + if let sendPaidMessageStars { + filteredAttributes.append(PaidStarsMessageAttribute(stars: sendPaidMessageStars, postponeSending: false)) + } if let forwardSource = forwardSource { messages.append(.forward(source: forwardSource, threadId: nil, grouping: .auto, attributes: filteredAttributes, correlationId: nil)) diff --git a/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift b/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift index b20939179e..9bf11f830f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift @@ -311,10 +311,10 @@ public final class ChatUserInfoItemNode: ListViewItemNode, ASGestureRecognizerDe } else { var countryName = "" let countriesConfiguration = item.context.currentCountriesConfiguration.with { $0 } - if let country = countriesConfiguration.countries.first(where: { $0.id == phoneCountry }) { - countryName = country.localizedName ?? country.name - } else if phoneCountry == "FT" { + if phoneCountry == "FT" { countryName = item.presentationData.strings.Chat_NonContactUser_AnonymousNumber + } else if let country = countriesConfiguration.countries.first(where: { $0.id == phoneCountry }) { + countryName = country.localizedName ?? country.name } else if phoneCountry == "TS" { countryName = "Test" } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerPaidMessage.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerPaidMessage.swift index ae7516293a..cae3d5b3d9 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerPaidMessage.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerPaidMessage.swift @@ -15,7 +15,7 @@ import TelegramPresentationData import TelegramNotices extension ChatControllerImpl { - func presentPaidMessageAlertIfNeeded(count: Int32 = 1, forceDark: Bool = false, completion: @escaping (Bool) -> Void) { + func presentPaidMessageAlertIfNeeded(count: Int32 = 1, forceDark: Bool = false, alwaysAsk: Bool = false, completion: @escaping (Bool) -> Void) { guard let peer = self.presentationInterfaceState.renderedPeer?.peer.flatMap(EnginePeer.init) else { completion(false) return @@ -24,11 +24,12 @@ extension ChatControllerImpl { let totalAmount = sendPaidMessageStars.value * Int64(count) let _ = (ApplicationSpecificNotice.dismissedPaidMessageWarningNamespace(accountManager: self.context.sharedContext.accountManager, peerId: peer.id) + |> take(1) |> deliverOnMainQueue).start(next: { [weak self] dismissedAmount in guard let self, let starsContext = self.context.starsContext else { return } - if let dismissedAmount, dismissedAmount == sendPaidMessageStars.value, let currentState = starsContext.currentState, currentState.balance.value > totalAmount { + if !alwaysAsk, let dismissedAmount, dismissedAmount == sendPaidMessageStars.value, let currentState = starsContext.currentState, currentState.balance.value > totalAmount { if count < 3 && totalAmount < 100 { completion(false) } else { @@ -52,6 +53,7 @@ extension ChatControllerImpl { count: count, amount: sendPaidMessageStars, totalAmount: nil, + hasCheck: !alwaysAsk, navigationController: self.navigationController as? NavigationController, completion: { [weak self] dontAskAgain in guard let self else { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index fda440a9a1..f61918c4e7 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3140,19 +3140,29 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G actions.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_MessageDialogRetry, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] _, f in - if let strongSelf = self { - let _ = resendMessages(account: strongSelf.context.account, messageIds: selectedGroup.map({ $0.id })).startStandalone() + if let self { + self.presentPaidMessageAlertIfNeeded(count: Int32(selectedGroup.count), alwaysAsk: true, completion: { [weak self] _ in + guard let self else { + return + } + let _ = resendMessages(account: self.context.account, messageIds: selectedGroup.map({ $0.id })).startStandalone() + }) + f(self.presentationInterfaceState.sendPaidMessageStars == nil ? .dismissWithoutContent : .default) } - f(.dismissWithoutContent) }))) if totalGroupCount != 1 { actions.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_MessageDialogRetryAll(totalGroupCount).string, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] _, f in - if let strongSelf = self { - let _ = resendMessages(account: strongSelf.context.account, messageIds: messages.map({ $0.id })).startStandalone() + if let self { + self.presentPaidMessageAlertIfNeeded(count: Int32(messages.count), alwaysAsk: true, completion: { [weak self] _ in + guard let self else { + return + } + let _ = resendMessages(account: self.context.account, messageIds: messages.map({ $0.id })).startStandalone() + }) + f(self.presentationInterfaceState.sendPaidMessageStars == nil ? .dismissWithoutContent : .default) } - f(.dismissWithoutContent) }))) } actions.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in From 8dd98a01f9ad5c8295db5b471ca095d3fb7f1b4c Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 7 Mar 2025 21:53:00 +0100 Subject: [PATCH 09/13] Fix return to stream --- .../Sources/SharedAccountContext.swift | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 753e8f3c60..e8b79259a2 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -971,6 +971,11 @@ public final class SharedAccountContextImpl: SharedAccountContext { if groupCallController.view.superview == nil { (mainWindow.viewController as? NavigationController)?.pushViewController(groupCallController) } + } else if let streamController = self.streamController { + mainWindow.hostView.containerView.endEditing(true) + if streamController.view.superview == nil { + (mainWindow.viewController as? NavigationController)?.pushViewController(streamController) + } } } } else { @@ -1357,8 +1362,23 @@ public final class SharedAccountContextImpl: SharedAccountContext { let streamController = MediaStreamComponentController(call: group) streamController.navigationPresentation = .flatModal streamController.parentNavigationController = navigationController + + let thisCallIsOnScreenPromise = ValuePromise(false, ignoreRepeated: true) + streamController.onViewDidAppear = { + thisCallIsOnScreenPromise.set(true) + } + streamController.onViewDidDisappear = { + thisCallIsOnScreenPromise.set(false) + } + self.streamController = streamController + self.mainWindow?.hostView.containerView.endEditing(true) + + thisCallIsOnScreenPromise.set(true) + self.hasGroupCallOnScreenPromise.set(thisCallIsOnScreenPromise.get()) + beginDisplayingCallStatusBar.set(.single(Void())) + navigationController.pushViewController(streamController) } } @@ -1786,6 +1806,11 @@ public final class SharedAccountContextImpl: SharedAccountContext { mainWindow.hostView.containerView.endEditing(true) (mainWindow.viewController as? NavigationController)?.pushViewController(groupCallController) } + } else if let streamController = self.streamController { + if streamController.isNodeLoaded && streamController.view.superview == nil { + mainWindow.hostView.containerView.endEditing(true) + (mainWindow.viewController as? NavigationController)?.pushViewController(streamController) + } } } From ff476cb0d7f704049ac6b70e4aec718abbfd5aa3 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 8 Mar 2025 00:59:01 +0400 Subject: [PATCH 10/13] Fix --- .../Gifts/GiftViewScreen/Sources/GiftViewScreen.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 9d2c630241..1ca31db8c7 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -2638,7 +2638,7 @@ public class GiftViewScreen: ViewControllerComponentContainer { giftsPeerId = peerId text = added ? presentationData.strings.Gift_Displayed_ChannelText : presentationData.strings.Gift_Hidden_ChannelText } else { - giftsPeerId = arguments.peerId + giftsPeerId = context.account.peerId text = added ? presentationData.strings.Gift_Displayed_NewText : presentationData.strings.Gift_Hidden_NewText } From b378b7b282985bd0d1801c31d93ab89248131180 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 8 Mar 2025 03:14:09 +0400 Subject: [PATCH 11/13] Various fixes --- .../PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift | 6 ++++++ .../Sources/ChatControllerOpenAttachmentMenu.swift | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index c70cee98c0..1673cf93da 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -12707,6 +12707,12 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc self.starsContext = nil } + if isMyProfile, let profileGiftsContext { + profileGiftsContext.updateFilter(.All) + profileGiftsContext.updateSorting(.date) + profileGiftsContext.reload() + } + self.presentationData = updatedPresentationData?.0 ?? context.sharedContext.currentPresentationData.with { $0 } let baseNavigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData) diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift index e5ba6f0de2..7844f12364 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift @@ -620,7 +620,7 @@ extension ChatControllerImpl { if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer, let starsContext = context.starsContext { let premiumGiftOptions = strongSelf.presentationInterfaceState.premiumGiftOptions if !premiumGiftOptions.isEmpty { - let controller = PremiumGiftAttachmentScreen(context: context, starsContext: starsContext, peerId: peer.id, premiumOptions: premiumGiftOptions, hasBirthday: true, completion: { [weak self] in + let controller = PremiumGiftAttachmentScreen(context: context, starsContext: starsContext, peerId: peer.id, premiumOptions: premiumGiftOptions, hasBirthday: strongSelf.presentationInterfaceState.hasBirthdayToday, completion: { [weak self] in guard let self else { return } From 588f5cb8c023482fa30c6b4f5e41d0432478edce Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 8 Mar 2025 18:30:57 +0100 Subject: [PATCH 12/13] Guard resource parsing --- .../Sources/EmojiKeyboardItemLayer.swift | 4 +++- .../Sources/EmojiPagerContentComponent.swift | 11 ++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiKeyboardItemLayer.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiKeyboardItemLayer.swift index db8ad96529..7a584de5bf 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiKeyboardItemLayer.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiKeyboardItemLayer.swift @@ -177,7 +177,9 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget { switch content { case let .animation(animationData): - let animationDataResource = animationData.resource._parse() + guard let animationDataResource = animationData.resource._parse() else { + return + } let loadAnimation: () -> Void = { [weak self] in guard let strongSelf = self else { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index 6811ddf78d..7de85bc70f 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -65,12 +65,15 @@ public final class EntityKeyboardAnimationData: Equatable { case stickerPackThumbnail(id: Int64, accessHash: Int64, info: StickerPackCollectionInfo.Accessor) case file(PartialMediaReference?, TelegramMediaFile.Accessor) - func _parse() -> MediaResourceReference { + func _parse() -> MediaResourceReference? { switch self { case let .resource(resource): return resource case let .stickerPackThumbnail(id, accessHash, info): - return .stickerPackThumbnail(stickerPack: .id(id: id, accessHash: accessHash), resource: info._parse().thumbnail!.resource) + guard let thumbnail = info._parse().thumbnail else { + return nil + } + return .stickerPackThumbnail(stickerPack: .id(id: id, accessHash: accessHash), resource: thumbnail.resource) case let .file(partialReference, file): let file = file._parse() if let partialReference { @@ -1740,7 +1743,9 @@ public final class EmojiPagerContentComponent: Component { }) } - component.animationRenderer.setFrameIndex(itemId: animationData.resource._parse().resource.id.stringRepresentation, size: itemLayer.pixelSize, frameIndex: sourceItem.frameIndex, placeholder: sourceItem.placeholder) + if let resource = animationData.resource._parse() { + component.animationRenderer.setFrameIndex(itemId: resource.resource.id.stringRepresentation, size: itemLayer.pixelSize, frameIndex: sourceItem.frameIndex, placeholder: sourceItem.placeholder) + } } else { let distance = itemLayer.position.y - itemLayout.frame(groupIndex: 0, itemIndex: 0).midY let maxDistance = self.bounds.height From 509de0f5e92386f0a1fc5aa43933c7e125b12ed7 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Mon, 10 Mar 2025 11:41:18 +0100 Subject: [PATCH 13/13] Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a9eb99ef70..b74f0cf625 100644 --- a/.gitignore +++ b/.gitignore @@ -69,4 +69,5 @@ submodules/OpusBinding/SharedHeaders/* submodules/FFMpegBinding/SharedHeaders/* submodules/OpenSSLEncryptionProvider/SharedHeaders/* submodules/TelegramCore/FlatSerialization/Sources/* -buildServer.json \ No newline at end of file +buildServer.json +.build/**