diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 9fe1c6f923..42e365f7d1 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -12768,3 +12768,12 @@ Sorry for the inconvenience."; "InviteLink.Create.RequestApprovalFeeUnavailable" = "You can't enable admin approval for links that require a monthly fee."; "WebApp.PrivacyPolicy_URL" = "https://telegram.org/privacy-tpa"; + +"ChatList.SubscriptionsLowBalance.Stars_1" = "%@ Star needed"; +"ChatList.SubscriptionsLowBalance.Stars_any" = "%@ Stars needed"; + +"ChatList.SubscriptionsLowBalance.Single.Title" = "%1$@ for %@$@"; +"ChatList.SubscriptionsLowBalance.Single.Text" = "Insufficient funds to cover your subscription."; + +"ChatList.SubscriptionsLowBalance.Multiple.Title" = "%@ for your subscriptions"; +"ChatList.SubscriptionsLowBalance.Multiple.Text" = "Insufficient funds to cover your subscriptions."; diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 89c5b3a4be..99971d76a1 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -413,12 +413,28 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele var openBirthdaySetup: (() -> Void)? var openPremiumManagement: (() -> Void)? var openStories: ((ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void)? + var openStarsTopup: ((Int64?) -> Void)? var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)? var didBeginSelectingChats: (() -> Void)? var canExpandHiddenItems: (() -> Bool)? public var displayFilterLimit: (() -> Void)? - public init(context: AccountContext, controller: ChatListControllerImpl?, location: ChatListControllerLocation, chatListMode: ChatListNodeMode = .chatList(appendContacts: true), previewing: Bool, controlsHistoryPreload: Bool, isInlineMode: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, filterBecameEmpty: @escaping (ChatListFilter?) -> Void, filterEmptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void, openArchiveSettings: @escaping () -> Void) { + public init( + context: AccountContext, + controller: ChatListControllerImpl?, + location: ChatListControllerLocation, + chatListMode: ChatListNodeMode = .chatList(appendContacts: true), + previewing: Bool, + controlsHistoryPreload: Bool, + isInlineMode: Bool, + presentationData: PresentationData, + animationCache: AnimationCache, + animationRenderer: MultiAnimationRenderer, + filterBecameEmpty: @escaping (ChatListFilter?) -> Void, + filterEmptyAction: @escaping (ChatListFilter?) -> Void, + secondaryEmptyAction: @escaping () -> Void, + openArchiveSettings: @escaping () -> Void) + { self.context = context self.controller = controller self.location = location diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index ad71ca4561..4a8c2fec32 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -2825,6 +2825,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { if let sourceNode = sourceNode as? ChatListItemNode { self.interaction.openStories?(id, sourceNode.avatarNode) } + }, openStarsTopup: { _ in }, dismissNotice: { _ in }, editPeer: { _ in }) @@ -3658,19 +3659,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } else if case .apps = key { if let navigationController = self.navigationController { if isRecommended { - #if DEBUG - let _ = (self.context.sharedContext.makeMiniAppListScreenInitialData(context: self.context) - |> deliverOnMainQueue).startStandalone(next: { [weak self] initialData in - guard let self, let navigationController = self.navigationController else { - return - } - navigationController.pushViewController(self.context.sharedContext.makeMiniAppListScreen(context: self.context, initialData: initialData)) - }) - #else if let peerInfoScreen = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { navigationController.pushViewController(peerInfoScreen) } - #endif } else if case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp), let parentController = self.parentController { self.context.sharedContext.openWebApp( context: self.context, @@ -4659,6 +4650,7 @@ public final class ChatListSearchShimmerNode: ASDisplayNode { }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: { }, openStories: { _, _ in + }, openStarsTopup: { _ in }, dismissNotice: { _ in }, editPeer: { _ in }) diff --git a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift index 1bd0168a82..7319da6dfb 100644 --- a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift @@ -156,7 +156,8 @@ public final class ChatListShimmerNode: ASDisplayNode { let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in gesture?.cancel() - }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openPremiumManagement: {}, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, dismissNotice: { _ in + }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openPremiumManagement: {}, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, openStarsTopup: { _ in + }, dismissNotice: { _ in }, editPeer: { _ in }) interaction.isInlineMode = isInlineMode diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index e16451c4c9..a670c2403b 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -109,6 +109,7 @@ public final class ChatListNodeInteraction { let openChatFolderUpdates: () -> Void let hideChatFolderUpdates: () -> Void let openStories: (ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void + let openStarsTopup: (Int64?) -> Void let dismissNotice: (ChatListNotice) -> Void let editPeer: (ChatListItem) -> Void @@ -164,6 +165,7 @@ public final class ChatListNodeInteraction { openChatFolderUpdates: @escaping () -> Void, hideChatFolderUpdates: @escaping () -> Void, openStories: @escaping (ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void, + openStarsTopup: @escaping (Int64?) -> Void, dismissNotice: @escaping (ChatListNotice) -> Void, editPeer: @escaping (ChatListItem) -> Void ) { @@ -206,6 +208,7 @@ public final class ChatListNodeInteraction { self.openChatFolderUpdates = openChatFolderUpdates self.hideChatFolderUpdates = hideChatFolderUpdates self.openStories = openStories + self.openStarsTopup = openStarsTopup self.dismissNotice = dismissNotice self.editPeer = editPeer } @@ -747,8 +750,8 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL nodeInteraction?.openPremiumGift(birthdays) case .reviewLogin: break - case .starsSubscriptionLowBalance: - break + case let .starsSubscriptionLowBalance(amount, _): + nodeInteraction?.openStarsTopup(amount) } case .hide: nodeInteraction?.dismissNotice(notice) @@ -1087,8 +1090,8 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL nodeInteraction?.openPremiumGift(birthdays) case .reviewLogin: break - case .starsSubscriptionLowBalance: - break + case let .starsSubscriptionLowBalance(amount, _): + nodeInteraction?.openStarsTopup(amount) } case .hide: nodeInteraction?.dismissNotice(notice) @@ -1208,6 +1211,7 @@ public final class ChatListNode: ListView { public var openStories: ((ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void)? public var openBirthdaySetup: (() -> Void)? public var openPremiumManagement: (() -> Void)? + public var openStarsTopup: ((Int64?) -> Void)? private var theme: PresentationTheme @@ -1809,6 +1813,11 @@ public final class ChatListNode: ListView { return } self.openStories?(subject, itemNode) + }, openStarsTopup: { [weak self] amount in + guard let self else { + return + } + self.openStarsTopup?(amount) }, dismissNotice: { [weak self] notice in guard let self else { return @@ -1910,6 +1919,8 @@ public final class ChatListNode: ListView { } else { displayArchiveIntro = .single(false) } + + let starsSubscriptionsContextPromise = Promise(nil) self.updateIsMainTabDisposable = (self.isMainTab.get() |> deliverOnMainQueue).startStrict(next: { [weak self] isMainTab in @@ -1932,9 +1943,10 @@ public final class ChatListNode: ListView { twoStepData, newSessionReviews(postbox: context.account.postbox), context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Birthday(id: context.account.peerId)), - context.account.stateManager.contactBirthdays + context.account.stateManager.contactBirthdays, + starsSubscriptionsContextPromise.get() ) - |> mapToSignal { suggestions, dismissedSuggestions, configuration, newSessionReviews, birthday, birthdays -> Signal in + |> mapToSignal { suggestions, dismissedSuggestions, configuration, newSessionReviews, birthday, birthdays, starsSubscriptionsContext -> Signal in if let newSessionReview = newSessionReviews.first { return .single(.reviewLogin(newSessionReview: newSessionReview, totalCount: newSessionReviews.count)) } @@ -1968,7 +1980,24 @@ public final class ChatListNode: ListView { todayBirthdayPeerIds = [] } - if suggestions.contains(.gracePremium) { + if suggestions.contains(.starsSubscriptionLowBalance) { + if let starsSubscriptionsContext { + return starsSubscriptionsContext.state + |> map { state in + if state.balance > 0 { + return .starsSubscriptionLowBalance( + amount: state.balance, + peers: state.subscriptions.map { $0.peer } + ) + } else { + return nil + } + } + } else { + starsSubscriptionsContextPromise.set(.single(context.engine.payments.peerStarsSubscriptionsContext(starsContext: nil, missingBalance: true))) + return .single(nil) + } + } else if suggestions.contains(.gracePremium) { return .single(.premiumGrace) } else if suggestions.contains(.setupBirthday) && birthday == nil { return .single(.setupBirthday) diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift index a3b38bca0e..603a3430d4 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift @@ -90,7 +90,7 @@ public enum ChatListNotice: Equatable { case birthdayPremiumGift(peers: [EnginePeer], birthdays: [EnginePeer.Id: TelegramBirthday]) case reviewLogin(newSessionReview: NewSessionReview, totalCount: Int) case premiumGrace - case starsSubscriptionLowBalance(amount: Int64) + case starsSubscriptionLowBalance(amount: Int64, peers: [EnginePeer]) } enum ChatListNodeEntry: Comparable, Identifiable { diff --git a/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift b/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift index c41a4c936e..e3ce47bb39 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift @@ -11,6 +11,8 @@ import ItemListUI import Markdown import AccountContext import MergedAvatarsNode +import TextNodeWithEntities +import TextFormat class ChatListNoticeItem: ListViewItem { enum Action { @@ -87,7 +89,7 @@ private let textFont = Font.regular(15.0) final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { private let contentContainer: ASDisplayNode private let titleNode: TextNode - private let textNode: TextNode + private let textNode: TextNodeWithEntities private let arrowNode: ASImageNode private let separatorNode: ASDisplayNode @@ -113,7 +115,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { self.contentContainer = ASDisplayNode() self.titleNode = TextNode() - self.textNode = TextNode() + self.textNode = TextNodeWithEntities() self.arrowNode = ASImageNode() self.separatorNode = ASDisplayNode() @@ -123,7 +125,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { self.clipsToBounds = true self.contentContainer.addSubnode(self.titleNode) - self.contentContainer.addSubnode(self.textNode) + self.contentContainer.addSubnode(self.textNode.textNode) self.contentContainer.addSubnode(self.arrowNode) self.addSubnode(self.contentContainer) @@ -153,7 +155,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { let previousItem = self.item let makeTitleLayout = TextNode.asyncLayout(self.titleNode) - let makeTextLayout = TextNode.asyncLayout(self.textNode) + let makeTextLayout = TextNodeWithEntities.asyncLayout(self.textNode) let makeOkButtonTextLayout = TextNode.asyncLayout(self.okButtonText) let makeCancelButtonTextLayout = TextNode.asyncLayout(self.cancelButtonText) @@ -262,10 +264,24 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { okButtonLayout = makeOkButtonTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.ChatList_SessionReview_PanelConfirm, font: titleFont, textColor: item.theme.list.itemAccentColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0))) cancelButtonLayout = makeCancelButtonTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.ChatList_SessionReview_PanelReject, font: titleFont, textColor: item.theme.list.itemDestructiveColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0))) - case let .starsSubscriptionLowBalance(amount): - let titleStringValue = NSMutableAttributedString(attributedString: NSAttributedString(string: "⭐️ \(amount) Stars needed for your subscriptions", font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor)) - titleString = titleStringValue - textString = NSAttributedString(string: "Insufficient funds to cover your subscriptions.", font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + case let .starsSubscriptionLowBalance(amount, peers): + let title: String + let text: String + let starsValue = item.strings.ChatList_SubscriptionsLowBalance_Stars(Int32(amount)) + if let peer = peers.first, peers.count == 1 { + title = item.strings.ChatList_SubscriptionsLowBalance_Single_Title(starsValue, peer.compactDisplayTitle).string + text = item.strings.ChatList_SubscriptionsLowBalance_Single_Text + } else { + title = item.strings.ChatList_SubscriptionsLowBalance_Multiple_Title(starsValue).string + text = item.strings.ChatList_SubscriptionsLowBalance_Multiple_Text + } + let attributedTitle = NSMutableAttributedString(string: "⭐️\(title)", font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor) + if let range = attributedTitle.string.range(of: "⭐️") { + attributedTitle.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: attributedTitle.string)) + attributedTitle.addAttribute(.baselineOffset, value: -1.0, range: NSRange(range, in: attributedTitle.string)) + } + titleString = attributedTitle + textString = NSAttributedString(string: text, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) } var leftInset: CGFloat = sideInset @@ -302,12 +318,12 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: verticalInset), size: titleLayout.0.size) } - let _ = textLayout.1() + let _ = textLayout.1(TextNodeWithEntities.Arguments(context: item.context, cache: item.context.animationCache, renderer: item.context.animationRenderer, placeholderColor: .white, attemptSynchronous: true)) if case .center = alignment { - strongSelf.textNode.frame = CGRect(origin: CGPoint(x: floor((params.width - textLayout.0.size.width) * 0.5), y: strongSelf.titleNode.frame.maxY + spacing), size: textLayout.0.size) + strongSelf.textNode.textNode.frame = CGRect(origin: CGPoint(x: floor((params.width - textLayout.0.size.width) * 0.5), y: strongSelf.titleNode.frame.maxY + spacing), size: textLayout.0.size) } else { - strongSelf.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: strongSelf.titleNode.frame.maxY + spacing), size: textLayout.0.size) + strongSelf.textNode.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: strongSelf.titleNode.frame.maxY + spacing), size: textLayout.0.size) } if !avatarPeers.isEmpty { @@ -343,6 +359,8 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { hasCloseButton = true } else if case .premiumGrace = item.notice { hasCloseButton = true + } else if case .starsSubscriptionLowBalance = item.notice { + hasCloseButton = true } if let okButtonLayout, let cancelButtonLayout { @@ -387,8 +405,8 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { let buttonWidth: CGFloat = floor(buttonsWidth * 0.5) let buttonHeight: CGFloat = 32.0 - let okButtonFrame = CGRect(origin: CGPoint(x: floor((params.width - buttonsWidth) * 0.5), y: strongSelf.textNode.frame.maxY + 6.0), size: CGSize(width: buttonWidth, height: buttonHeight)) - let cancelButtonFrame = CGRect(origin: CGPoint(x: okButtonFrame.maxX, y: strongSelf.textNode.frame.maxY + 6.0), size: CGSize(width: buttonWidth, height: buttonHeight)) + let okButtonFrame = CGRect(origin: CGPoint(x: floor((params.width - buttonsWidth) * 0.5), y: strongSelf.textNode.textNode.frame.maxY + 6.0), size: CGSize(width: buttonWidth, height: buttonHeight)) + let cancelButtonFrame = CGRect(origin: CGPoint(x: okButtonFrame.maxX, y: strongSelf.textNode.textNode.frame.maxY + 6.0), size: CGSize(width: buttonWidth, height: buttonHeight)) okButton.frame = okButtonFrame cancelButton.frame = cancelButtonFrame diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index cedad73715..347f115d99 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -227,6 +227,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, ASScrollView }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: { }, openStories: { _, _ in + }, openStarsTopup: { _ in }, dismissNotice: { _ in }, editPeer: { _ in }) diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 70804382b8..974bf7976f 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -376,6 +376,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, ASScrollViewDelegate { }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: { }, openStories: { _, _ in + }, openStarsTopup: { _ in }, dismissNotice: { _ in }, editPeer: { _ in }) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift index 02f2d6b739..b38d00b01d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift @@ -910,7 +910,7 @@ public final class StarsTransactionsContext { private final class StarsSubscriptionsContextImpl { private let account: Account - private weak var starsContext: StarsContext? + private let missingBalance: Bool private var _state: StarsSubscriptionsContext.State private let _statePromise = Promise() @@ -923,16 +923,16 @@ private final class StarsSubscriptionsContextImpl { private var stateDisposable: Disposable? private let updateDisposable = MetaDisposable() - init(account: Account, starsContext: StarsContext) { + init(account: Account, starsContext: StarsContext?, missingBalance: Bool = false) { assert(Queue.mainQueue().isCurrent()) self.account = account - self.starsContext = starsContext + self.missingBalance = missingBalance - let currentSubscriptions = starsContext.currentState?.subscriptions ?? [] - let canLoadMore = starsContext.currentState?.canLoadMoreSubscriptions ?? true + let currentSubscriptions = starsContext?.currentState?.subscriptions ?? [] + let canLoadMore = starsContext?.currentState?.canLoadMoreSubscriptions ?? true - self._state = StarsSubscriptionsContext.State(subscriptions: currentSubscriptions, canLoadMore: canLoadMore, isLoading: false) + self._state = StarsSubscriptionsContext.State(balance: 0, subscriptions: currentSubscriptions, canLoadMore: canLoadMore, isLoading: false) self._statePromise.set(.single(self._state)) self.loadMore() @@ -956,7 +956,7 @@ private final class StarsSubscriptionsContextImpl { updatedState.isLoading = true self.updateState(updatedState) - self.disposable.set((_internal_requestStarsSubscriptions(account: self.account, peerId: self.account.peerId, offset: nextOffset, missingBalance: false) + self.disposable.set((_internal_requestStarsSubscriptions(account: self.account, peerId: self.account.peerId, offset: nextOffset, missingBalance: self.missingBalance) |> deliverOnMainQueue).start(next: { [weak self] status in guard let self else { return @@ -964,6 +964,7 @@ private final class StarsSubscriptionsContextImpl { self.nextOffset = status.nextSubscriptionsOffset var updatedState = self._state + updatedState.balance = status.balance updatedState.subscriptions = nextOffset.isEmpty ? status.subscriptions : updatedState.subscriptions + status.subscriptions updatedState.isLoading = false updatedState.canLoadMore = self.nextOffset != nil @@ -1021,11 +1022,13 @@ private final class StarsSubscriptionsContextImpl { public final class StarsSubscriptionsContext { public struct State: Equatable { + public var balance: Int64 public var subscriptions: [StarsContext.State.Subscription] public var canLoadMore: Bool public var isLoading: Bool - init(subscriptions: [StarsContext.State.Subscription], canLoadMore: Bool, isLoading: Bool) { + init(balance: Int64, subscriptions: [StarsContext.State.Subscription], canLoadMore: Bool, isLoading: Bool) { + self.balance = balance self.subscriptions = subscriptions self.canLoadMore = canLoadMore self.isLoading = isLoading @@ -1052,9 +1055,9 @@ public final class StarsSubscriptionsContext { } } - init(account: Account, starsContext: StarsContext) { + init(account: Account, starsContext: StarsContext?, missingBalance: Bool) { self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: { - return StarsSubscriptionsContextImpl(account: account, starsContext: starsContext) + return StarsSubscriptionsContextImpl(account: account, starsContext: starsContext, missingBalance: missingBalance) }) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift index a7292134b5..43b9079f7e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift @@ -86,8 +86,8 @@ public extension TelegramEngine { return StarsTransactionsContext(account: self.account, subject: subject, mode: mode) } - public func peerStarsSubscriptionsContext(starsContext: StarsContext) -> StarsSubscriptionsContext { - return StarsSubscriptionsContext(account: self.account, starsContext: starsContext) + public func peerStarsSubscriptionsContext(starsContext: StarsContext?, missingBalance: Bool = false) -> StarsSubscriptionsContext { + return StarsSubscriptionsContext(account: self.account, starsContext: starsContext, missingBalance: missingBalance) } public func sendStarsPaymentForm(formId: Int64, source: BotPaymentInvoiceSource) -> Signal { diff --git a/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift b/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift index 0206dacbb7..8edf85c688 100644 --- a/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift +++ b/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift @@ -664,6 +664,8 @@ public final class ChatInlineSearchResultsListComponent: Component { }, openStories: { _, _ in }, + openStarsTopup: { _ in + }, dismissNotice: { _ in }, editPeer: { _ in diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift index ed6532694a..27bcb14a8a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift @@ -182,7 +182,8 @@ public final class LoadingOverlayNode: ASDisplayNode { let interaction = ChatListNodeInteraction(context: context, animationCache: context.animationCache, animationRenderer: context.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in gesture?.cancel() - }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openPremiumManagement: {}, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, dismissNotice: { _ in + }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openPremiumManagement: {}, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, openStarsTopup: { _ in + }, dismissNotice: { _ in }, editPeer: { _ in }) @@ -533,6 +534,8 @@ private final class PeerInfoScreenPersonalChannelItemNode: PeerInfoScreenItemNod StoryContainerScreen.openPeerStories(context: item.context, peerId: item.data.peer.id, parentController: controller, avatarNode: itemNode.avatarNode) }, + openStarsTopup: { _ in + }, dismissNotice: { _ in }, editPeer: { _ in diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift index 13114175db..6aeef906f7 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift @@ -202,6 +202,8 @@ final class GreetingMessageListItemComponent: Component { }, openStories: { _, _ in }, + openStarsTopup: { _ in + }, dismissNotice: { _ in }, editPeer: { _ in diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift index 61127abfb4..bf1e17537d 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift @@ -217,6 +217,8 @@ final class QuickReplySetupScreenComponent: Component { }, openStories: { _, _ in }, + openStarsTopup: { _ in + }, dismissNotice: { _ in }, editPeer: { [weak listNode] _ in diff --git a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift index 23c029753a..819d491cbd 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift @@ -870,6 +870,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, ASScrollViewDelegate }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: { }, openStories: { _, _ in + }, openStarsTopup: { _ in }, dismissNotice: { _ in }, editPeer: { _ in }) diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift index 7f3e18ec47..5869307456 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift @@ -195,7 +195,13 @@ func openWebAppImpl(context: AccountContext, parentController: ViewController, u return } var presentImpl: ((ViewController, Any?) -> Void)? - let params = WebAppParameters(source: isInline ? .inline : .simple, peerId: peer.id, botId: botId, botName: botName, botVerified: botVerified, url: result.url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false, fullSize: result.flags.contains(.fullSize)) + let source: WebAppParameters.Source + if isInline { + source = .inline + } else { + source = url.isEmpty ? .generic : .simple + } + let params = WebAppParameters(source: source, peerId: peer.id, botId: botId, botName: botName, botVerified: botVerified, url: result.url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false, fullSize: result.flags.contains(.fullSize)) let controller = standaloneWebAppController(context: context, updatedPresentationData: updatedPresentationData, params: params, threadId: threadId, openUrl: { [weak parentController] url, concealed, commit in ChatControllerImpl.botOpenUrl(context: context, peerId: peer.id, controller: parentController as? ChatControllerImpl, url: url, concealed: concealed, present: { c, a in presentImpl?(c, a) diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index b339d07840..ebf77e9191 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -290,6 +290,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, ASScrollViewDe }, openChatFolderUpdates: { }, hideChatFolderUpdates: { }, openStories: { _, _ in + }, openStarsTopup: { _ in }, dismissNotice: { _ in }, editPeer: { _ in }) diff --git a/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift index 88d69874ba..e5741bad8a 100644 --- a/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift @@ -172,6 +172,8 @@ private struct CommandChatInputContextPanelEntry: Comparable, Identifiable { }, openStories: { _, _ in }, + openStarsTopup: { _ in + }, dismissNotice: { _ in }, editPeer: { _ in