diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index cf541e0c07..0b3c86d41f 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -14034,3 +14034,26 @@ Sorry for the inconvenience."; "Privacy.Gifts.PremiumToast.Action" = "Open"; "Gift.Send.ErrorDisallowed" = "**%@** doesn't accept this kind of gifts."; + +"ChatbotSetup.Rights.ManageMessages" = "Manage Messages"; +"ChatbotSetup.Rights.ReadMessages" = "Read Messages"; +"ChatbotSetup.Rights.ReplyToMessages" = "Reply to Messages"; +"ChatbotSetup.Rights.MarkAsRead" = "Mark Messages as Read"; +"ChatbotSetup.Rights.DeleteSentMessages" = "Delete Sent Messages"; +"ChatbotSetup.Rights.DeleteReceivedMessages" = "Delete Received Messages"; + +"ChatbotSetup.Rights.ManageProfile" = "Manage Profile"; +"ChatbotSetup.Rights.EditName" = "Edit Name"; +"ChatbotSetup.Rights.EditBio" = "Edit Bio"; +"ChatbotSetup.Rights.EditProfilePhoto" = "Edit Profile Photo"; +"ChatbotSetup.Rights.EditUsername" = "Edit Username"; + +"ChatbotSetup.Rights.ManageGiftsAndStars" = "Manage Gifts and Stars"; +"ChatbotSetup.Rights.ViewGifts" = "View Gifts"; +"ChatbotSetup.Rights.SellGifts" = "Sell Gifts"; +"ChatbotSetup.Rights.ChangeGiftSettings" = "Change Gift Settings"; +"ChatbotSetup.Rights.TransferAndUpgradeGifts" = "Transfer and Upgrade Gifts"; +"ChatbotSetup.Rights.TransferStars" = "Transfer Stars"; +"ChatbotSetup.Rights.ManageStories" = "Manage Stories"; + +"Gift.Send.Upgrade.ForcedInfo" = "%1$@ accepts only unique gifts. [Learn More >]()"; diff --git a/submodules/ChatListUI/BUILD b/submodules/ChatListUI/BUILD index c64893a407..b1491734a3 100644 --- a/submodules/ChatListUI/BUILD +++ b/submodules/ChatListUI/BUILD @@ -114,6 +114,7 @@ swift_library( "//submodules/TelegramUI/Components/LottieComponent", "//submodules/TelegramUI/Components/AvatarUploadToastScreen", "//submodules/TelegramUI/Components/Ads/AdsInfoScreen", + "//submodules/TelegramUI/Components/Ads/AdsReportScreen", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 6d5338baa8..b90f8608a1 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -54,6 +54,7 @@ import OldChannelsController import TextFormat import AvatarUploadToastScreen import AdsInfoScreen +import AdsReportScreen private final class ContextControllerContentSourceImpl: ContextControllerContentSource { let controller: ViewController @@ -6131,15 +6132,16 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.push(controller) } - func openAdInfo(_ node: ASDisplayNode) { + func openAdInfo(node: ASDisplayNode, adPeer: AdPeer) { let controller = self let referenceView = node.view let context = self.context let presentationData = context.sharedContext.currentPresentationData.with { $0 } + //TODO:localize var actions: [ContextMenuItem] = [] - //if adAttribute.sponsorInfo != nil || adAttribute.additionalInfo != nil { + if adPeer.sponsorInfo != nil || adPeer.additionalInfo != nil { actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_AdSponsorInfo, textColor: .primary, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Channels"), color: theme.actionSheet.primaryTextColor) }, iconSource: nil, action: { [weak self] c, _ in @@ -6154,32 +6156,40 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController subItems.append(.separator) -// if let sponsorInfo = adAttribute.sponsorInfo { -// subItems.append(.action(ContextMenuActionItem(text: sponsorInfo, textColor: .primary, textLayout: .multiline, textFont: .custom(font: Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 0.8)), height: nil, verticalOffset: nil), badge: nil, icon: { theme in -// return nil -// }, iconSource: nil, action: { [weak self] c, _ in -// c?.dismiss(completion: { -// UIPasteboard.general.string = sponsorInfo -// -// self?.displayUndo(.copy(text: presentationData.strings.Chat_ContextMenu_AdSponsorInfoCopied)) -// }) -// }))) -// } -// if let additionalInfo = adAttribute.additionalInfo { -// subItems.append(.action(ContextMenuActionItem(text: additionalInfo, textColor: .primary, textLayout: .multiline, textFont: .custom(font: Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 0.8)), height: nil, verticalOffset: nil), badge: nil, icon: { theme in -// return nil -// }, iconSource: nil, action: { [weak self] c, _ in -// c?.dismiss(completion: { -// UIPasteboard.general.string = additionalInfo -// -// self?.displayUndo(.copy(text: presentationData.strings.Chat_ContextMenu_AdSponsorInfoCopied)) -// }) -// }))) -// } + if let sponsorInfo = adPeer.sponsorInfo { + subItems.append(.action(ContextMenuActionItem(text: sponsorInfo, textColor: .primary, textLayout: .multiline, textFont: .custom(font: Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 0.8)), height: nil, verticalOffset: nil), badge: nil, icon: { theme in + return nil + }, iconSource: nil, action: { [weak self] c, _ in + c?.dismiss(completion: { + UIPasteboard.general.string = sponsorInfo + + if let self { + self.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Chat_ContextMenu_AdSponsorInfoCopied), elevatedLayout: false, action: { _ in + return true + }), in: .current) + } + }) + }))) + } + if let additionalInfo = adPeer.additionalInfo { + subItems.append(.action(ContextMenuActionItem(text: additionalInfo, textColor: .primary, textLayout: .multiline, textFont: .custom(font: Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 0.8)), height: nil, verticalOffset: nil), badge: nil, icon: { theme in + return nil + }, iconSource: nil, action: { [weak self] c, _ in + c?.dismiss(completion: { + UIPasteboard.general.string = additionalInfo + + if let self { + self.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Chat_ContextMenu_AdSponsorInfoCopied), elevatedLayout: false, action: { _ in + return true + }), in: .current) + } + }) + }))) + } c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) }))) - //} + } actions.append(.action(ContextMenuActionItem(text: "About These Ads", textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor) @@ -6190,70 +6200,59 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } }))) - if "".isEmpty { - actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_ReportAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.actionSheet.primaryTextColor) - }, iconSource: nil, action: { [weak self] _, f in - f(.default) - - guard let navigationController = self?.navigationController as? NavigationController else { - return + actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_ReportAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.actionSheet.primaryTextColor) + }, iconSource: nil, action: { [weak self] _, f in + f(.default) + + guard let navigationController = self?.navigationController as? NavigationController else { + return + } + + let _ = (context.engine.messages.reportAdMessage(opaqueId: adPeer.opaqueId, option: nil) + |> deliverOnMainQueue).start(next: { [weak navigationController] result in + if case let .options(title, options) = result { + Queue.mainQueue().after(0.2) { + navigationController?.pushViewController( + AdsReportScreen( + context: context, + opaqueId: adPeer.opaqueId, + title: title, + options: options, + completed: { + //removeAd?(adAttribute.opaqueId) + } + ) + ) + } } - let _ = navigationController - //.dismiss(animated: true) - - // let _ = (context.engine.messages.reportAdMessage(peerId: message.id.peerId, opaqueId: adAttribute.opaqueId, option: nil) - // |> deliverOnMainQueue).start(next: { [weak navigationController] result in - // if case let .options(title, options) = result { - // Queue.mainQueue().after(0.2) { - // navigationController?.pushViewController( - // AdsReportScreen( - // context: context, - // peerId: message.id.peerId, - // opaqueId: adAttribute.opaqueId, - // title: title, - // options: options, - // completed: { - // removeAd?(adAttribute.opaqueId) - // } - // ) - // ) - // } - // } - // }) - }))) - - actions.append(.separator) - - actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_RemoveAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.primaryTextColor) - }, iconSource: nil, action: { [weak self] c, _ in - c?.dismiss(completion: { - let _ = self -// if context.isPremium { -// removeAd?(adAttribute.opaqueId) -// } else { -// self?.presentNoAdsDemo() -// } - }) - }))) - } -// } else { -// if !actions.isEmpty { -// actions.append(.separator) -// } -// actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Hide, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in -// return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.primaryTextColor) -// }, iconSource: nil, action: { [weak self] c, _ in -// c?.dismiss(completion: { -// if context.isPremium { -// removeAd?(adAttribute.opaqueId) -// } else { -// self?.presentNoAdsDemo() -// } -// }) -// }))) -// } + }) + }))) + + actions.append(.separator) + + actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_RemoveAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.primaryTextColor) + }, iconSource: nil, action: { [weak self] c, _ in + guard let navigationController = self?.navigationController as? NavigationController else { + return + } + c?.dismiss(completion: { + if context.isPremium && !"".isEmpty { + //removeAd?(adAttribute.opaqueId) + } else { + var replaceImpl: ((ViewController) -> Void)? + let demoController = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, forceDark: false, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: false, dismissed: nil) + replaceImpl?(controller) + }, dismissed: nil) + replaceImpl = { [weak demoController] c in + demoController?.replace(with: c) + } + navigationController.pushViewController(demoController) + } + }) + }))) let contextController = ContextController(presentationData: presentationData, source: .reference(AdsInfoContextReferenceContentSource(controller: controller, sourceView: referenceView, insets: .zero, contentInsets: .zero)), items: .single(ContextController.Items(content: .list(actions))), gesture: nil) controller.presentInGlobalOverlay(contextController) diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index ef7d0214de..4d8534e741 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -1684,8 +1684,8 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { contentNode.dismissSearch = { [weak self] in self?.dismissSearch?() } - contentNode.openAdInfo = { [weak self] node in - self?.controller?.openAdInfo(node) + contentNode.openAdInfo = { [weak self] node, adPeer in + self?.controller?.openAdInfo(node: node, adPeer: adPeer) } self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, contentNode: contentNode, cancel: { [weak self] in diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index d473ae9a91..8802ea428c 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -62,9 +62,9 @@ final class ChatListSearchInteraction { let openStories: ((PeerId, ASDisplayNode) -> Void)? let switchToFilter: (ChatListSearchPaneKey) -> Void let dismissSearch: () -> Void - let openAdInfo: (ASDisplayNode) -> Void + let openAdInfo: (ASDisplayNode, AdPeer) -> Void - init(openPeer: @escaping (EnginePeer, EnginePeer?, Int64?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void, openMessage: @escaping (EnginePeer, Int64?, EngineMessage.Id, Bool) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (EngineMessage.Id, Bool) -> Void, messageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void), mediaMessageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, getSelectedMessageIds: @escaping () -> Set?, openStories: ((PeerId, ASDisplayNode) -> Void)?, switchToFilter: @escaping (ChatListSearchPaneKey) -> Void, dismissSearch: @escaping () -> Void, openAdInfo: @escaping (ASDisplayNode) -> Void) { + init(openPeer: @escaping (EnginePeer, EnginePeer?, Int64?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void, openMessage: @escaping (EnginePeer, Int64?, EngineMessage.Id, Bool) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (EngineMessage.Id, Bool) -> Void, messageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void), mediaMessageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, getSelectedMessageIds: @escaping () -> Set?, openStories: ((PeerId, ASDisplayNode) -> Void)?, switchToFilter: @escaping (ChatListSearchPaneKey) -> Void, dismissSearch: @escaping () -> Void, openAdInfo: @escaping (ASDisplayNode, AdPeer) -> Void) { self.openPeer = openPeer self.openDisabledPeer = openDisabledPeer self.openMessage = openMessage @@ -105,7 +105,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo private let navigationController: NavigationController? var dismissSearch: (() -> Void)? - var openAdInfo: ((ASDisplayNode) -> Void)? + var openAdInfo: ((ASDisplayNode, AdPeer) -> Void)? private let dimNode: ASDisplayNode let filterContainerNode: ChatListSearchFiltersContainerNode @@ -307,8 +307,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } }, dismissSearch: { [weak self] in self?.dismissSearch?() - }, openAdInfo: { [weak self] node in - self?.openAdInfo?(node) + }, openAdInfo: { [weak self] node, adPeer in + self?.openAdInfo?(node, adPeer) }) self.paneContainerNode.interaction = interaction diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index a479c14661..388e03209f 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -439,8 +439,9 @@ public enum ChatListSearchEntry: Comparable, Identifiable { case topic(EnginePeer, ChatListItemContent.ThreadInfo, Int, PresentationTheme, PresentationStrings, ChatListSearchSectionExpandType) case recentlySearchedPeer(EnginePeer, EnginePeer?, (Int32, Bool)?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder, PeerStoryStats?, Bool) + case adPeer(AdPeer, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder, ChatListSearchSectionExpandType, String?) case localPeer(EnginePeer, EnginePeer?, (Int32, Bool)?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder, ChatListSearchSectionExpandType, PeerStoryStats?, Bool, Bool) - case globalPeer(FoundPeer, (Int32, Bool)?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder, ChatListSearchSectionExpandType, PeerStoryStats?, Bool, String?, Bool) + case globalPeer(FoundPeer, (Int32, Bool)?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder, ChatListSearchSectionExpandType, PeerStoryStats?, Bool, String?) case message(EngineMessage, EngineRenderedPeer, EnginePeerReadCounters?, EngineMessageHistoryThread.Info?, ChatListPresentationData, Int32, Bool?, Bool, MessageOrderingKey, (id: String, size: Int64, isFirstInList: Bool)?, MessageSection, Bool, PeerStoryStats?, Bool, TelegramSearchPeersScope) case messagePlaceholder(Int32, ChatListPresentationData, TelegramSearchPeersScope) case emptyMessagesFooter(ChatListPresentationData, TelegramSearchPeersScope, String?) @@ -454,7 +455,9 @@ public enum ChatListSearchEntry: Comparable, Identifiable { return .localPeerId(peer.id) case let .localPeer(peer, _, _, _, _, _, _, _, _, _, _, _): return .localPeerId(peer.id) - case let .globalPeer(peer, _, _, _, _, _, _, _, _, _, _, _): + case let .adPeer(peer, _, _, _, _, _, _, _): + return .globalPeerId(peer.peer.id) + case let .globalPeer(peer, _, _, _, _, _, _, _, _, _, _): return .globalPeerId(peer.peer.id) case let .message(message, _, _, _, _, _, _, _, _, _, section, _, _, _, _): return .messageId(message.id, section) @@ -487,8 +490,14 @@ public enum ChatListSearchEntry: Comparable, Identifiable { } else { return false } - case let .globalPeer(lhsPeer, lhsUnreadBadge, lhsIndex, lhsTheme, lhsStrings, lhsSortOrder, lhsDisplayOrder, lhsExpandType, lhsStoryStats, lhsRequiresPremiumForMessaging, lhsQuery, lhsIsAd): - if case let .globalPeer(rhsPeer, rhsUnreadBadge, rhsIndex, rhsTheme, rhsStrings, rhsSortOrder, rhsDisplayOrder, rhsExpandType, rhsStoryStats, rhsRequiresPremiumForMessaging, rhsQuery, rhsIsAd) = rhs, lhsPeer == rhsPeer && lhsIndex == rhsIndex && lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder && lhsUnreadBadge?.0 == rhsUnreadBadge?.0 && lhsUnreadBadge?.1 == rhsUnreadBadge?.1 && lhsExpandType == rhsExpandType && lhsStoryStats == rhsStoryStats && lhsRequiresPremiumForMessaging == rhsRequiresPremiumForMessaging, lhsQuery == rhsQuery, lhsIsAd == rhsIsAd { + case let .adPeer(lhsPeer, lhsIndex, lhsTheme, lhsStrings, lhsSortOrder, lhsDisplayOrder, lhsExpandType, lhsQuery): + if case let .adPeer(rhsPeer, rhsIndex, rhsTheme, rhsStrings, rhsSortOrder, rhsDisplayOrder, rhsExpandType, rhsQuery) = rhs, lhsPeer == rhsPeer && lhsIndex == rhsIndex && lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder && lhsExpandType == rhsExpandType && lhsQuery == rhsQuery { + return true + } else { + return false + } + case let .globalPeer(lhsPeer, lhsUnreadBadge, lhsIndex, lhsTheme, lhsStrings, lhsSortOrder, lhsDisplayOrder, lhsExpandType, lhsStoryStats, lhsRequiresPremiumForMessaging, lhsQuery): + if case let .globalPeer(rhsPeer, rhsUnreadBadge, rhsIndex, rhsTheme, rhsStrings, rhsSortOrder, rhsDisplayOrder, rhsExpandType, rhsStoryStats, rhsRequiresPremiumForMessaging, rhsQuery) = rhs, lhsPeer == rhsPeer && lhsIndex == rhsIndex && lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder && lhsUnreadBadge?.0 == rhsUnreadBadge?.0 && lhsUnreadBadge?.1 == rhsUnreadBadge?.1 && lhsExpandType == rhsExpandType && lhsStoryStats == rhsStoryStats && lhsRequiresPremiumForMessaging == rhsRequiresPremiumForMessaging, lhsQuery == rhsQuery { return true } else { return false @@ -620,14 +629,23 @@ public enum ChatListSearchEntry: Comparable, Identifiable { return false case let .localPeer(_, _, _, rhsIndex, _, _, _, _, _, _, _, _): return lhsIndex <= rhsIndex - case .globalPeer, .message, .messagePlaceholder, .emptyMessagesFooter, .addContact: + case .adPeer, .globalPeer, .message, .messagePlaceholder, .emptyMessagesFooter, .addContact: return true } - case let .globalPeer(_, _, lhsIndex, _, _, _, _, _, _, _, _, _): + case let .adPeer(_, lhsIndex, _, _, _, _, _, _): switch rhs { case .topic, .recentlySearchedPeer, .localPeer: return false - case let .globalPeer(_, _, rhsIndex, _, _, _, _, _, _, _, _, _): + case let .adPeer(_, rhsIndex, _, _, _, _, _, _): + return lhsIndex <= rhsIndex + case .globalPeer, .message, .messagePlaceholder, .emptyMessagesFooter, .addContact: + return true + } + case let .globalPeer(_, _, lhsIndex, _, _, _, _, _, _, _, _): + switch rhs { + case .topic, .recentlySearchedPeer, .localPeer, .adPeer: + return false + case let .globalPeer(_, _, rhsIndex, _, _, _, _, _, _, _, _): return lhsIndex <= rhsIndex case .message, .messagePlaceholder, .emptyMessagesFooter, .addContact: return true @@ -808,6 +826,51 @@ public enum ChatListSearchEntry: Comparable, Identifiable { openStories(peer.id, sourceNode.avatarNode) } }) + case let .adPeer(peer, _, theme, strings, nameSortOrder, nameDisplayOrder, expandType, _): + let enabled = true + var suffixString = "" + if let subscribers = peer.subscribers, subscribers != 0 { + if case .user = peer.peer { + suffixString = ", \(strings.Conversation_StatusBotSubscribers(subscribers))" + } else if case let .channel(channel) = peer.peer, case .broadcast = channel.info { + suffixString = ", \(strings.Conversation_StatusSubscribers(subscribers))" + } else { + suffixString = ", \(strings.Conversation_StatusMembers(subscribers))" + } + } + + let header: ChatListSearchItemHeader? + let actionTitle: String? + switch expandType { + case .none: + actionTitle = nil + case .expand: + actionTitle = strings.ChatList_Search_ShowMore + case .collapse: + actionTitle = strings.ChatList_Search_ShowLess + } + header = ChatListSearchItemHeader(type: .globalPeers, theme: theme, strings: strings, actionTitle: actionTitle, action: actionTitle == nil ? nil : { _ in + toggleExpandGlobalResults() + }) + + return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .generalSearch(isSavedMessages: false), peer: .peer(peer: peer.peer, chatPeer: peer.peer), status: .addressName(suffixString), badge: nil, requiresPremiumForMessaging: false, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, searchQuery: nil, isAd: true, action: { _ in + interaction.peerSelected(peer.peer, nil, nil, nil, false) + }, disabledAction: { _ in + interaction.disabledPeerSelected(peer.peer, nil, .generic) + }, contextAction: peerContextAction.flatMap { peerContextAction in + return { node, gesture, location in + peerContextAction(peer.peer, .search(nil), node, gesture, location) + } + }, animationCache: interaction.animationCache, animationRenderer: interaction.animationRenderer, storyStats: nil, openStories: { itemPeer, sourceNode in + guard case let .peer(_, chatPeer) = itemPeer, let peer = chatPeer else { + return + } + if let sourceNode = sourceNode as? ContactsPeerItemNode { + openStories(peer.id, sourceNode.avatarNode) + } + }, adButtonAction: { node in + interaction.openAdInfo(node, peer) + }) case let .localPeer(peer, associatedPeer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder, expandType, storyStats, requiresPremiumForMessaging, isSelf): let primaryPeer: EnginePeer var chatPeer: EnginePeer? @@ -942,7 +1005,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { openStories(peer.id, sourceNode.avatarNode) } }) - case let .globalPeer(peer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder, expandType, storyStats, requiresPremiumForMessaging, query, isAd): + case let .globalPeer(peer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder, expandType, storyStats, requiresPremiumForMessaging, query): var enabled = true if filter.contains(.onlyWriteable) { enabled = canSendMessagesToPeer(peer.peer) @@ -1002,7 +1065,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { isSavedMessages = true } - return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .generalSearch(isSavedMessages: isSavedMessages), peer: .peer(peer: EnginePeer(peer.peer), chatPeer: EnginePeer(peer.peer)), status: .addressName(suffixString), badge: badge, requiresPremiumForMessaging: requiresPremiumForMessaging, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, searchQuery: query, isAd: isAd, action: { _ in + return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .generalSearch(isSavedMessages: isSavedMessages), peer: .peer(peer: EnginePeer(peer.peer), chatPeer: EnginePeer(peer.peer)), status: .addressName(suffixString), badge: badge, requiresPremiumForMessaging: requiresPremiumForMessaging, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, searchQuery: query, isAd: false, action: { _ in interaction.peerSelected(EnginePeer(peer.peer), nil, nil, nil, false) }, disabledAction: { _ in interaction.disabledPeerSelected(EnginePeer(peer.peer), nil, requiresPremiumForMessaging ? .premiumRequired : .generic) @@ -1019,8 +1082,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { if let sourceNode = sourceNode as? ContactsPeerItemNode { openStories(peer.id, sourceNode.avatarNode) } - }, adButtonAction: { node in - interaction.openAdInfo(node) + }, adButtonAction: { _ in }) case let .message(message, peer, readState, threadInfo, presentationData, _, selected, displayCustomHeader, orderingKey, _, section, allPaused, storyStats, requiresPremiumForMessaging, searchScope): let header: ChatListSearchItemHeader @@ -1791,7 +1853,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { self.mediaNode.isHidden = true self.recentListNode.isHidden = peersFilter.contains(.excludeRecent) - let currentRemotePeers = Atomic<([FoundPeer], [FoundPeer])?>(value: nil) + let currentRemotePeers = Atomic<([FoundPeer], [FoundPeer], [AdPeer])?>(value: nil) let presentationDataPromise = self.presentationDataPromise let searchStatePromise = self.searchStatePromise let selectionPromise = self.selectedMessagesPromise @@ -2351,35 +2413,40 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let _ = previousRecentlySearchedPeersState.swap(nil) } - let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError> - let currentRemotePeersValue: ([FoundPeer], [FoundPeer]) = currentRemotePeers.with { $0 } ?? ([], []) + let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], [AdPeer], Bool), NoError> + let currentRemotePeersValue: ([FoundPeer], [FoundPeer], [AdPeer]) = currentRemotePeers.with { $0 } ?? ([], [], []) if case .savedMessagesChats = location { - foundRemotePeers = .single(([], [], false)) + foundRemotePeers = .single(([], [], [], false)) } else if let query = query, case .chats = key { if query.hasPrefix("#") { - foundRemotePeers = .single(([], [], false)) + foundRemotePeers = .single(([], [], [], false)) } else { foundRemotePeers = ( - .single((currentRemotePeersValue.0, currentRemotePeersValue.1, true)) + .single((currentRemotePeersValue.0, currentRemotePeersValue.1, currentRemotePeersValue.2, true)) |> then( globalPeerSearchContext.searchRemotePeers(engine: context.engine, query: query) - |> map { ($0.0, $0.1, false) } + |> mapToSignal { result in + return context.engine.peers.searchAdPeers(query: query) + |> map { adPeers in + return (result.0, result.1, adPeers, false) + } + } ) ) } } else if let query = query, case .channels = key { foundRemotePeers = ( - .single((currentRemotePeersValue.0, currentRemotePeersValue.1, true)) + .single((currentRemotePeersValue.0, currentRemotePeersValue.1, currentRemotePeersValue.2, true)) |> then( globalPeerSearchContext.searchRemotePeers(engine: context.engine, query: query, scope: .channels) - |> map { ($0.0, $0.1, false) } + |> map { ($0.0, $0.1, [], false) } ) ) } else if let query, case .apps = key { let _ = query - foundRemotePeers = .single(([], [], false)) + foundRemotePeers = .single(([], [], [], false)) } else { - foundRemotePeers = .single(([], [], false)) + foundRemotePeers = .single(([], [], [], false)) } let searchLocations: [SearchMessagesLocation] if let options = options { @@ -2661,7 +2728,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { foundThreads ) |> map { accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, foundPublicMessages, presentationData, searchState, selectionState, resolvedMessage, recentPeers, allAndFoundThreads -> ([ChatListSearchEntry], Bool)? in - let isSearching = foundRemotePeers.2 || foundRemoteMessages.1 || foundPublicMessages.1 + let isSearching = foundRemotePeers.3 || foundRemoteMessages.1 || foundPublicMessages.1 var entries: [ChatListSearchEntry] = [] var index = 0 @@ -2677,7 +2744,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { recentPeers = [] } - let _ = currentRemotePeers.swap((foundRemotePeers.0, foundRemotePeers.1)) + let _ = currentRemotePeers.swap((foundRemotePeers.0, foundRemotePeers.1, foundRemotePeers.2)) let filteredPeer: (EnginePeer, EnginePeer) -> Bool = { peer, accountPeer in if let requestPeerType { @@ -2882,7 +2949,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { index += 1 } } - + if peersFilter.contains(.includeSelf) { for renderedPeer in foundLocalPeers.peers { if renderedPeer.peerId == context.account.peerId, let peer = renderedPeer.peers[renderedPeer.peerId], filteredPeer(peer, EnginePeer(accountPeer)) { @@ -2962,7 +3029,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } } } - + for peer in foundRemotePeers.0 { if case .expand = localExpandType, numberOfLocalPeers >= 3 { break @@ -2978,6 +3045,14 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { var numberOfGlobalPeers = 0 index = 0 + for peer in foundRemotePeers.2 { + if !existingPeerIds.contains(peer.peer.id) { + existingPeerIds.insert(peer.peer.id) + entries.append(.adPeer(peer, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, globalExpandType, finalQuery)) + index += 1 + } + } + if let _ = tagMask { } else { for peer in foundRemotePeers.1 { @@ -2986,14 +3061,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } if !existingPeerIds.contains(peer.peer.id), filteredPeer(EnginePeer(peer.peer), EnginePeer(accountPeer)) { - //TODO:unmock - var isAd = !"".isEmpty - #if DEBUG - isAd = numberOfGlobalPeers == 0 - #endif - existingPeerIds.insert(peer.peer.id) - entries.append(.globalPeer(peer, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, globalExpandType, nil, false, finalQuery, isAd)) + entries.append(.globalPeer(peer, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, globalExpandType, nil, false, finalQuery)) index += 1 numberOfGlobalPeers += 1 } @@ -3013,7 +3082,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } var firstHeaderId: Int64? - if !foundRemotePeers.2 { + if !foundRemotePeers.3 { index = 0 var existingPostIds = Set() for foundPublicMessageSet in foundPublicMessages.0 { @@ -3279,8 +3348,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { - }, openAdInfo: { node in - interaction.openAdInfo(node) + }, openAdInfo: { node, adPeer in + interaction.openAdInfo(node, adPeer) }, openAccountFreezeInfo: { }) chatListInteraction.isSearchMode = true @@ -3410,7 +3479,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { if case let .user(user) = peer, user.flags.contains(.requirePremium) { requiresPremiumForMessagingPeerIds.append(peer.id) } - case let .globalPeer(foundPeer, _, _, _, _, _, _, _, _, _, _, _): + case let .globalPeer(foundPeer, _, _, _, _, _, _, _, _, _, _): storyStatsIds.append(foundPeer.peer.id) if let user = foundPeer.peer as? TelegramUser, user.flags.contains(.requirePremium) { requiresPremiumForMessagingPeerIds.append(foundPeer.peer.id) @@ -3451,8 +3520,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { mappedItems[i] = .recentlySearchedPeer(peer, associatedPeer, unreadBadge, index, theme, strings, sortOrder, displayOrder, stats[peer.id] ?? nil, requiresPremiumForMessaging[peer.id] ?? false) case let .localPeer(peer, associatedPeer, unreadBadge, index, theme, strings, sortOrder, displayOrder, expandType, _, _, isSelf): mappedItems[i] = .localPeer(peer, associatedPeer, unreadBadge, index, theme, strings, sortOrder, displayOrder, expandType, stats[peer.id] ?? nil, requiresPremiumForMessaging[peer.id] ?? false, isSelf) - case let .globalPeer(peer, unreadBadge, index, theme, strings, sortOrder, displayOrder, expandType, _, _, searchQuery, isAd): - mappedItems[i] = .globalPeer(peer, unreadBadge, index, theme, strings, sortOrder, displayOrder, expandType, stats[peer.peer.id] ?? nil, requiresPremiumForMessaging[peer.peer.id] ?? false, searchQuery, isAd) + case let .globalPeer(peer, unreadBadge, index, theme, strings, sortOrder, displayOrder, expandType, _, _, searchQuery): + mappedItems[i] = .globalPeer(peer, unreadBadge, index, theme, strings, sortOrder, displayOrder, expandType, stats[peer.peer.id] ?? nil, requiresPremiumForMessaging[peer.peer.id] ?? false, searchQuery) case let .message(message, peer, combinedPeerReadState, threadInfo, presentationData, totalCount, selected, displayCustomHeader, key, resourceId, section, allPaused, _, _, searchScope): mappedItems[i] = .message(message, peer, combinedPeerReadState, threadInfo, presentationData, totalCount, selected, displayCustomHeader, key, resourceId, section, allPaused, stats[peer.peerId] ?? nil, requiresPremiumForMessaging[peer.peerId] ?? false, searchScope) default: @@ -5263,7 +5332,7 @@ public final class ChatListSearchShimmerNode: ASDisplayNode { }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { - }, openAdInfo: { _ in + }, openAdInfo: { _, _ in }, openAccountFreezeInfo: { }) var isInlineMode = false diff --git a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift index cc9dcbf111..032e03ab2e 100644 --- a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift @@ -161,7 +161,7 @@ public final class ChatListShimmerNode: ASDisplayNode { }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { - }, openAdInfo: { _ in + }, openAdInfo: { _, _ in }, openAccountFreezeInfo: { }) interaction.isInlineMode = isInlineMode diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 8175610721..8d4f30727a 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -114,7 +114,7 @@ public final class ChatListNodeInteraction { let editPeer: (ChatListItem) -> Void let openWebApp: (TelegramUser) -> Void let openPhotoSetup: () -> Void - let openAdInfo: (ASDisplayNode) -> Void + let openAdInfo: (ASDisplayNode, AdPeer) -> Void let openAccountFreezeInfo: () -> Void public var searchTextHighightState: String? @@ -174,7 +174,7 @@ public final class ChatListNodeInteraction { editPeer: @escaping (ChatListItem) -> Void, openWebApp: @escaping (TelegramUser) -> Void, openPhotoSetup: @escaping () -> Void, - openAdInfo: @escaping (ASDisplayNode) -> Void, + openAdInfo: @escaping (ASDisplayNode, AdPeer) -> Void, openAccountFreezeInfo: @escaping () -> Void ) { self.activateSearch = activateSearch @@ -1245,7 +1245,7 @@ public final class ChatListNode: ListView { public var openStarsTopup: ((Int64?) -> Void)? public var openWebApp: ((TelegramUser) -> Void)? public var openPhotoSetup: (() -> Void)? - public var openAdInfo: ((ASDisplayNode) -> Void)? + public var openAdInfo: ((ASDisplayNode, AdPeer) -> Void)? public var openAccountFreezeInfo: (() -> Void)? private var theme: PresentationTheme @@ -1905,8 +1905,8 @@ public final class ChatListNode: ListView { return } self.openPhotoSetup?() - }, openAdInfo: { [weak self] node in - self?.openAdInfo?(node) + }, openAdInfo: { [weak self] node, adPeer in + self?.openAdInfo?(node, adPeer) }, openAccountFreezeInfo: { [weak self] in self?.openAccountFreezeInfo?() }) diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 10044328a6..9d5fe45f50 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -569,14 +569,13 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { }, iconSource: nil, action: { [weak self] _, f in f(.default) - let _ = (context.engine.messages.reportAdMessage(peerId: message.id.peerId, opaqueId: adAttribute.opaqueId, option: nil) + let _ = (context.engine.messages.reportAdMessage(opaqueId: adAttribute.opaqueId, option: nil) |> deliverOnMainQueue).start(next: { [weak self] result in if case let .options(title, options) = result { if let navigationController = self?.baseNavigationController() as? NavigationController { navigationController.pushViewController( AdsReportScreen( context: context, - peerId: message.id.peerId, opaqueId: adAttribute.opaqueId, title: title, options: options, diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 5e2b000949..73c47b9b04 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -3131,14 +3131,13 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { }, iconSource: nil, action: { [weak self] _, f in f(.default) - let _ = (context.engine.messages.reportAdMessage(peerId: message.id.peerId, opaqueId: adAttribute.opaqueId, option: nil) + let _ = (context.engine.messages.reportAdMessage(opaqueId: adAttribute.opaqueId, option: nil) |> deliverOnMainQueue).start(next: { [weak self] result in if case let .options(title, options) = result { if let navigationController = self?.baseNavigationController() as? NavigationController { navigationController.pushViewController( AdsReportScreen( context: context, - peerId: message.id.peerId, opaqueId: adAttribute.opaqueId, title: title, options: options, diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift index 728209a759..eada09d94e 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift @@ -174,8 +174,9 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { case disallowedGiftsUnlimited(PresentationTheme, String, Bool, Bool) case disallowedGiftsLimited(PresentationTheme, String, Bool, Bool) case disallowedGiftsUnique(PresentationTheme, String, Bool, Bool) + case disallowedGiftsPremium(PresentationTheme, String, Bool, Bool) case disallowedGiftsInfo(PresentationTheme, String) - case showGiftButton(PresentationTheme, String, Bool, Bool) + case showGiftButton(PresentationTheme, String, Bool, Bool, Bool) case showGiftButtonInfo(PresentationTheme, String) var section: ItemListSectionId { @@ -202,7 +203,7 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { return SelectivePrivacySettingsSection.hideReadTime.rawValue case .subscribeToPremium, .subscribeToPremiumInfo: return SelectivePrivacySettingsSection.premium.rawValue - case .disallowedGiftsHeader, .disallowedGiftsUnlimited, .disallowedGiftsLimited, .disallowedGiftsUnique, .disallowedGiftsInfo: + case .disallowedGiftsHeader, .disallowedGiftsUnlimited, .disallowedGiftsLimited, .disallowedGiftsUnique, .disallowedGiftsPremium, .disallowedGiftsInfo: return SelectivePrivacySettingsSection.disallowedGifts.rawValue case .showGiftButton, .showGiftButtonInfo: return SelectivePrivacySettingsSection.giftButton.rawValue @@ -285,12 +286,14 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { return 35 case .disallowedGiftsUnique: return 36 - case .disallowedGiftsInfo: + case .disallowedGiftsPremium: return 37 - case .showGiftButton: + case .disallowedGiftsInfo: return 38 - case .showGiftButtonInfo: + case .showGiftButton: return 39 + case .showGiftButtonInfo: + return 40 } } @@ -518,14 +521,20 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { } else { return false } + case let .disallowedGiftsPremium(lhsTheme, lhsText, lhsEnabled, lhsValue): + if case let .disallowedGiftsPremium(rhsTheme, rhsText, rhsEnabled, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsEnabled == rhsEnabled, lhsValue == rhsValue { + return true + } else { + return false + } case let .disallowedGiftsInfo(lhsTheme, lhsText): if case let .disallowedGiftsInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { return true } else { return false } - case let .showGiftButton(lhsTheme, lhsText, lhsEnabled, lhsValue): - if case let .showGiftButton(rhsTheme, rhsText, rhsEnabled, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsEnabled == rhsEnabled, lhsValue == rhsValue { + case let .showGiftButton(lhsTheme, lhsText, lhsEnabled, lhsValue, lhsAvailable): + if case let .showGiftButton(rhsTheme, rhsText, rhsEnabled, rhsValue, rhsAvailable) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsEnabled == rhsEnabled, lhsValue == rhsValue, lhsAvailable == rhsAvailable { return true } else { return false @@ -696,18 +705,28 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { }, activatedWhileDisabled: { arguments.displayLockedGiftsInfo() }) - case let .disallowedGiftsInfo(_, text): - return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) - case let .showGiftButton(_, text, isLocked, value): + case let .disallowedGiftsPremium(_, text, isLocked, value): return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enableInteractiveChanges: !isLocked, enabled: true, displayLocked: isLocked, sectionId: self.section, style: .blocks, updated: { updatedValue in if !isLocked { - arguments.updateShowGiftButton?(updatedValue) + arguments.updateDisallowedGifts?(.premium, !updatedValue) } else { arguments.displayLockedGiftsInfo() } }, activatedWhileDisabled: { arguments.displayLockedGiftsInfo() }) + case let .disallowedGiftsInfo(_, text): + return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) + case let .showGiftButton(_, text, isLocked, value, available): + return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enableInteractiveChanges: !isLocked, enabled: available, displayLocked: isLocked, sectionId: self.section, style: .blocks, updated: { updatedValue in + if !isLocked { + arguments.updateShowGiftButton?(updatedValue) + } else if available { + arguments.displayLockedGiftsInfo() + } + }, activatedWhileDisabled: { + arguments.displayLockedGiftsInfo() + }) case let .showGiftButtonInfo(_, text): let attributedString = NSMutableAttributedString(string: text, font: Font.regular(presentationData.fontSize.itemListBaseHeaderFontSize), textColor: presentationData.theme.list.freeTextColor) if let range = attributedString.string.range(of: "#") { @@ -1125,8 +1144,9 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present entries.append(.disallowedGiftsUnlimited(presentationData.theme, "Unlimited", !isPremium, !(state.disallowedGifts?.contains(.unlimited) ?? false))) entries.append(.disallowedGiftsLimited(presentationData.theme, "Limited-Edition", !isPremium, !(state.disallowedGifts?.contains(.limited) ?? false))) entries.append(.disallowedGiftsUnique(presentationData.theme, "Unique", !isPremium, !(state.disallowedGifts?.contains(.unique) ?? false))) + entries.append(.disallowedGiftsPremium(presentationData.theme, "Premium Subscriptions", !isPremium, !(state.disallowedGifts?.contains(.premium) ?? false))) entries.append(.disallowedGiftsInfo(presentationData.theme, "Choose the types of gifts that you allow others to send you.")) - entries.append(.showGiftButton(presentationData.theme, "Show Gift Icon in Chats", !isPremium, state.showGiftButton == true)) + entries.append(.showGiftButton(presentationData.theme, "Show Gift Icon in Chats", !isPremium, state.showGiftButton == true && state.disallowedGifts != TelegramDisallowedGifts.All, state.disallowedGifts != TelegramDisallowedGifts.All)) entries.append(.showGiftButtonInfo(presentationData.theme, "Display the # Gift icon in the message input field for both participants in all chats.")) } @@ -1603,6 +1623,12 @@ public func selectivePrivacySettingsController( } else { updatedDisallowedGifts.remove(.unique) } + case .premium: + if value { + updatedDisallowedGifts.insert(.premium) + } else { + updatedDisallowedGifts.remove(.premium) + } default: break } @@ -1713,7 +1739,11 @@ public func selectivePrivacySettingsController( disallowedGifts = value } if let value = state.showGiftButton { - showGiftButton = value + if disallowedGifts != TelegramDisallowedGifts.All { + showGiftButton = value + } else { + showGiftButton = false + } } } diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 09079638a9..a57091204c 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -232,7 +232,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, ASScrollView }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { - }, openAdInfo: { _ in + }, openAdInfo: { _, _ in }, openAccountFreezeInfo: { }) diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index f2ae983894..acb84752c4 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -381,7 +381,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, ASScrollViewDelegate { }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { - }, openAdInfo: { _ in + }, openAdInfo: { _, _ in }, openAccountFreezeInfo: { }) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 6a20e73065..f6a9bd1a6c 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -241,7 +241,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[2004110666] = { return Api.DialogFilterSuggested.parse_dialogFilterSuggested($0) } dict[-445792507] = { return Api.DialogPeer.parse_dialogPeer($0) } dict[1363483106] = { return Api.DialogPeer.parse_dialogPeerFolder($0) } - dict[1653721450] = { return Api.DisallowedStarGiftsSettings.parse_disallowedStarGiftsSettings($0) } + dict[1911715524] = { return Api.DisallowedGiftsSettings.parse_disallowedGiftsSettings($0) } dict[-1881881384] = { return Api.Document.parse_document($0) } dict[922273905] = { return Api.Document.parse_documentEmpty($0) } dict[297109817] = { return Api.DocumentAttribute.parse_documentAttributeAnimated($0) } @@ -300,7 +300,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1297942941] = { return Api.GeoPoint.parse_geoPoint($0) } dict[286776671] = { return Api.GeoPoint.parse_geoPointEmpty($0) } dict[-565420653] = { return Api.GeoPointAddress.parse_geoPointAddress($0) } - dict[-715184062] = { return Api.GlobalPrivacySettings.parse_globalPrivacySettings($0) } + dict[-29248689] = { return Api.GlobalPrivacySettings.parse_globalPrivacySettings($0) } dict[-839330845] = { return Api.GroupCall.parse_groupCall($0) } dict[2004925620] = { return Api.GroupCall.parse_groupCallDiscarded($0) } dict[-341428482] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) } @@ -1149,7 +1149,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1831650802] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) } dict[34280482] = { return Api.User.parse_user($0) } dict[-742634630] = { return Api.User.parse_userEmpty($0) } - dict[791719153] = { return Api.UserFull.parse_userFull($0) } + dict[-1712881595] = { return Api.UserFull.parse_userFull($0) } dict[-2100168954] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) } dict[1326562017] = { return Api.UserProfilePhoto.parse_userProfilePhotoEmpty($0) } dict[164646985] = { return Api.UserStatus.parse_userStatusEmpty($0) } @@ -1685,7 +1685,7 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.DialogPeer: _1.serialize(buffer, boxed) - case let _1 as Api.DisallowedStarGiftsSettings: + case let _1 as Api.DisallowedGiftsSettings: _1.serialize(buffer, boxed) case let _1 as Api.Document: _1.serialize(buffer, boxed) diff --git a/submodules/TelegramApi/Sources/Api27.swift b/submodules/TelegramApi/Sources/Api27.swift index 1583ec47c7..576b864fcb 100644 --- a/submodules/TelegramApi/Sources/Api27.swift +++ b/submodules/TelegramApi/Sources/Api27.swift @@ -614,13 +614,13 @@ public extension Api { } public extension Api { enum UserFull: TypeConstructorDescription { - case userFull(flags: Int32, flags2: Int32, id: Int64, about: String?, settings: Api.PeerSettings, personalPhoto: Api.Photo?, profilePhoto: Api.Photo?, fallbackPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, botInfo: Api.BotInfo?, pinnedMsgId: Int32?, commonChatsCount: Int32, folderId: Int32?, ttlPeriod: Int32?, themeEmoticon: String?, privateForwardName: String?, botGroupAdminRights: Api.ChatAdminRights?, botBroadcastAdminRights: Api.ChatAdminRights?, wallpaper: Api.WallPaper?, stories: Api.PeerStories?, businessWorkHours: Api.BusinessWorkHours?, businessLocation: Api.BusinessLocation?, businessGreetingMessage: Api.BusinessGreetingMessage?, businessAwayMessage: Api.BusinessAwayMessage?, businessIntro: Api.BusinessIntro?, birthday: Api.Birthday?, personalChannelId: Int64?, personalChannelMessage: Int32?, stargiftsCount: Int32?, starrefProgram: Api.StarRefProgram?, botVerification: Api.BotVerification?, sendPaidMessagesStars: Int64?, disallowedStargifts: Api.DisallowedStarGiftsSettings?) + case userFull(flags: Int32, flags2: Int32, id: Int64, about: String?, settings: Api.PeerSettings, personalPhoto: Api.Photo?, profilePhoto: Api.Photo?, fallbackPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, botInfo: Api.BotInfo?, pinnedMsgId: Int32?, commonChatsCount: Int32, folderId: Int32?, ttlPeriod: Int32?, themeEmoticon: String?, privateForwardName: String?, botGroupAdminRights: Api.ChatAdminRights?, botBroadcastAdminRights: Api.ChatAdminRights?, wallpaper: Api.WallPaper?, stories: Api.PeerStories?, businessWorkHours: Api.BusinessWorkHours?, businessLocation: Api.BusinessLocation?, businessGreetingMessage: Api.BusinessGreetingMessage?, businessAwayMessage: Api.BusinessAwayMessage?, businessIntro: Api.BusinessIntro?, birthday: Api.Birthday?, personalChannelId: Int64?, personalChannelMessage: Int32?, stargiftsCount: Int32?, starrefProgram: Api.StarRefProgram?, botVerification: Api.BotVerification?, sendPaidMessagesStars: Int64?, disallowedGifts: Api.DisallowedGiftsSettings?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .userFull(let flags, let flags2, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let wallpaper, let stories, let businessWorkHours, let businessLocation, let businessGreetingMessage, let businessAwayMessage, let businessIntro, let birthday, let personalChannelId, let personalChannelMessage, let stargiftsCount, let starrefProgram, let botVerification, let sendPaidMessagesStars, let disallowedStargifts): + case .userFull(let flags, let flags2, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let wallpaper, let stories, let businessWorkHours, let businessLocation, let businessGreetingMessage, let businessAwayMessage, let businessIntro, let birthday, let personalChannelId, let personalChannelMessage, let stargiftsCount, let starrefProgram, let botVerification, let sendPaidMessagesStars, let disallowedGifts): if boxed { - buffer.appendInt32(791719153) + buffer.appendInt32(-1712881595) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags2, buffer: buffer, boxed: false) @@ -654,15 +654,15 @@ public extension Api { if Int(flags2) & Int(1 << 11) != 0 {starrefProgram!.serialize(buffer, true)} if Int(flags2) & Int(1 << 12) != 0 {botVerification!.serialize(buffer, true)} if Int(flags2) & Int(1 << 14) != 0 {serializeInt64(sendPaidMessagesStars!, buffer: buffer, boxed: false)} - if Int(flags2) & Int(1 << 15) != 0 {disallowedStargifts!.serialize(buffer, true)} + if Int(flags2) & Int(1 << 15) != 0 {disallowedGifts!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .userFull(let flags, let flags2, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let wallpaper, let stories, let businessWorkHours, let businessLocation, let businessGreetingMessage, let businessAwayMessage, let businessIntro, let birthday, let personalChannelId, let personalChannelMessage, let stargiftsCount, let starrefProgram, let botVerification, let sendPaidMessagesStars, let disallowedStargifts): - return ("userFull", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("about", about as Any), ("settings", settings as Any), ("personalPhoto", personalPhoto as Any), ("profilePhoto", profilePhoto as Any), ("fallbackPhoto", fallbackPhoto as Any), ("notifySettings", notifySettings as Any), ("botInfo", botInfo as Any), ("pinnedMsgId", pinnedMsgId as Any), ("commonChatsCount", commonChatsCount as Any), ("folderId", folderId as Any), ("ttlPeriod", ttlPeriod as Any), ("themeEmoticon", themeEmoticon as Any), ("privateForwardName", privateForwardName as Any), ("botGroupAdminRights", botGroupAdminRights as Any), ("botBroadcastAdminRights", botBroadcastAdminRights as Any), ("wallpaper", wallpaper as Any), ("stories", stories as Any), ("businessWorkHours", businessWorkHours as Any), ("businessLocation", businessLocation as Any), ("businessGreetingMessage", businessGreetingMessage as Any), ("businessAwayMessage", businessAwayMessage as Any), ("businessIntro", businessIntro as Any), ("birthday", birthday as Any), ("personalChannelId", personalChannelId as Any), ("personalChannelMessage", personalChannelMessage as Any), ("stargiftsCount", stargiftsCount as Any), ("starrefProgram", starrefProgram as Any), ("botVerification", botVerification as Any), ("sendPaidMessagesStars", sendPaidMessagesStars as Any), ("disallowedStargifts", disallowedStargifts as Any)]) + case .userFull(let flags, let flags2, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let wallpaper, let stories, let businessWorkHours, let businessLocation, let businessGreetingMessage, let businessAwayMessage, let businessIntro, let birthday, let personalChannelId, let personalChannelMessage, let stargiftsCount, let starrefProgram, let botVerification, let sendPaidMessagesStars, let disallowedGifts): + return ("userFull", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("about", about as Any), ("settings", settings as Any), ("personalPhoto", personalPhoto as Any), ("profilePhoto", profilePhoto as Any), ("fallbackPhoto", fallbackPhoto as Any), ("notifySettings", notifySettings as Any), ("botInfo", botInfo as Any), ("pinnedMsgId", pinnedMsgId as Any), ("commonChatsCount", commonChatsCount as Any), ("folderId", folderId as Any), ("ttlPeriod", ttlPeriod as Any), ("themeEmoticon", themeEmoticon as Any), ("privateForwardName", privateForwardName as Any), ("botGroupAdminRights", botGroupAdminRights as Any), ("botBroadcastAdminRights", botBroadcastAdminRights as Any), ("wallpaper", wallpaper as Any), ("stories", stories as Any), ("businessWorkHours", businessWorkHours as Any), ("businessLocation", businessLocation as Any), ("businessGreetingMessage", businessGreetingMessage as Any), ("businessAwayMessage", businessAwayMessage as Any), ("businessIntro", businessIntro as Any), ("birthday", birthday as Any), ("personalChannelId", personalChannelId as Any), ("personalChannelMessage", personalChannelMessage as Any), ("stargiftsCount", stargiftsCount as Any), ("starrefProgram", starrefProgram as Any), ("botVerification", botVerification as Any), ("sendPaidMessagesStars", sendPaidMessagesStars as Any), ("disallowedGifts", disallowedGifts as Any)]) } } @@ -767,9 +767,9 @@ public extension Api { } } var _32: Int64? if Int(_2!) & Int(1 << 14) != 0 {_32 = reader.readInt64() } - var _33: Api.DisallowedStarGiftsSettings? + var _33: Api.DisallowedGiftsSettings? if Int(_2!) & Int(1 << 15) != 0 {if let signature = reader.readInt32() { - _33 = Api.parse(reader, signature: signature) as? Api.DisallowedStarGiftsSettings + _33 = Api.parse(reader, signature: signature) as? Api.DisallowedGiftsSettings } } let _c1 = _1 != nil let _c2 = _2 != nil @@ -805,7 +805,7 @@ public extension Api { let _c32 = (Int(_2!) & Int(1 << 14) == 0) || _32 != nil let _c33 = (Int(_2!) & Int(1 << 15) == 0) || _33 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 { - return Api.UserFull.userFull(flags: _1!, flags2: _2!, id: _3!, about: _4, settings: _5!, personalPhoto: _6, profilePhoto: _7, fallbackPhoto: _8, notifySettings: _9!, botInfo: _10, pinnedMsgId: _11, commonChatsCount: _12!, folderId: _13, ttlPeriod: _14, themeEmoticon: _15, privateForwardName: _16, botGroupAdminRights: _17, botBroadcastAdminRights: _18, wallpaper: _19, stories: _20, businessWorkHours: _21, businessLocation: _22, businessGreetingMessage: _23, businessAwayMessage: _24, businessIntro: _25, birthday: _26, personalChannelId: _27, personalChannelMessage: _28, stargiftsCount: _29, starrefProgram: _30, botVerification: _31, sendPaidMessagesStars: _32, disallowedStargifts: _33) + return Api.UserFull.userFull(flags: _1!, flags2: _2!, id: _3!, about: _4, settings: _5!, personalPhoto: _6, profilePhoto: _7, fallbackPhoto: _8, notifySettings: _9!, botInfo: _10, pinnedMsgId: _11, commonChatsCount: _12!, folderId: _13, ttlPeriod: _14, themeEmoticon: _15, privateForwardName: _16, botGroupAdminRights: _17, botBroadcastAdminRights: _18, wallpaper: _19, stories: _20, businessWorkHours: _21, businessLocation: _22, businessGreetingMessage: _23, businessAwayMessage: _24, businessIntro: _25, birthday: _26, personalChannelId: _27, personalChannelMessage: _28, stargiftsCount: _29, starrefProgram: _30, botVerification: _31, sendPaidMessagesStars: _32, disallowedGifts: _33) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api5.swift b/submodules/TelegramApi/Sources/Api5.swift index bdf4515c45..88cff55c26 100644 --- a/submodules/TelegramApi/Sources/Api5.swift +++ b/submodules/TelegramApi/Sources/Api5.swift @@ -1425,14 +1425,14 @@ public extension Api { } } public extension Api { - enum DisallowedStarGiftsSettings: TypeConstructorDescription { - case disallowedStarGiftsSettings(flags: Int32) + enum DisallowedGiftsSettings: TypeConstructorDescription { + case disallowedGiftsSettings(flags: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .disallowedStarGiftsSettings(let flags): + case .disallowedGiftsSettings(let flags): if boxed { - buffer.appendInt32(1653721450) + buffer.appendInt32(1911715524) } serializeInt32(flags, buffer: buffer, boxed: false) break @@ -1441,17 +1441,17 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .disallowedStarGiftsSettings(let flags): - return ("disallowedStarGiftsSettings", [("flags", flags as Any)]) + case .disallowedGiftsSettings(let flags): + return ("disallowedGiftsSettings", [("flags", flags as Any)]) } } - public static func parse_disallowedStarGiftsSettings(_ reader: BufferReader) -> DisallowedStarGiftsSettings? { + public static func parse_disallowedGiftsSettings(_ reader: BufferReader) -> DisallowedGiftsSettings? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil if _c1 { - return Api.DisallowedStarGiftsSettings.disallowedStarGiftsSettings(flags: _1!) + return Api.DisallowedGiftsSettings.disallowedGiftsSettings(flags: _1!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api7.swift b/submodules/TelegramApi/Sources/Api7.swift index 50e65c4677..7bc3feafee 100644 --- a/submodules/TelegramApi/Sources/Api7.swift +++ b/submodules/TelegramApi/Sources/Api7.swift @@ -946,25 +946,25 @@ public extension Api { } public extension Api { enum GlobalPrivacySettings: TypeConstructorDescription { - case globalPrivacySettings(flags: Int32, noncontactPeersPaidStars: Int64?, disallowedStargifts: Api.DisallowedStarGiftsSettings?) + case globalPrivacySettings(flags: Int32, noncontactPeersPaidStars: Int64?, disallowedGifts: Api.DisallowedGiftsSettings?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .globalPrivacySettings(let flags, let noncontactPeersPaidStars, let disallowedStargifts): + case .globalPrivacySettings(let flags, let noncontactPeersPaidStars, let disallowedGifts): if boxed { - buffer.appendInt32(-715184062) + buffer.appendInt32(-29248689) } serializeInt32(flags, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 5) != 0 {serializeInt64(noncontactPeersPaidStars!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 6) != 0 {disallowedStargifts!.serialize(buffer, true)} + if Int(flags) & Int(1 << 6) != 0 {disallowedGifts!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .globalPrivacySettings(let flags, let noncontactPeersPaidStars, let disallowedStargifts): - return ("globalPrivacySettings", [("flags", flags as Any), ("noncontactPeersPaidStars", noncontactPeersPaidStars as Any), ("disallowedStargifts", disallowedStargifts as Any)]) + case .globalPrivacySettings(let flags, let noncontactPeersPaidStars, let disallowedGifts): + return ("globalPrivacySettings", [("flags", flags as Any), ("noncontactPeersPaidStars", noncontactPeersPaidStars as Any), ("disallowedGifts", disallowedGifts as Any)]) } } @@ -973,15 +973,15 @@ public extension Api { _1 = reader.readInt32() var _2: Int64? if Int(_1!) & Int(1 << 5) != 0 {_2 = reader.readInt64() } - var _3: Api.DisallowedStarGiftsSettings? + var _3: Api.DisallowedGiftsSettings? if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.DisallowedStarGiftsSettings + _3 = Api.parse(reader, signature: signature) as? Api.DisallowedGiftsSettings } } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 5) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 6) == 0) || _3 != nil if _c1 && _c2 && _c3 { - return Api.GlobalPrivacySettings.globalPrivacySettings(flags: _1!, noncontactPeersPaidStars: _2, disallowedStargifts: _3) + return Api.GlobalPrivacySettings.globalPrivacySettings(flags: _1!, noncontactPeersPaidStars: _2, disallowedGifts: _3) } else { return nil diff --git a/submodules/TelegramCore/Sources/Settings/PrivacySettings.swift b/submodules/TelegramCore/Sources/Settings/PrivacySettings.swift index 9337bfc7b5..11ad57ff69 100644 --- a/submodules/TelegramCore/Sources/Settings/PrivacySettings.swift +++ b/submodules/TelegramCore/Sources/Settings/PrivacySettings.swift @@ -306,6 +306,14 @@ public struct TelegramDisallowedGifts: OptionSet, Codable { public static let unlimited = TelegramDisallowedGifts(rawValue: 1 << 0) public static let limited = TelegramDisallowedGifts(rawValue: 1 << 1) public static let unique = TelegramDisallowedGifts(rawValue: 1 << 2) + public static let premium = TelegramDisallowedGifts(rawValue: 1 << 3) + + public static let All: TelegramDisallowedGifts = [ + .unlimited, + .limited, + .unique, + .premium + ] public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: StringCodingKey.self) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift index 55962f61db..27c1a93ba0 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift @@ -526,7 +526,7 @@ private class AdMessagesHistoryContextImpl { } func markAction(opaqueId: Data, media: Bool, fullscreen: Bool) { - _internal_markAdAction(account: self.account, peerId: self.peerId, opaqueId: opaqueId, media: media, fullscreen: fullscreen) + _internal_markAdAction(account: self.account, opaqueId: opaqueId, media: media, fullscreen: fullscreen) } func remove(opaqueId: Data) { @@ -601,7 +601,7 @@ public class AdMessagesHistoryContext { } -func _internal_markAdAction(account: Account, peerId: EnginePeer.Id, opaqueId: Data, media: Bool, fullscreen: Bool) { +func _internal_markAdAction(account: Account, opaqueId: Data, media: Bool, fullscreen: Bool) { var flags: Int32 = 0 if media { flags |= (1 << 0) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/QuickReplyMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/QuickReplyMessages.swift index 75181796e7..f65987fe66 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/QuickReplyMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/QuickReplyMessages.swift @@ -1125,6 +1125,23 @@ public struct TelegramBusinessBotRights: OptionSet, Codable { public static let transferStars = TelegramBusinessBotRights(rawValue: 1 << 12) public static let manageStories = TelegramBusinessBotRights(rawValue: 1 << 13) + public static let All: TelegramBusinessBotRights = [ + .reply, + .readMessages, + .deleteSentMessages, + .deleteReceivedMessages, + .editName, + .editBio, + .editProfilePhoto, + .editUsername, + .viewGifts, + .sellGifts, + .changeGiftSettings, + .transferAndUpgradeGifts, + .transferStars, + .manageStories + ] + public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: StringCodingKey.self) let value = try? container.decode(Int32.self, forKey: "v") diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReportAds.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReportAds.swift index 3a93dd60b3..83e40887f1 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReportAds.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReportAds.swift @@ -19,7 +19,7 @@ public enum ReportAdMessageError { case premiumRequired } -func _internal_reportAdMessage(account: Account, peerId: EnginePeer.Id, opaqueId: Data, option: Data?) -> Signal { +func _internal_reportAdMessage(account: Account, opaqueId: Data, option: Data?) -> Signal { return account.network.request(Api.functions.messages.reportSponsoredMessage(randomId: Buffer(data: opaqueId), option: Buffer(data: option))) |> mapError { error -> ReportAdMessageError in if error.errorDescription == "PREMIUM_ACCOUNT_REQUIRED" { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 2ae4018184..07e4ed2243 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -1504,8 +1504,8 @@ public extension TelegramEngine { }).startStandalone() } - public func reportAdMessage(peerId: EnginePeer.Id, opaqueId: Data, option: Data?) -> Signal { - return _internal_reportAdMessage(account: self.account, peerId: peerId, opaqueId: opaqueId, option: option) + public func reportAdMessage(opaqueId: Data, option: Data?) -> Signal { + return _internal_reportAdMessage(account: self.account, opaqueId: opaqueId, option: option) } public func reportContent(subject: ReportContentSubject, option: Data?, message: String?) -> Signal { @@ -1516,8 +1516,8 @@ public extension TelegramEngine { return _internal_updateExtendedMedia(account: self.account, messageIds: messageIds) } - public func markAdAction(peerId: EnginePeer.Id, opaqueId: Data, media: Bool, fullscreen: Bool) { - _internal_markAdAction(account: self.account, peerId: peerId, opaqueId: opaqueId, media: media, fullscreen: fullscreen) + public func markAdAction(opaqueId: Data, media: Bool, fullscreen: Bool) { + _internal_markAdAction(account: self.account, opaqueId: opaqueId, media: media, fullscreen: fullscreen) } public func getAllLocalChannels(count: Int) -> Signal<[EnginePeer.Id], NoError> { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/AdPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/AdPeers.swift index 589bad08d4..556b38990c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/AdPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/AdPeers.swift @@ -6,12 +6,14 @@ import TelegramApi public class AdPeer: Equatable { public let opaqueId: Data public let peer: EnginePeer + public let subscribers: Int32? public let sponsorInfo: String? public let additionalInfo: String? - public init(opaqueId: Data, peer: EnginePeer, sponsorInfo: String?, additionalInfo: String?) { + public init(opaqueId: Data, peer: EnginePeer, subscribers: Int32?, sponsorInfo: String?, additionalInfo: String?) { self.opaqueId = opaqueId self.peer = peer + self.subscribers = subscribers self.sponsorInfo = sponsorInfo self.additionalInfo = additionalInfo } @@ -23,6 +25,9 @@ public class AdPeer: Equatable { if lhs.peer != rhs.peer { return false } + if lhs.subscribers != rhs.subscribers { + return false + } if lhs.sponsorInfo != rhs.sponsorInfo { return false } @@ -49,17 +54,32 @@ func _internal_searchAdPeers(account: Account, query: String) -> Signal<[AdPeer] let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: parsedPeers) + var subscribers: [PeerId: Int32] = [:] + for chat in chats { + if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { + switch chat { + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _, _, _, _, _, _): + if let participantsCount = participantsCount { + subscribers[groupOrChannel.id] = participantsCount + } + default: + break + } + } + } + var result: [AdPeer] = [] for peer in peers { switch peer { case let .sponsoredPeer(_, randomId, apiPeer, sponsorInfo, additionalInfo): - guard let peer = parsedPeers.peers[apiPeer.peerId] else { + guard let peer = transaction.getPeer(apiPeer.peerId) else { continue } result.append( AdPeer( opaqueId: randomId.makeData(), peer: EnginePeer(peer), + subscribers: subscribers[peer.id], sponsorInfo: sponsorInfo, additionalInfo: additionalInfo ) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index 101136152f..80919bc218 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -397,7 +397,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee let sendPaidMessageStars = sendPaidMessageStars.flatMap { StarsAmount(value: $0, nanos: 0) } var disallowedGifts: TelegramDisallowedGifts = [] - if case let .disallowedStarGiftsSettings(giftFlags) = disallowedStarGifts { + if case let .disallowedGiftsSettings(giftFlags) = disallowedStarGifts { if (giftFlags & (1 << 0)) != 0 { disallowedGifts.insert(.unlimited) } @@ -407,6 +407,9 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee if (giftFlags & (1 << 2)) != 0 { disallowedGifts.insert(.unique) } + if (giftFlags & (1 << 3)) != 0 { + disallowedGifts.insert(.premium) + } } return previous.withUpdatedAbout(userFullAbout) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift index 6c39ea3db9..5afae00181 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift @@ -34,7 +34,7 @@ func _internal_updateGlobalPrivacySettings(account: Account) -> Signal Signal Signal Signal retryRequest |> ignoreValues diff --git a/submodules/TelegramUI/Components/Ads/AdsInfoScreen/Sources/AdsInfoScreen.swift b/submodules/TelegramUI/Components/Ads/AdsInfoScreen/Sources/AdsInfoScreen.swift index 65f23993ee..9050d1f148 100644 --- a/submodules/TelegramUI/Components/Ads/AdsInfoScreen/Sources/AdsInfoScreen.swift +++ b/submodules/TelegramUI/Components/Ads/AdsInfoScreen/Sources/AdsInfoScreen.swift @@ -1277,22 +1277,19 @@ public class AdsInfoScreen: ViewController { guard let navigationController = self?.controller?.navigationController as? NavigationController else { return } - - self?.controller?.dismiss(animated: true) - - let _ = (context.engine.messages.reportAdMessage(peerId: message.id.peerId, opaqueId: adAttribute.opaqueId, option: nil) + + let _ = (context.engine.messages.reportAdMessage(opaqueId: adAttribute.opaqueId, option: nil) |> deliverOnMainQueue).start(next: { [weak navigationController] result in if case let .options(title, options) = result { Queue.mainQueue().after(0.2) { navigationController?.pushViewController( AdsReportScreen( context: context, - peerId: message.id.peerId, opaqueId: adAttribute.opaqueId, title: title, options: options, completed: { - removeAd?(adAttribute.opaqueId) + // removeAd?(adAttribute.opaqueId) } ) ) diff --git a/submodules/TelegramUI/Components/Ads/AdsReportScreen/Sources/AdsReportScreen.swift b/submodules/TelegramUI/Components/Ads/AdsReportScreen/Sources/AdsReportScreen.swift index 34da6c35b2..3d7d4db84d 100644 --- a/submodules/TelegramUI/Components/Ads/AdsReportScreen/Sources/AdsReportScreen.swift +++ b/submodules/TelegramUI/Components/Ads/AdsReportScreen/Sources/AdsReportScreen.swift @@ -250,7 +250,6 @@ private final class SheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext - let peerId: EnginePeer.Id let opaqueId: Data let title: String let options: [ReportAdMessageResult.Option] @@ -262,7 +261,6 @@ private final class SheetContent: CombinedComponent { init( context: AccountContext, - peerId: EnginePeer.Id, opaqueId: Data, title: String, options: [ReportAdMessageResult.Option], @@ -273,7 +271,6 @@ private final class SheetContent: CombinedComponent { update: @escaping (ComponentTransition) -> Void ) { self.context = context - self.peerId = peerId self.opaqueId = opaqueId self.title = title self.options = options @@ -288,9 +285,6 @@ private final class SheetContent: CombinedComponent { if lhs.context !== rhs.context { return false } - if lhs.peerId != rhs.peerId { - return false - } if lhs.opaqueId != rhs.opaqueId { return false } @@ -329,7 +323,6 @@ private final class SheetContent: CombinedComponent { let update = component.update let accountContext = component.context - let peerId = component.peerId let opaqueId = component.opaqueId let complete = component.complete let action: (SheetPageContent.Item) -> Void = { [weak state] item in @@ -337,7 +330,7 @@ private final class SheetContent: CombinedComponent { return } state.disposable.set( - (accountContext.engine.messages.reportAdMessage(peerId: peerId, opaqueId: opaqueId, option: item.option) + (accountContext.engine.messages.reportAdMessage(opaqueId: opaqueId, option: item.option) |> deliverOnMainQueue).start(next: { [weak state] result in switch result { case let .options(title, options): @@ -423,7 +416,6 @@ private final class SheetContainerComponent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext - let peerId: EnginePeer.Id let opaqueId: Data let title: String let options: [ReportAdMessageResult.Option] @@ -432,7 +424,6 @@ private final class SheetContainerComponent: CombinedComponent { init( context: AccountContext, - peerId: EnginePeer.Id, opaqueId: Data, title: String, options: [ReportAdMessageResult.Option], @@ -440,7 +431,6 @@ private final class SheetContainerComponent: CombinedComponent { complete: @escaping (ReportResult) -> Void ) { self.context = context - self.peerId = peerId self.opaqueId = opaqueId self.title = title self.options = options @@ -452,9 +442,6 @@ private final class SheetContainerComponent: CombinedComponent { if lhs.context !== rhs.context { return false } - if lhs.peerId != rhs.peerId { - return false - } if lhs.opaqueId != rhs.opaqueId { return false } @@ -490,7 +477,6 @@ private final class SheetContainerComponent: CombinedComponent { component: SheetComponent( content: AnyComponent(SheetContent( context: context.component.context, - peerId: context.component.peerId, opaqueId: context.component.opaqueId, title: context.component.title, options: context.component.options, @@ -571,7 +557,6 @@ public final class AdsReportScreen: ViewControllerComponentContainer { public init( context: AccountContext, - peerId: EnginePeer.Id, opaqueId: Data, title: String, options: [ReportAdMessageResult.Option], @@ -585,7 +570,6 @@ public final class AdsReportScreen: ViewControllerComponentContainer { context: context, component: SheetContainerComponent( context: context, - peerId: peerId, opaqueId: opaqueId, title: title, options: options, diff --git a/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift b/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift index 36ef1d2e41..a9425265d6 100644 --- a/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift +++ b/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift @@ -686,7 +686,7 @@ public final class ChatInlineSearchResultsListComponent: Component { }, openPhotoSetup: { }, - openAdInfo: { _ in + openAdInfo: { _, _ in }, openAccountFreezeInfo: { } diff --git a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift index 81ffcdae4f..118669cffa 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift @@ -69,11 +69,19 @@ public final class GiftItemComponent: Component { } } } + + public enum Font { + case generic + case monospaced + } + public let text: String + public let font: Font public let color: Color - public init(text: String, color: Color) { + public init(text: String, font: Font = .generic, color: Color) { self.text = text + self.font = font self.color = color } } @@ -593,11 +601,19 @@ public final class GiftItemComponent: Component { } else { ribbonFontSize = 10.0 } + let ribbonFont: UIFont + switch ribbon.font { + case .generic: + ribbonFont = Font.semibold(ribbonFontSize) + case .monospaced: + ribbonFont = Font.with(size: 10.0, design: .monospace, weight: .semibold) + } + let ribbonTextSize = self.ribbonText.update( transition: transition, component: AnyComponent( MultilineTextComponent( - text: .plain(NSAttributedString(string: ribbon.text, font: Font.semibold(ribbonFontSize), textColor: .white)), + text: .plain(NSAttributedString(string: ribbon.text, font: ribbonFont, textColor: .white)), horizontalAlignment: .center ) ), @@ -778,7 +794,11 @@ public final class GiftItemComponent: Component { } else { selectionLayer = SimpleShapeLayer() self.selectionLayer = selectionLayer - self.layer.addSublayer(selectionLayer) + if self.ribbon.layer.superlayer != nil { + self.layer.insertSublayer(selectionLayer, below: self.ribbon.layer) + } else { + self.layer.addSublayer(selectionLayer) + } selectionLayer.fillColor = UIColor.clear.cgColor selectionLayer.strokeColor = UIColor.white.cgColor diff --git a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift index 5d04a6823e..0ffe72ca7c 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift @@ -430,10 +430,15 @@ final class GiftOptionsScreenComponent: Component { ) mainController.push(giftController) } else { + var forceUnique = false + if let disallowedGifts = self.state?.disallowedGifts, disallowedGifts.contains(.limited) && !disallowedGifts.contains(.unique) { + forceUnique = true + } + let giftController = GiftSetupScreen( context: component.context, peerId: component.peerId, - subject: .starGift(gift), + subject: .starGift(gift, forceUnique), completion: component.completion ) mainController.push(giftController) @@ -714,7 +719,7 @@ final class GiftOptionsScreenComponent: Component { let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 }) - let isPremiumDisabled = premiumConfiguration.isPremiumDisabled + let isPremiumDisabled = premiumConfiguration.isPremiumDisabled || state.disallowedGifts?.contains(.premium) == true let isSelfGift = component.peerId == component.context.account.peerId let isChannelGift = component.peerId.namespace == Namespaces.Peer.CloudChannel @@ -1425,7 +1430,11 @@ final class GiftOptionsScreenComponent: Component { } if disallowedGifts.contains(.limited) { if gift.availability != nil { - return false + if !disallowedGifts.contains(.unique) && gift.upgradeStars != nil { + + } else { + return false + } } } } diff --git a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift index 611fe56346..70c92c1082 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift @@ -371,7 +371,7 @@ final class GiftSetupScreenComponent: Component { } else { fatalError() } - case let .starGift(starGift): + case let .starGift(starGift, _): finalPrice = starGift.price if self.includeUpgrade, let upgradeStars = starGift.upgradeStars { finalPrice += upgradeStars @@ -403,7 +403,7 @@ final class GiftSetupScreenComponent: Component { return } - if peerId.namespace == Namespaces.Peer.CloudChannel, case let .starGift(starGift) = component.subject { + if peerId.namespace == Namespaces.Peer.CloudChannel, case let .starGift(starGift, _) = component.subject { var controllers = navigationController.viewControllers controllers = controllers.filter { !($0 is GiftSetupScreen) && !($0 is GiftOptionsScreenProtocol) } navigationController.setViewControllers(controllers, animated: true) @@ -555,6 +555,10 @@ final class GiftSetupScreenComponent: Component { self.hideName = true } + if case let .starGift(gift, true) = component.subject, gift.upgradeStars != nil { + self.includeUpgrade = true + } + let _ = (component.context.engine.data.get( TelegramEngine.EngineData.Item.Peer.Peer(id: component.peerId), TelegramEngine.EngineData.Item.Peer.Peer(id: component.context.account.peerId), @@ -684,7 +688,7 @@ final class GiftSetupScreenComponent: Component { self.options = options }) - if case let .starGift(gift) = component.subject { + if case let .starGift(gift, _) = component.subject { if let _ = gift.upgradeStars { self.previewPromise.set( component.context.engine.payments.starGiftUpgradePreview(giftId: gift.id) @@ -742,7 +746,7 @@ final class GiftSetupScreenComponent: Component { contentHeight += environment.navigationHeight contentHeight += 26.0 - if case let .starGift(starGift) = component.subject, let availability = starGift.availability { + if case let .starGift(starGift, _) = component.subject, let availability = starGift.availability { let remains: Int32 = availability.remains let total: Int32 = availability.total let position = CGFloat(remains) / CGFloat(total) @@ -909,7 +913,7 @@ final class GiftSetupScreenComponent: Component { let (currency, amount) = product.storeProduct?.priceCurrencyAndAmount ?? ("USD", 1) subject = .premium(months: product.months, amount: amount, currency: currency) } - case let .starGift(gift): + case let .starGift(gift, _): subject = .starGift(gift: gift) upgradeStars = gift.upgradeStars } @@ -1061,13 +1065,17 @@ final class GiftSetupScreenComponent: Component { contentHeight += starsSectionSize.height contentHeight += sectionSpacing } - case let .starGift(gift): + case let .starGift(gift, forceUnique): if let upgradeStars = gift.upgradeStars, component.peerId != component.context.account.peerId { let upgradeFooterRawString: String if isChannelGift { upgradeFooterRawString = environment.strings.Gift_SendChannel_Upgrade_Info(peerName).string } else { - upgradeFooterRawString = environment.strings.Gift_Send_Upgrade_Info(peerName).string + if forceUnique { + upgradeFooterRawString = environment.strings.Gift_Send_Upgrade_ForcedInfo(peerName).string + } else { + upgradeFooterRawString = environment.strings.Gift_Send_Upgrade_Info(peerName).string + } } let parsedString = parseMarkdownIntoAttributedString(upgradeFooterRawString, attributes: footerAttributes) @@ -1136,8 +1144,8 @@ final class GiftSetupScreenComponent: Component { ) )), ], alignment: .left, spacing: 2.0)), - accessory: .toggle(ListActionItemComponent.Toggle(style: .regular, isOn: self.includeUpgrade, action: { [weak self] _ in - guard let self else { + accessory: .toggle(ListActionItemComponent.Toggle(style: .regular, isOn: self.includeUpgrade, isEnabled: !forceUnique, action: { [weak self] _ in + guard let self, !forceUnique else { return } self.includeUpgrade = !self.includeUpgrade @@ -1263,7 +1271,7 @@ final class GiftSetupScreenComponent: Component { let amountString = product.price buttonString = "\(environment.strings.Gift_Send_Send) \(amountString)" } - case let .starGift(starGift): + case let .starGift(starGift, _): var finalPrice: Int64 = starGift.price if self.includeUpgrade, let upgradePrice = starGift.upgradeStars { finalPrice += upgradePrice @@ -1685,7 +1693,7 @@ final class GiftSetupScreenComponent: Component { public final class GiftSetupScreen: ViewControllerComponentContainer { public enum Subject: Equatable { case premium(PremiumGiftProduct) - case starGift(StarGift.Gift) + case starGift(StarGift.Gift, Bool) } private let context: AccountContext diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/ButtonsComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/ButtonsComponent.swift index 09e7712a60..de4bce26f3 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/ButtonsComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/ButtonsComponent.swift @@ -141,3 +141,24 @@ private func generateCloseButtonImage() -> UIImage? { context.strokePath() })?.withRenderingMode(.alwaysTemplate) } + +func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor) -> UIImage? { + return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.setFillColor(backgroundColor.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) + + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setStrokeColor(foregroundColor.cgColor) + + context.move(to: CGPoint(x: 10.0, y: 10.0)) + context.addLine(to: CGPoint(x: 20.0, y: 20.0)) + context.strokePath() + + context.move(to: CGPoint(x: 20.0, y: 10.0)) + context.addLine(to: CGPoint(x: 10.0, y: 20.0)) + context.strokePath() + }) +} diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUnpinScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUnpinScreen.swift new file mode 100644 index 0000000000..a7022964f4 --- /dev/null +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUnpinScreen.swift @@ -0,0 +1,381 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import SwiftSignalKit +import TelegramCore +import Markdown +import TextFormat +import TelegramPresentationData +import ViewControllerComponent +import SheetComponent +import BundleIconComponent +import BalancedTextComponent +import MultilineTextComponent +import ButtonComponent +import PlainButtonComponent +import GiftItemComponent +import AccountContext + +private final class SheetContent: CombinedComponent { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let gifts: [ProfileGiftsContext.State.StarGift] + let completion: (StarGiftReference) -> Void + let dismiss: () -> Void + + init( + context: AccountContext, + gifts: [ProfileGiftsContext.State.StarGift], + completion: @escaping (StarGiftReference) -> Void, + dismiss: @escaping () -> Void + ) { + self.context = context + self.gifts = gifts + self.completion = completion + self.dismiss = dismiss + } + + static func ==(lhs: SheetContent, rhs: SheetContent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.gifts != rhs.gifts { + return false + } + return true + } + + final class State: ComponentState { + var selectedGift: StarGiftReference? + } + + func makeState() -> State { + return State() + } + + static var body: Body { + let closeButton = Child(Button.self) + + let title = Child(BalancedTextComponent.self) + let text = Child(BalancedTextComponent.self) + let gifts = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) + let button = Child(ButtonComponent.self) + + return { context in + let environment = context.environment[EnvironmentType.self] + let component = context.component + let state = context.state + + let theme = environment.theme + let strings = environment.strings + + let sideInset: CGFloat = 16.0 + environment.safeInsets.left + let textSideInset: CGFloat = 32.0 + environment.safeInsets.left + + let titleFont = Font.semibold(17.0) + let subtitleFont = Font.regular(12.0) + let textColor = theme.actionSheet.primaryTextColor + let secondaryTextColor = theme.actionSheet.secondaryTextColor + + var contentSize = CGSize(width: context.availableSize.width, height: 10.0) + + let closeButton = closeButton.update( + component: Button( + content: AnyComponent(Text(text: strings.Common_Cancel, font: Font.regular(17.0), color: theme.actionSheet.controlAccentColor)), + action: { [weak component] in + component?.dismiss() + } + ), + availableSize: CGSize(width: 100.0, height: 30.0), + transition: .immediate + ) + context.add(closeButton + .position(CGPoint(x: environment.safeInsets.left + 16.0 + closeButton.size.width / 2.0, y: 28.0)) + ) + + let title = title.update( + component: BalancedTextComponent( + text: .plain(NSAttributedString(string: "Too Many Pinned Gifts", font: titleFont, textColor: textColor)), + horizontalAlignment: .center, + maximumNumberOfLines: 1, + lineSpacing: 0.1 + ), + availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), + transition: .immediate + ) + context.add(title + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + title.size.height / 2.0)) + ) + contentSize.height += title.size.height + + let text = text.update( + component: BalancedTextComponent( + text: .plain(NSAttributedString(string: "Select a gift to unpin below:", font: subtitleFont, textColor: secondaryTextColor)), + horizontalAlignment: .center, + maximumNumberOfLines: 1, + lineSpacing: 0.2 + ), + availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), + transition: .immediate + ) + context.add(text + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + text.size.height / 2.0)) + ) + contentSize.height += text.size.height + contentSize.height += 17.0 + + let itemsSideInset = environment.safeInsets.left + 16.0 + let spacing: CGFloat = 10.0 + let itemsInRow = 3 + let width = (context.availableSize.width - itemsSideInset * 2.0 - spacing * CGFloat(itemsInRow - 1)) / CGFloat(itemsInRow) + + var updatedGifts: [_UpdatedChildComponent] = [] + var index = 0 + var nextOriginX = itemsSideInset + for gift in component.gifts { + guard case let .unique(uniqueGift) = gift.gift else { + continue + } + var ribbonColor: GiftItemComponent.Ribbon.Color = .blue + for attribute in uniqueGift.attributes { + if case let .backdrop(_, innerColor, outerColor, _, _, _) = attribute { + ribbonColor = .custom(outerColor, innerColor) + break + } + } + + updatedGifts.append( + gifts[index].update( + component: AnyComponent( + PlainButtonComponent( + content: AnyComponent( + GiftItemComponent( + context: component.context, + theme: theme, + strings: strings, + subject: .uniqueGift(gift: uniqueGift), + ribbon: GiftItemComponent.Ribbon(text: "#\(uniqueGift.number)", font: .monospaced, color: ribbonColor), + isSelected: state.selectedGift == gift.reference, + mode: .grid + ) + ), + effectAlignment: .center, + action: { [weak state] in + guard let state else { + return + } + state.selectedGift = gift.reference + state.updated(transition: .spring(duration: 0.3)) + }, + animateAlpha: false + ) + ), + availableSize: CGSize(width: width, height: width), + transition: context.transition + ) + ) + context.add(updatedGifts[index] + .position(CGPoint(x: nextOriginX + updatedGifts[index].size.width / 2.0, y: contentSize.height + updatedGifts[index].size.height / 2.0)) + ) + + nextOriginX += updatedGifts[index].size.width + spacing + if nextOriginX > context.availableSize.width - itemsSideInset { + contentSize.height += updatedGifts[index].size.height + spacing + nextOriginX = itemsSideInset + } + + index += 1 + } + contentSize.height += 14.0 + + let button = button.update( + component: ButtonComponent( + background: ButtonComponent.Background( + color: theme.list.itemCheckColors.fillColor, + foreground: theme.list.itemCheckColors.foregroundColor, + pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) + ), + content: AnyComponentWithIdentity( + id: AnyHashable("unpin"), + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "Unpin", font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)))) + ), + isEnabled: state.selectedGift != nil, + displaysProgress: false, + action: { [weak state] in + guard let state else { + return + } + if let selectedGift = state.selectedGift { + component.completion(selectedGift) + component.dismiss() + } + } + ), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0), + transition: context.transition + ) + context.add(button + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + button.size.height / 2.0)) + .cornerRadius(10.0) + ) + contentSize.height += button.size.height + contentSize.height += 7.0 + + let effectiveBottomInset: CGFloat = environment.metrics.isTablet ? 0.0 : environment.safeInsets.bottom + contentSize.height += 5.0 + effectiveBottomInset + + return contentSize + } + } +} + +private final class SheetContainerComponent: CombinedComponent { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let gifts: [ProfileGiftsContext.State.StarGift] + let completion: (StarGiftReference) -> Void + + init( + context: AccountContext, + gifts: [ProfileGiftsContext.State.StarGift], + completion: @escaping (StarGiftReference) -> Void + ) { + self.context = context + self.gifts = gifts + self.completion = completion + } + + static func ==(lhs: SheetContainerComponent, rhs: SheetContainerComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.gifts != rhs.gifts { + return false + } + return true + } + + static var body: Body { + let sheet = Child(SheetComponent.self) + let animateOut = StoredActionSlot(Action.self) + + let sheetExternalState = SheetComponent.ExternalState() + + return { context in + let environment = context.environment[EnvironmentType.self] + + let controller = environment.controller + + let sheet = sheet.update( + component: SheetComponent( + content: AnyComponent(SheetContent( + context: context.component.context, + gifts: context.component.gifts, + completion: context.component.completion, + dismiss: { + animateOut.invoke(Action { _ in + if let controller = controller() { + controller.dismiss(completion: nil) + } + }) + } + )), + backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), + followContentSizeChanges: true, + externalState: sheetExternalState, + animateOut: animateOut + ), + environment: { + environment + SheetComponentEnvironment( + isDisplaying: environment.value.isVisible, + isCentered: environment.metrics.widthClass == .regular, + hasInputHeight: !environment.inputHeight.isZero, + regularMetricsSize: CGSize(width: 430.0, height: 900.0), + dismiss: { animated in + if animated { + animateOut.invoke(Action { _ in + if let controller = controller() { + controller.dismiss(completion: nil) + } + }) + } else { + if let controller = controller() { + controller.dismiss(completion: nil) + } + } + } + ) + }, + availableSize: context.availableSize, + transition: context.transition + ) + + context.add(sheet + .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)) + ) + + if let controller = controller(), !controller.automaticallyControlPresentationContextLayout { + let layout = ContainerViewLayout( + size: context.availableSize, + metrics: environment.metrics, + deviceMetrics: environment.deviceMetrics, + intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: max(environment.safeInsets.bottom, sheetExternalState.contentHeight), right: 0.0), + safeInsets: UIEdgeInsets(top: 0.0, left: environment.safeInsets.left, bottom: 0.0, right: environment.safeInsets.right), + additionalInsets: .zero, + statusBarHeight: environment.statusBarHeight, + inputHeight: nil, + inputHeightIsInteractivellyChanging: false, + inVoiceOver: false + ) + controller.presentationContext.containerLayoutUpdated(layout, transition: context.transition.containedViewLayoutTransition) + } + + return context.availableSize + } + } +} + + +public class GiftUnpinScreen: ViewControllerComponentContainer { + private let context: AccountContext + private let gifts: [ProfileGiftsContext.State.StarGift] + private let completion: (StarGiftReference) -> Void + + public init( + context: AccountContext, + gifts: [ProfileGiftsContext.State.StarGift], + completion: @escaping (StarGiftReference) -> Void + ) { + self.context = context + self.gifts = gifts + self.completion = completion + + super.init( + context: context, + component: SheetContainerComponent( + context: context, + gifts: gifts, + completion: completion + ), + navigationBarAppearance: .none, + statusBarStyle: .ignore, + theme: .default + ) + + self.navigationPresentation = .flatModal + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public func dismissAnimated() { + if let view = self.node.hostView.findTaggedView(tag: SheetComponent.View.Tag()) as? SheetComponent.View { + view.dismissAnimated() + } + } +} diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 1c77f28074..8119ee549e 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -3007,12 +3007,6 @@ public class GiftViewScreen: ViewControllerComponentContainer { self.subject = .profileGift(peerId, gift.withPinnedToTop(false)) } } - } else { - var maxPinnedCount: Int = 6 - if let value = context.currentAppConfiguration.with({ $0 }).data?["stargifts_pinned_to_top_limit"] as? Double { - maxPinnedCount = Int(value) - } - self.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: strings.PeerInfo_Gifts_ToastPinLimit_Text(Int32(maxPinnedCount)), timeout: nil, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) } }) }))) diff --git a/submodules/TelegramUI/Components/ListActionItemComponent/Sources/ListActionItemComponent.swift b/submodules/TelegramUI/Components/ListActionItemComponent/Sources/ListActionItemComponent.swift index 43f978e590..0840cf9a5b 100644 --- a/submodules/TelegramUI/Components/ListActionItemComponent/Sources/ListActionItemComponent.swift +++ b/submodules/TelegramUI/Components/ListActionItemComponent/Sources/ListActionItemComponent.swift @@ -18,12 +18,14 @@ public final class ListActionItemComponent: Component { public var style: ToggleStyle public var isOn: Bool public var isInteractive: Bool + public var isEnabled: Bool public var action: ((Bool) -> Void)? - public init(style: ToggleStyle, isOn: Bool, isInteractive: Bool = true, action: ((Bool) -> Void)? = nil) { + public init(style: ToggleStyle, isOn: Bool, isInteractive: Bool = true, isEnabled: Bool = true, action: ((Bool) -> Void)? = nil) { self.style = style self.isOn = isOn self.isInteractive = isInteractive + self.isEnabled = isEnabled self.action = action } @@ -37,6 +39,9 @@ public final class ListActionItemComponent: Component { if lhs.isInteractive != rhs.isInteractive { return false } + if lhs.isEnabled != rhs.isEnabled { + return false + } if (lhs.action == nil) != (rhs.action == nil) { return false } @@ -648,7 +653,9 @@ public final class ListActionItemComponent: Component { } } - switchNode.isUserInteractionEnabled = toggle.isInteractive + switchNode.isUserInteractionEnabled = toggle.isInteractive && toggle.isEnabled + switchNode.alpha = toggle.isEnabled ? 1.0 : 0.3 + switchNode.layer.allowsGroupOpacity = !toggle.isEnabled if updateSwitchTheme { switchNode.frameColor = component.theme.list.itemSwitchColors.frameColor diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift index 4816daf1b6..851f4d4a25 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift @@ -187,7 +187,7 @@ public final class LoadingOverlayNode: ASDisplayNode { }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { - }, openAdInfo: { _ in + }, openAdInfo: { _, _ in }, openAccountFreezeInfo: { }) @@ -548,7 +548,7 @@ private final class PeerInfoScreenPersonalChannelItemNode: PeerInfoScreenItemNod }, openPhotoSetup: { }, - openAdInfo: { _ in + openAdInfo: { _, _ in }, openAccountFreezeInfo: { } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift index 91ad9d8083..820832185f 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift @@ -540,14 +540,18 @@ private final class PeerInfoPendingPane { switch key { case .gifts: var canManage = false + var canGift = true if let peer = data.peer { + if let cachedUserData = data.cachedData as? CachedUserData, cachedUserData.disallowedGifts == .All { + canGift = false + } if let channel = peer as? TelegramChannel, case .broadcast = channel.info { if channel.hasPermission(.sendSomething) { canManage = true } } } - paneNode = PeerInfoGiftsPaneNode(context: context, peerId: peerId, chatControllerInteraction: chatControllerInteraction, profileGifts: data.profileGiftsContext!, canManage: canManage) + paneNode = PeerInfoGiftsPaneNode(context: context, peerId: peerId, chatControllerInteraction: chatControllerInteraction, profileGifts: data.profileGiftsContext!, canManage: canManage, canGift: canGift) case .stories, .storyArchive, .botPreview: var canManage = false if let peer = data.peer { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index c4ae4a17fe..be5e628289 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -6435,15 +6435,18 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser, !user.isDeleted && user.botInfo == nil && !user.flags.contains(.isSupport) { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Profile_SendGift, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Gift"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - if let self { - self.openPremiumGift() - } - }))) + if let cachedData = data.cachedData as? CachedUserData, cachedData.disallowedGifts == .All { + } else { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Profile_SendGift, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Gift"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + if let self { + self.openPremiumGift() + } + }))) + } } if let cachedData = data.cachedData as? CachedUserData, cachedData.flags.contains(.translationHidden) { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index a285032575..efe9ecc5e0 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -33,6 +33,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr private let peerId: PeerId private let profileGifts: ProfileGiftsContext private let canManage: Bool + private let canGift: Bool private var dataDisposable: Disposable? @@ -101,12 +102,13 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr private let maxPinnedCount: Int - public init(context: AccountContext, peerId: PeerId, chatControllerInteraction: ChatControllerInteraction, profileGifts: ProfileGiftsContext, canManage: Bool) { + public init(context: AccountContext, peerId: PeerId, chatControllerInteraction: ChatControllerInteraction, profileGifts: ProfileGiftsContext, canManage: Bool, canGift: Bool) { self.context = context self.peerId = peerId self.chatControllerInteraction = chatControllerInteraction self.profileGifts = profileGifts self.canManage = canManage + self.canGift = canGift self.backgroundNode = ASDisplayNode() self.scrollNode = ASScrollNode() @@ -434,6 +436,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr let ribbonText: String? var ribbonColor: GiftItemComponent.Ribbon.Color = .blue + var ribbonFont: GiftItemComponent.Ribbon.Font = .generic switch product.gift { case let .generic(gift): if let availability = gift.availability { @@ -442,7 +445,8 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr ribbonText = nil } case let .unique(gift): - ribbonText = params.presentationData.strings.PeerInfo_Gifts_OneOf(compactNumericCountString(Int(gift.availability.issued), decimalSeparator: params.presentationData.dateTimeFormat.decimalSeparator)).string + ribbonFont = .monospaced + ribbonText = "#\(gift.number)" for attribute in gift.attributes { if case let .backdrop(_, innerColor, outerColor, _, _, _) = attribute { ribbonColor = .custom(outerColor, innerColor) @@ -471,7 +475,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr strings: params.presentationData.strings, peer: peer, subject: subject, - ribbon: ribbonText.flatMap { GiftItemComponent.Ribbon(text: $0, color: ribbonColor) }, + ribbon: ribbonText.flatMap { GiftItemComponent.Ribbon(text: $0, font: ribbonFont, color: ribbonColor) }, isHidden: !product.savedToProfile, isPinned: product.pinnedToTop, isEditing: self.isReordering, @@ -542,6 +546,19 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr return false } if pinnedToTop && self.pinnedReferences.count >= self.maxPinnedCount { + if let gifts = self.profileGifts.currentState?.gifts.filter({ $0.pinnedToTop }) { + let controller = GiftUnpinScreen( + context: context, + gifts: gifts, + completion: { [weak self] reference in + guard let self else { + return + } + self.profileGifts.updateStarGiftPinnedToTop(reference: reference, pinnedToTop: false) + } + ) + self.parentController?.push(controller) + } return false } if let reference = product.reference { @@ -657,7 +674,10 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr let panelSeparator: ASDisplayNode let panelButton: SolidRoundedButtonNode - let panelAlpha = params.expandProgress + var panelAlpha = params.expandProgress + if !self.canGift { + panelAlpha = 0.0 + } if let current = self.panelBackground { panelBackground = current diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift index 7e0a7745b6..d22e398031 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift @@ -212,7 +212,7 @@ final class GreetingMessageListItemComponent: Component { }, openPhotoSetup: { }, - openAdInfo: { _ in + openAdInfo: { _, _ in }, openAccountFreezeInfo: { } diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift index dd19b5efa6..4b33a6a58a 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift @@ -233,7 +233,7 @@ final class QuickReplySetupScreenComponent: Component { }, openPhotoSetup: { }, - openAdInfo: { _ in + openAdInfo: { _, _ in }, openAccountFreezeInfo: { } diff --git a/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift b/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift index 2d8a980932..5b644960ad 100644 --- a/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift @@ -112,14 +112,24 @@ final class ChatbotSetupScreenComponent: Component { final class Permission { var id: String + var key: TelegramBusinessBotRights? var title: String var value: Bool? var enabled: Bool var subpermissions: [Permission]? var expanded: Bool? - init(id: String, title: String, value: Bool? = nil, enabled: Bool = true, subpermissions: [Permission]? = nil, expanded: Bool? = nil) { + init( + id: String, + key: TelegramBusinessBotRights? = nil, + title: String, + value: Bool? = nil, + enabled: Bool = true, + subpermissions: [Permission]? = nil, + expanded: Bool? = nil + ) { self.id = id + self.key = key self.title = title self.value = value self.enabled = enabled @@ -162,7 +172,6 @@ final class ChatbotSetupScreenComponent: Component { ) private var permissions: [Permission] = [] - private var botRights: TelegramBusinessBotRights = [] override init(frame: CGRect) { @@ -184,30 +193,6 @@ final class ChatbotSetupScreenComponent: Component { self.addSubview(self.scrollView) self.scrollView.layer.addSublayer(self.topOverscrollLayer) - - self.permissions = [ - Permission(id: "message", title: "Manage Messages", subpermissions: [ - Permission(id: "read", title: "Read Messages", value: true, enabled: false), - Permission(id: "reply", title: "Reply to Messages", value: true), - Permission(id: "mark", title: "Mark Messages as Read", value: true), - Permission(id: "deleteSent", title: "Delete Sent Messages", value: true), - Permission(id: "deleteReceived", title: "Delete Received Messages", value: true) - ], expanded: false), - Permission(id: "profile", title: "Manage Profile", subpermissions: [ - Permission(id: "name", title: "Edit Name", value: true), - Permission(id: "bio", title: "Edit Bio", value: true), - Permission(id: "avatar", title: "Edit Profile Picture", value: true), - Permission(id: "username", title: "Edit Username", value: true) - ], expanded: false), - Permission(id: "gifts", title: "Manage Gifts and Stars", subpermissions: [ - Permission(id: "view", title: "View Gifts", value: true), - Permission(id: "sell", title: "Sell Gifts", value: true), - Permission(id: "settings", title: "Change Gift Settings", value: true), - Permission(id: "transfer", title: "Transfer and Upgrade Gifts", value: true), - Permission(id: "transferStars", title: "Transfer Stars", value: true) - ], expanded: false), - Permission(id: "stories", title: "Manage Stories", value: true) - ] } required init?(coder: NSCoder) { @@ -250,7 +235,7 @@ final class ChatbotSetupScreenComponent: Component { let _ = component.context.engine.accountData.setAccountConnectedBot(bot: TelegramAccountConnectedBot( id: peer.id, recipients: recipients, - rights: [] + rights: self.botRights )).startStandalone() } else { let _ = component.context.engine.accountData.setAccountConnectedBot(bot: nil).startStandalone() @@ -526,13 +511,17 @@ final class ChatbotSetupScreenComponent: Component { self.isUpdating = false } + let environment = environment[EnvironmentType.self].value + let themeUpdated = self.environment?.theme !== environment.theme + self.environment = environment + if self.component == nil { if let bot = component.initialData.bot, let botPeer = component.initialData.botPeer, let addressName = botPeer.addressName { self.botResolutionState = BotResolutionState(query: addressName, state: .found(peer: botPeer, isInstalled: true)) self.resetQueryText = addressName.lowercased() self.botRights = bot.rights - + let initialRecipients = bot.recipients var mappedCategories = Set() @@ -571,12 +560,32 @@ final class ChatbotSetupScreenComponent: Component { self.hasAccessToAllChatsByDefault = initialRecipients.exclude } + + self.permissions = [ + Permission(id: "message", title: environment.strings.ChatbotSetup_Rights_ManageMessages, subpermissions: [ + Permission(id: "read", title: environment.strings.ChatbotSetup_Rights_ReadMessages, value: true, enabled: false), + Permission(id: "reply", key: .reply, title: environment.strings.ChatbotSetup_Rights_ReplyToMessages), + Permission(id: "mark", key: .readMessages, title: environment.strings.ChatbotSetup_Rights_MarkAsRead), + Permission(id: "deleteSent", key: .deleteSentMessages, title: environment.strings.ChatbotSetup_Rights_DeleteSentMessages), + Permission(id: "deleteReceived", key: .deleteReceivedMessages, title: environment.strings.ChatbotSetup_Rights_DeleteReceivedMessages) + ], expanded: false), + Permission(id: "profile", title: environment.strings.ChatbotSetup_Rights_ManageProfile, subpermissions: [ + Permission(id: "name", key: .editName, title: environment.strings.ChatbotSetup_Rights_EditName), + Permission(id: "bio", key: .editBio, title: environment.strings.ChatbotSetup_Rights_EditBio), + Permission(id: "avatar", key: .editProfilePhoto, title: environment.strings.ChatbotSetup_Rights_EditProfilePhoto), + Permission(id: "username", key: .editUsername, title: environment.strings.ChatbotSetup_Rights_EditUsername) + ], expanded: false), + Permission(id: "gifts", title: environment.strings.ChatbotSetup_Rights_ManageGiftsAndStars, subpermissions: [ + Permission(id: "view", key: .viewGifts, title: environment.strings.ChatbotSetup_Rights_ViewGifts), + Permission(id: "sell", key: .sellGifts, title: environment.strings.ChatbotSetup_Rights_SellGifts), + Permission(id: "settings", key: .changeGiftSettings, title: environment.strings.ChatbotSetup_Rights_ChangeGiftSettings), + Permission(id: "transfer", key: .transferAndUpgradeGifts, title: environment.strings.ChatbotSetup_Rights_TransferAndUpgradeGifts), + Permission(id: "transferStars", key: .transferStars, title: environment.strings.ChatbotSetup_Rights_TransferStars) + ], expanded: false), + Permission(id: "stories", key: .manageStories, title: environment.strings.ChatbotSetup_Rights_ManageStories) + ] } - - let environment = environment[EnvironmentType.self].value - let themeUpdated = self.environment?.theme !== environment.theme - self.environment = environment - + self.component = component self.state = state @@ -732,6 +741,7 @@ final class ChatbotSetupScreenComponent: Component { if case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.isBusiness) { botResolutionState.state = .found(peer: peer, isInstalled: true) self.botResolutionState = botResolutionState + self.botRights = .All self.state?.updated(transition: .spring(duration: 0.3)) } else { self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.ChatbotSetup_ErrorBotNotBusinessCapable, actions: [ @@ -1027,158 +1037,192 @@ final class ChatbotSetupScreenComponent: Component { if !self.hasAccessToAllChatsByDefault { contentHeight += excludedUsersContentHeight } - - var permissionsItems: [AnyComponentWithIdentity] = [] - for permission in self.permissions { - var value = permission.value == true - - var titleItems: [AnyComponentWithIdentity] = [] - titleItems.append( - AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString( - string: permission.title, - font: Font.regular(presentationData.listsFontSize.baseDisplaySize), - textColor: environment.theme.list.itemPrimaryTextColor - )), - maximumNumberOfLines: 1 - ))) - ) - - if let subpermissions = permission.subpermissions { - value = false - var selectedCount = 0 - for subpermission in subpermissions { - if subpermission.value == true { - value = true - selectedCount += 1 - } + if case .found(_, true) = self.botResolutionState?.state { + var permissionsItems: [AnyComponentWithIdentity] = [] + for permission in self.permissions { + var value: Bool + if let key = permission.key { + value = self.botRights.contains(key) + } else { + value = permission.value == true } + + var titleItems: [AnyComponentWithIdentity] = [] titleItems.append( - AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent( + AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent( text: .plain(NSAttributedString( - string: "\(selectedCount)/\(subpermissions.count)", - font: Font.with(size: presentationData.listsFontSize.baseDisplaySize / 17.0 * 13.0, design: .round, weight: .semibold), + string: permission.title, + font: Font.regular(presentationData.listsFontSize.baseDisplaySize), textColor: environment.theme.list.itemPrimaryTextColor )), maximumNumberOfLines: 1 ))) ) - titleItems.append( - AnyComponentWithIdentity(id: AnyHashable(2), component: AnyComponent(BundleIconComponent( - name: "Item List/ExpandingItemVerticalRegularArrow", - tintColor: environment.theme.list.itemPrimaryTextColor, - flipVertically: permission.expanded == true - ))) - ) - } - permissionsItems.append( - AnyComponentWithIdentity(id: permission.id, component: AnyComponent(ListActionItemComponent( - theme: environment.theme, - title: AnyComponent(HStack(titleItems, spacing: 6.0)), - accessory: .toggle(ListActionItemComponent.Toggle(style: .icons, isOn: value, action: { [weak self] value in - guard let self else { - return + + if let subpermissions = permission.subpermissions { + value = false + var selectedCount = 0 + for subpermission in subpermissions { + if subpermission.value == true { + value = true + selectedCount += 1 } - if let subpermissions = permission.subpermissions { - for subpermission in subpermissions { - if subpermission.enabled { - subpermission.value = value - } - } - } else if let value = permission.value { - permission.value = value - } - self.state?.updated(transition: .spring(duration: 0.4)) - })), - action: permission.subpermissions != nil ? { [weak self] _ in - guard let self else { - return - } - var scrollToBottom = false - if let expanded = permission.expanded { - permission.expanded = !expanded - if !expanded { - scrollToBottom = true - } - } - self.state?.updated(transition: .spring(duration: 0.4)) - if scrollToBottom { - self.scrollView.setContentOffset(CGPoint(x: 0.0, y: self.scrollView.contentSize.height - self.scrollView.bounds.height), animated: true) - } - } : nil - ))) - ) - - if let subpermissions = permission.subpermissions, permission.expanded == true { - for subpermission in subpermissions { - permissionsItems.append( - AnyComponentWithIdentity(id: subpermission.id, component: AnyComponent(ListActionItemComponent( - theme: environment.theme, - title: AnyComponent(VStack([ - AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString( - string: subpermission.title, - font: Font.regular(presentationData.listsFontSize.baseDisplaySize), - textColor: environment.theme.list.itemPrimaryTextColor - )), - maximumNumberOfLines: 1 - ))), - ], alignment: .left, spacing: 2.0)), - leftIcon: .check(ListActionItemComponent.LeftIcon.Check(isSelected: subpermission.value == true, isEnabled: subpermission.enabled, toggle: nil)), - accessory: nil, - action: subpermission.enabled ? { [weak self] _ in - guard let self else { - return - } - if let value = subpermission.value { - subpermission.value = !value - } - self.state?.updated(transition: .spring(duration: 0.4)) - } : nil + } + titleItems.append( + AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: "\(selectedCount)/\(subpermissions.count)", + font: Font.with(size: presentationData.listsFontSize.baseDisplaySize / 17.0 * 13.0, design: .round, weight: .semibold), + textColor: environment.theme.list.itemPrimaryTextColor + )), + maximumNumberOfLines: 1 + ))) + ) + titleItems.append( + AnyComponentWithIdentity(id: AnyHashable(2), component: AnyComponent(BundleIconComponent( + name: "Item List/ExpandingItemVerticalRegularArrow", + tintColor: environment.theme.list.itemPrimaryTextColor, + flipVertically: permission.expanded == true ))) ) } - //permissionsItems.append(AnyComponentWithIdentity(id: "\(permission.id)_sub", component: AnyComponent(VStack(stackItems, spacing: 0.0)))) + permissionsItems.append( + AnyComponentWithIdentity(id: permission.id, component: AnyComponent(ListActionItemComponent( + theme: environment.theme, + title: AnyComponent(HStack(titleItems, spacing: 6.0)), + accessory: .toggle(ListActionItemComponent.Toggle(style: .icons, isOn: value, action: { [weak self] value in + guard let self else { + return + } + if let subpermissions = permission.subpermissions { + for subpermission in subpermissions { + if subpermission.enabled { + if let key = subpermission.key { + if value { + self.botRights.insert(key) + } else { + self.botRights.remove(key) + } + } + } + } + } else if let key = permission.key { + if value { + self.botRights.insert(key) + } else { + self.botRights.remove(key) + } + } + self.state?.updated(transition: .spring(duration: 0.4)) + })), + action: permission.subpermissions != nil ? { [weak self] _ in + guard let self else { + return + } + var scrollToBottom = false + if let expanded = permission.expanded { + permission.expanded = !expanded + if !expanded { + scrollToBottom = true + } + } + self.state?.updated(transition: .spring(duration: 0.4)) + if scrollToBottom { + self.scrollView.setContentOffset(CGPoint(x: 0.0, y: self.scrollView.contentSize.height - self.scrollView.bounds.height), animated: true) + } + } : nil + ))) + ) + + if let subpermissions = permission.subpermissions, permission.expanded == true { + for subpermission in subpermissions { + var value = false + if let key = permission.key { + value = self.botRights.contains(key) + } + + permissionsItems.append( + AnyComponentWithIdentity(id: subpermission.id, component: AnyComponent(ListActionItemComponent( + theme: environment.theme, + title: AnyComponent(VStack([ + AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: subpermission.title, + font: Font.regular(presentationData.listsFontSize.baseDisplaySize), + textColor: environment.theme.list.itemPrimaryTextColor + )), + maximumNumberOfLines: 1 + ))), + ], alignment: .left, spacing: 2.0)), + leftIcon: .check(ListActionItemComponent.LeftIcon.Check(isSelected: value, isEnabled: subpermission.enabled, toggle: nil)), + accessory: nil, + action: subpermission.enabled ? { [weak self] _ in + guard let self else { + return + } + if let key = subpermission.key { + if !value { + self.botRights.insert(key) + } else { + self.botRights.remove(key) + } + } + self.state?.updated(transition: .spring(duration: 0.4)) + } : nil + ))) + ) + } + //permissionsItems.append(AnyComponentWithIdentity(id: "\(permission.id)_sub", component: AnyComponent(VStack(stackItems, spacing: 0.0)))) + } } - } - - - - - let permissionsSectionSize = self.permissionsSection.update( - transition: transition, - component: AnyComponent(ListSectionComponent( - theme: environment.theme, - header: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString( - string: environment.strings.ChatbotSetup_PermissionsSectionHeader, - font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), - textColor: environment.theme.list.freeTextColor - )), - maximumNumberOfLines: 0 - )), - footer: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString( - string: environment.strings.ChatbotSetup_PermissionsSectionFooter, - font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), - textColor: environment.theme.list.freeTextColor - )), - maximumNumberOfLines: 0 - )), - items: permissionsItems - )), - environment: {}, - containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0) - ) - let permissionsSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: permissionsSectionSize) - if let permissionsSectionView = self.permissionsSection.view { - if permissionsSectionView.superview == nil { - self.scrollView.addSubview(permissionsSectionView) + + var permissionsTransition = transition + if self.permissionsSection.view?.superview == nil { + permissionsTransition = .immediate } - transition.setFrame(view: permissionsSectionView, frame: permissionsSectionFrame) + + let permissionsSectionSize = self.permissionsSection.update( + transition: permissionsTransition, + component: AnyComponent(ListSectionComponent( + theme: environment.theme, + header: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: environment.strings.ChatbotSetup_PermissionsSectionHeader, + font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), + textColor: environment.theme.list.freeTextColor + )), + maximumNumberOfLines: 0 + )), + footer: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: environment.strings.ChatbotSetup_PermissionsSectionFooter, + font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), + textColor: environment.theme.list.freeTextColor + )), + maximumNumberOfLines: 0 + )), + items: permissionsItems + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0) + ) + let permissionsSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: permissionsSectionSize) + if let permissionsSectionView = self.permissionsSection.view { + if permissionsSectionView.superview == nil { + self.scrollView.addSubview(permissionsSectionView) + + permissionsSectionView.alpha = 1.0 + transition.animateAlpha(view: permissionsSectionView, from: 0.0, to: 1.0) + } + permissionsTransition.setFrame(view: permissionsSectionView, frame: permissionsSectionFrame) + } + contentHeight += permissionsSectionSize.height + } else if let permissionsSectionView = self.permissionsSection.view { + transition.setAlpha(view: permissionsSectionView, alpha: 0.0, completion: { _ in + permissionsSectionView.removeFromSuperview() + }) } - contentHeight += permissionsSectionSize.height contentHeight += bottomContentInset contentHeight += environment.safeInsets.bottom diff --git a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift index 2b419b6569..0488beb955 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift @@ -875,7 +875,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, ASScrollViewDelegate }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { - }, openAdInfo: { _ in + }, openAdInfo: { _, _ in }, openAccountFreezeInfo: { }) let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 8735c5c3ed..15de45a0dc 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -9265,8 +9265,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if let value = value { self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, context: self.context, text: value, action: canSendMessagesToChat(self.presentationInterfaceState) ? self.presentationData.strings.Conversation_SendDice : nil), elevatedLayout: false, action: { [weak self] action in - if let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState), action == .undo { - strongSelf.sendMessages([.message(text: "", attributes: [], inlineStickers: [:], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: dice.emoji)), threadId: strongSelf.chatLocation.threadId, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]) + if let self, canSendMessagesToChat(self.presentationInterfaceState), action == .undo { + self.presentPaidMessageAlertIfNeeded(completion: { [weak self] postpone in + guard let self else { + return + } + self.sendMessages([.message(text: "", attributes: [], inlineStickers: [:], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: dice.emoji)), threadId: self.chatLocation.threadId, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])], postpone: postpone) + }) } return false }), in: .current) diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index e516970a28..197e0e91a6 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -559,13 +559,12 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState }, iconSource: nil, action: { _, f in f(.default) - let _ = (context.engine.messages.reportAdMessage(peerId: message.id.peerId, opaqueId: adAttribute.opaqueId, option: nil) + let _ = (context.engine.messages.reportAdMessage(opaqueId: adAttribute.opaqueId, option: nil) |> deliverOnMainQueue).start(next: { result in if case let .options(title, options) = result { controllerInteraction.navigationController()?.pushViewController( AdsReportScreen( context: context, - peerId: message.id.peerId, opaqueId: adAttribute.opaqueId, title: title, options: options, diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index aa7b9960a4..933bd42aa0 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -295,7 +295,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, ASScrollViewDe }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { - }, openAdInfo: { _ in + }, openAdInfo: { _, _ in }, openAccountFreezeInfo: { }) interaction.searchTextHighightState = searchQuery diff --git a/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift index 7fdf358ddb..e120053492 100644 --- a/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift @@ -182,7 +182,7 @@ private struct CommandChatInputContextPanelEntry: Comparable, Identifiable { }, openPhotoSetup: { }, - openAdInfo: { _ in + openAdInfo: { _, _ in }, openAccountFreezeInfo: { }