Merge commit '5e79ec4b32e8c3c9c86216c9697fd40251ba3f2d'

This commit is contained in:
Isaac 2025-03-19 18:56:56 +01:00
commit 93b421cd73
83 changed files with 1820 additions and 768 deletions

View File

@ -13961,6 +13961,8 @@ Sorry for the inconvenience.";
"PeerInfo.Gifts.ToastUnpinned.Text" = "Gift Unpinned.";
"PeerInfo.Gifts.ToastPinned.Title" = "Gift Pinned";
"PeerInfo.Gifts.ToastPinned.Text" = "Now it will always be shown on the top.";
"PeerInfo.Gifts.ToastPinned.TitleNew" = "%@ Pinned";
"PeerInfo.Gifts.ToastPinned.ReplacingText" = "replacing %@.";
"Chat.PaidMessage.Confirm.Title" = "Confirm Payment";
"Chat.PaidMessage.Confirm.DontAskAgain" = "Don't ask again";
@ -14034,3 +14036,56 @@ 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 >]()";
"Privacy.Gifts.ShowGiftButton" = "Show Gift Icon in Chats";
"Privacy.Gifts.ShowGiftButtonInfo" = "Display the #Gift icon in the message input field for both participants in all chats.";
"Privacy.Gifts.AcceptedTypes" = "ACCEPTED GIFT TYPES";
"Privacy.Gifts.AcceptedTypes.Unlimited" = "Unlimited";
"Privacy.Gifts.AcceptedTypes.Limited" = "Limited-Edition";
"Privacy.Gifts.AcceptedTypes.Unique" = "Unique";
"Privacy.Gifts.AcceptedTypes.Premium" = "Premium Subscriptions";
"Privacy.Gifts.AcceptedTypes.Info" = "Choose the types of gifts that you allow others to send you.";
"Chat.PanelFrozenAccount.Title" = "You account is frozen";
"Chat.PanelFrozenAccount.Text" = "Tap to view details";
"FrozenAccount.Title" = "Your Account is Frozen";
"FrozenAccount.Violation.Title" = "Violation of Terms";
"FrozenAccount.Violation.Text" = "Your account was frozen for breaking Telegram's Terms and Conditions.";
"FrozenAccount.ReadOnly.Title" = "Read-Only Mode";
"FrozenAccount.ReadOnly.Text" = "You can access your account but can't send messages or take actions.";
"FrozenAccount.Appeal.Title" = "Appeal Before Deactivation";
"FrozenAccount.Appeal.Text" = "Appeal via [@SpamBot]() before %@, or your account will be deleted.";
"FrozenAccount.SubmitAppeal" = "Submit an Appeal";
"FrozenAccount.Understood" = "Understood";
"AdsInfo.Search.Respect.Text" = "Ads like this do not use your personal information and are based on the search query you entered.";
"AdsInfo.Search.Ads.Text" = "You can turn off ads by subscribing to [Telegram Premium]().";
"AdsInfo.Search.Launch.Text" = "Anyone can create an ad to display in search results for any query. Check out the Telegram Ad Platform for details. [Learn More >]()";
"ChatList.FrozenAccount.Title" = "Your account is frozen";
"ChatList.FrozenAccount.Text" = "Tap to view details and submit an appeal.";

View File

@ -11,6 +11,7 @@ public final class BotCheckoutController: ViewController {
public final class InputData {
public enum FetchError {
case generic
case disallowedStarGifts
}
public let form: BotPaymentForm
@ -53,8 +54,13 @@ public final class BotCheckoutController: ViewController {
}
return context.engine.payments.fetchBotPaymentForm(source: source, themeParams: themeParams)
|> mapError { _ -> FetchError in
return .generic
|> mapError { error -> FetchError in
switch error {
case .disallowedStarGift:
return .disallowedStarGifts
default:
return .generic
}
}
|> mapToSignal { paymentForm -> Signal<InputData, FetchError> in
let botPeer: Signal<EnginePeer?, FetchError>

View File

@ -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",

View File

@ -54,6 +54,7 @@ import OldChannelsController
import TextFormat
import AvatarUploadToastScreen
import AdsInfoScreen
import AdsReportScreen
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
let controller: ViewController
@ -3935,13 +3936,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
if resetCurrentEntry {
strongSelf.selectTab(id: selectedEntryId)
strongSelf.selectTab(id: selectedEntryId, switchToChatsIfNeeded: false)
}
}))
}
private func selectTab(id: ChatListFilterTabEntryId) {
if self.parent == nil {
private func selectTab(id: ChatListFilterTabEntryId, switchToChatsIfNeeded: Bool = true) {
if self.parent == nil, switchToChatsIfNeeded {
if let navigationController = self.context.sharedContext.mainWindow?.viewController as? NavigationController {
for controller in navigationController.viewControllers {
if let controller = controller as? TabBarController {
@ -6131,7 +6132,7 @@ 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
@ -6139,7 +6140,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
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,34 +6155,42 @@ 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
actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_AboutAd, 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)
}, iconSource: nil, action: { [weak self] _, f in
f(.default)
@ -6190,70 +6199,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)
@ -6310,6 +6308,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
var isStoryPostingAvailable: Bool {
guard !self.context.isFrozen else {
return false
}
switch self.storyPostingAvailability {
case .enabled:
return true

View File

@ -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

View File

@ -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<EngineMessage.Id>?, 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<EngineMessage.Id>?, 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

View File

@ -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,52 @@ 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)
context.engine.messages.markAdAction(opaqueId: peer.opaqueId, media: false, fullscreen: 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 +1006,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 +1066,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 +1083,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 +1854,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 +2414,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 +2729,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 +2745,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 +2950,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 +3030,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}
}
}
for peer in foundRemotePeers.0 {
if case .expand = localExpandType, numberOfLocalPeers >= 3 {
break
@ -2978,6 +3046,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 +3062,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 +3083,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}
var firstHeaderId: Int64?
if !foundRemotePeers.2 {
if !foundRemotePeers.3 {
index = 0
var existingPostIds = Set<MessageId>()
for foundPublicMessageSet in foundPublicMessages.0 {
@ -3279,8 +3349,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 +3480,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 +3521,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 +5333,7 @@ public final class ChatListSearchShimmerNode: ASDisplayNode {
}, editPeer: { _ in
}, openWebApp: { _ in
}, openPhotoSetup: {
}, openAdInfo: { _ in
}, openAdInfo: { _, _ in
}, openAccountFreezeInfo: {
})
var isInlineMode = false

View File

@ -161,7 +161,7 @@ public final class ChatListShimmerNode: ASDisplayNode {
}, editPeer: { _ in
}, openWebApp: { _ in
}, openPhotoSetup: {
}, openAdInfo: { _ in
}, openAdInfo: { _, _ in
}, openAccountFreezeInfo: {
})
interaction.isInlineMode = isInlineMode

View File

@ -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?()
})

View File

@ -289,9 +289,8 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
textString = NSAttributedString(string: item.strings.ChatList_AddPhoto_Text, font: smallTextFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
avatarPeer = accountPeer
case .accountFreeze:
//TODO:localize
titleString = NSAttributedString(string: "Your account is frozen", font: titleFont, textColor: item.theme.list.itemDestructiveColor)
textString = NSAttributedString(string: "Tap to view details and submit an appeal.", font: smallTextFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
titleString = NSAttributedString(string: item.strings.ChatList_FrozenAccount_Title, font: titleFont, textColor: item.theme.list.itemDestructiveColor)
textString = NSAttributedString(string: item.strings.ChatList_FrozenAccount_Text, font: smallTextFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
}
var leftInset: CGFloat = sideInset

View File

@ -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,

View File

@ -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,

View File

@ -36,12 +36,13 @@ public class ItemListCheckboxItem: ListViewItem, ItemListItem {
let color: ItemListCheckboxItemColor
let textColor: TextColor
let checked: Bool
let enabled: Bool
let zeroSeparatorInsets: Bool
public let sectionId: ItemListSectionId
let action: () -> Void
let deleteAction: (() -> Void)?
public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, iconSize: CGSize? = nil, iconPlacement: IconPlacement = .default, title: String, subtitle: String? = nil, style: ItemListCheckboxItemStyle, color: ItemListCheckboxItemColor = .accent, textColor: TextColor = .primary, checked: Bool, zeroSeparatorInsets: Bool, sectionId: ItemListSectionId, action: @escaping () -> Void, deleteAction: (() -> Void)? = nil) {
public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, iconSize: CGSize? = nil, iconPlacement: IconPlacement = .default, title: String, subtitle: String? = nil, style: ItemListCheckboxItemStyle, color: ItemListCheckboxItemColor = .accent, textColor: TextColor = .primary, checked: Bool, enabled: Bool = true, zeroSeparatorInsets: Bool, sectionId: ItemListSectionId, action: @escaping () -> Void, deleteAction: (() -> Void)? = nil) {
self.presentationData = presentationData
self.icon = icon
self.iconSize = iconSize
@ -52,6 +53,7 @@ public class ItemListCheckboxItem: ListViewItem, ItemListItem {
self.color = color
self.textColor = textColor
self.checked = checked
self.enabled = enabled
self.zeroSeparatorInsets = zeroSeparatorInsets
self.sectionId = sectionId
self.action = action
@ -95,7 +97,9 @@ public class ItemListCheckboxItem: ListViewItem, ItemListItem {
public func selected(listView: ListView){
listView.clearHighlightAnimated(true)
self.action()
if self.enabled {
self.action()
}
}
}
@ -209,7 +213,7 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize)
let subtitleFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 15.0 / 17.0))
let titleColor: UIColor
var titleColor: UIColor
let subtitleColor: UIColor = item.presentationData.theme.list.itemSecondaryTextColor
switch item.textColor {
case .primary:
@ -217,6 +221,9 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
case .accent:
titleColor = item.presentationData.theme.list.itemAccentColor
}
if !item.enabled {
titleColor = item.presentationData.theme.list.itemDisabledTextColor
}
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 28.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
@ -239,12 +246,16 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
updatedTheme = item.presentationData.theme
}
if currentItem?.presentationData.theme !== item.presentationData.theme || currentItem?.color != item.color {
switch item.color {
case .accent:
updateCheckImage = PresentationResourcesItemList.checkIconImage(item.presentationData.theme)
case .secondary:
updateCheckImage = PresentationResourcesItemList.secondaryCheckIconImage(item.presentationData.theme)
if currentItem?.presentationData.theme !== item.presentationData.theme || currentItem?.color != item.color || currentItem?.enabled != item.enabled {
if !item.enabled {
updateCheckImage = PresentationResourcesItemList.disabledCheckIconImage(item.presentationData.theme)
} else {
switch item.color {
case .accent:
updateCheckImage = PresentationResourcesItemList.checkIconImage(item.presentationData.theme)
case .secondary:
updateCheckImage = PresentationResourcesItemList.secondaryCheckIconImage(item.presentationData.theme)
}
}
}
@ -261,6 +272,11 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
} else {
strongSelf.activateArea.accessibilityValue = ""
}
if item.enabled {
strongSelf.activateArea.accessibilityTraits = []
} else {
strongSelf.activateArea.accessibilityTraits = [.notEnabled]
}
strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height))
@ -368,7 +384,7 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
override public func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) {
super.setHighlighted(highlighted, at: point, animated: animated)
if highlighted {
if highlighted && (self.item?.enabled ?? false) {
self.highlightedBackgroundNode.alpha = 1.0
if self.highlightedBackgroundNode.supernode == nil {
var anchorNode: ASDisplayNode?

View File

@ -139,7 +139,7 @@ public func retry<T, E>(_ delayIncrement: Double, maxDelay: Double, onQueue queu
}
}
public func retry<T, E>(retryOnError: @escaping (E) -> Bool, delayIncrement: Double, maxDelay: Double, maxRetries: Int, onQueue queue: Queue) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
public func retry<T, E>(retryOnError: @escaping (E) -> Bool, delayIncrement: Double, maxDelay: Double, maxRetries: Int?, onQueue queue: Queue) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal { subscriber in
let shouldRetry = Atomic(value: true)
@ -161,7 +161,7 @@ public func retry<T, E>(retryOnError: @escaping (E) -> Bool, delayIncrement: Dou
return (min(maxDelay, value + delayIncrement), count + 1)
}
if count >= maxRetries {
if let maxRetries, count >= maxRetries {
subscriber.putError(error)
} else {
let time: DispatchTime = DispatchTime.now() + Double(delay)

View File

@ -141,13 +141,13 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
case forwardsPreview(PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationChatBubbleCorners, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, String, Bool, String)
case birthdayHeader(PresentationTheme, String)
case settingHeader(PresentationTheme, String)
case everybody(PresentationTheme, String, Bool, Bool)
case contacts(PresentationTheme, String, Bool, Bool)
case nobody(PresentationTheme, String, Bool, Bool)
case everybody(PresentationTheme, String, Bool, Bool, Bool)
case contacts(PresentationTheme, String, Bool, Bool, Bool)
case nobody(PresentationTheme, String, Bool, Bool, Bool)
case settingInfo(PresentationTheme, String, String)
case exceptionsHeader(PresentationTheme, String)
case disableFor(PresentationTheme, String, String)
case enableFor(PresentationTheme, String, String)
case disableFor(PresentationTheme, String, String, Bool)
case enableFor(PresentationTheme, String, String, Bool)
case peersInfo(PresentationTheme, String)
case callsP2PHeader(PresentationTheme, String)
case callsP2PAlways(PresentationTheme, String, Bool)
@ -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
@ -211,86 +212,88 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
var stableId: Int32 {
switch self {
case .forwardsPreviewHeader:
return 0
case .forwardsPreview:
return 1
case .birthdayHeader:
return 2
case .settingHeader:
return 3
case .everybody:
return 4
case .contacts:
return 5
case .nobody:
return 6
case .settingInfo:
return 7
case .phoneDiscoveryHeader:
return 8
case .phoneDiscoveryEverybody:
return 9
case .phoneDiscoveryMyContacts:
return 10
case .phoneDiscoveryInfo:
return 11
case .exceptionsHeader:
return 12
case .disableFor:
return 13
case .enableFor:
return 14
case .peersInfo:
return 15
case .callsP2PHeader:
return 16
case .callsP2PAlways:
return 17
case .callsP2PContacts:
return 18
case .callsP2PNever:
return 19
case .callsP2PInfo:
return 20
case .callsP2PDisableFor:
return 21
case .callsP2PEnableFor:
return 22
case .callsP2PPeersInfo:
return 23
case .callsIntegrationEnabled:
return 24
case .callsIntegrationInfo:
return 25
case .setPublicPhoto:
return 26
case .removePublicPhoto:
return 27
case .publicPhotoInfo:
return 28
case .hideReadTime:
return 29
case .hideReadTimeInfo:
return 30
case .subscribeToPremium:
return 31
case .subscribeToPremiumInfo:
return 32
case .disallowedGiftsHeader:
return 33
case .disallowedGiftsUnlimited:
return 34
case .disallowedGiftsLimited:
return 35
case .disallowedGiftsUnique:
return 36
case .disallowedGiftsInfo:
return 37
case .showGiftButton:
return 38
return 0
case .showGiftButtonInfo:
return 1
case .forwardsPreviewHeader:
return 2
case .forwardsPreview:
return 3
case .birthdayHeader:
return 4
case .settingHeader:
return 5
case .everybody:
return 6
case .contacts:
return 7
case .nobody:
return 8
case .settingInfo:
return 9
case .phoneDiscoveryHeader:
return 10
case .phoneDiscoveryEverybody:
return 11
case .phoneDiscoveryMyContacts:
return 12
case .phoneDiscoveryInfo:
return 13
case .exceptionsHeader:
return 14
case .disableFor:
return 15
case .enableFor:
return 16
case .peersInfo:
return 17
case .callsP2PHeader:
return 18
case .callsP2PAlways:
return 19
case .callsP2PContacts:
return 20
case .callsP2PNever:
return 21
case .callsP2PInfo:
return 22
case .callsP2PDisableFor:
return 23
case .callsP2PEnableFor:
return 24
case .callsP2PPeersInfo:
return 25
case .callsIntegrationEnabled:
return 26
case .callsIntegrationInfo:
return 27
case .setPublicPhoto:
return 28
case .removePublicPhoto:
return 29
case .publicPhotoInfo:
return 30
case .hideReadTime:
return 31
case .hideReadTimeInfo:
return 32
case .subscribeToPremium:
return 33
case .subscribeToPremiumInfo:
return 34
case .disallowedGiftsHeader:
return 35
case .disallowedGiftsUnlimited:
return 36
case .disallowedGiftsLimited:
return 37
case .disallowedGiftsUnique:
return 38
case .disallowedGiftsPremium:
return 39
case .disallowedGiftsInfo:
return 40
}
}
@ -320,20 +323,20 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
} else {
return false
}
case let .everybody(lhsTheme, lhsText, lhsValue, lhsIsLocked):
if case let .everybody(rhsTheme, rhsText, rhsValue, rhsIsLocked) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsIsLocked == rhsIsLocked {
case let .everybody(lhsTheme, lhsText, lhsValue, lhsIsLocked, lhsEnabled):
if case let .everybody(rhsTheme, rhsText, rhsValue, rhsIsLocked, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsIsLocked == rhsIsLocked, lhsEnabled == rhsEnabled {
return true
} else {
return false
}
case let .contacts(lhsTheme, lhsText, lhsValue, lhsIsLocked):
if case let .contacts(rhsTheme, rhsText, rhsValue, rhsIsLocked) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsIsLocked == rhsIsLocked {
case let .contacts(lhsTheme, lhsText, lhsValue, lhsIsLocked, lhsEnabled):
if case let .contacts(rhsTheme, rhsText, rhsValue, rhsIsLocked, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsIsLocked == rhsIsLocked, lhsEnabled == rhsEnabled {
return true
} else {
return false
}
case let .nobody(lhsTheme, lhsText, lhsValue, lhsIsLocked):
if case let .nobody(rhsTheme, rhsText, rhsValue, rhsIsLocked) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsIsLocked == rhsIsLocked {
case let .nobody(lhsTheme, lhsText, lhsValue, lhsIsLocked, lhsEnabled):
if case let .nobody(rhsTheme, rhsText, rhsValue, rhsIsLocked, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsIsLocked == rhsIsLocked, lhsEnabled == rhsEnabled {
return true
} else {
return false
@ -350,14 +353,14 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
} else {
return false
}
case let .disableFor(lhsTheme, lhsText, lhsValue):
if case let .disableFor(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
case let .disableFor(lhsTheme, lhsText, lhsValue, lhsEnabled):
if case let .disableFor(rhsTheme, rhsText, rhsValue, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsEnabled == rhsEnabled {
return true
} else {
return false
}
case let .enableFor(lhsTheme, lhsText, lhsValue):
if case let .enableFor(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
case let .enableFor(lhsTheme, lhsText, lhsValue, lhsEnabled):
if case let .enableFor(rhsTheme, rhsText, rhsValue, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsEnabled == rhsEnabled {
return true
} else {
return false
@ -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
@ -556,23 +565,23 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
})
case let .settingHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, multiline: true, sectionId: self.section)
case let .everybody(_, text, value, isLocked):
return ItemListCheckboxItem(presentationData: presentationData, icon: !isLocked ? nil : generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/Lock"), color: presentationData.theme.list.itemSecondaryTextColor), iconPlacement: .check, title: text, style: .left, checked: value && !isLocked, zeroSeparatorInsets: false, sectionId: self.section, action: {
case let .everybody(_, text, value, isLocked, isEnabled):
return ItemListCheckboxItem(presentationData: presentationData, icon: !isLocked ? nil : generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/Lock"), color: presentationData.theme.list.itemSecondaryTextColor), iconPlacement: .check, title: text, style: .left, checked: value && !isLocked, enabled: isEnabled, zeroSeparatorInsets: false, sectionId: self.section, action: {
if isLocked {
} else {
arguments.updateType(.everybody)
}
})
case let .contacts(_, text, value, isLocked):
return ItemListCheckboxItem(presentationData: presentationData, icon: !isLocked ? nil : generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/Lock"), color: presentationData.theme.list.itemSecondaryTextColor), iconPlacement: .check, title: text, style: .left, checked: value && !isLocked, zeroSeparatorInsets: false, sectionId: self.section, action: {
case let .contacts(_, text, value, isLocked, isEnabled):
return ItemListCheckboxItem(presentationData: presentationData, icon: !isLocked ? nil : generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/Lock"), color: presentationData.theme.list.itemSecondaryTextColor), iconPlacement: .check, title: text, style: .left, checked: value && !isLocked, enabled: isEnabled, zeroSeparatorInsets: false, sectionId: self.section, action: {
if isLocked {
arguments.displayLockedInfo()
} else {
arguments.updateType(.contacts)
}
})
case let .nobody(_, text, value, isLocked):
return ItemListCheckboxItem(presentationData: presentationData, icon: !isLocked ? nil : generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/Lock"), color: presentationData.theme.list.itemSecondaryTextColor), iconPlacement: .check, title: text, style: .left, checked: value && !isLocked, zeroSeparatorInsets: false, sectionId: self.section, action: {
case let .nobody(_, text, value, isLocked, isEnabled):
return ItemListCheckboxItem(presentationData: presentationData, icon: !isLocked ? nil : generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/Lock"), color: presentationData.theme.list.itemSecondaryTextColor), iconPlacement: .check, title: text, style: .left, checked: value && !isLocked, enabled: isEnabled, zeroSeparatorInsets: false, sectionId: self.section, action: {
if isLocked {
arguments.displayLockedInfo()
} else {
@ -585,12 +594,12 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
})
case let .exceptionsHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .disableFor(_, title, value):
return ItemListDisclosureItem(presentationData: presentationData, title: title, label: value, sectionId: self.section, style: .blocks, action: {
arguments.openSelective(.main, false)
})
case let .enableFor(_, title, value):
return ItemListDisclosureItem(presentationData: presentationData, title: title, label: value, sectionId: self.section, style: .blocks, action: {
case let .disableFor(_, title, value, isEnabled):
return ItemListDisclosureItem(presentationData: presentationData, title: title, enabled: isEnabled, label: value, sectionId: self.section, style: .blocks, action: {
arguments.openSelective(.main, false)
})
case let .enableFor(_, title, value, isEnabled):
return ItemListDisclosureItem(presentationData: presentationData, title: title, enabled: isEnabled, label: value, sectionId: self.section, style: .blocks, action: {
arguments.openSelective(.main, true)
})
case let .peersInfo(_, text):
@ -696,18 +705,30 @@ 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: {
if available {
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: "#") {
@ -979,6 +1000,16 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
enableForText = presentationData.strings.Privacy_GroupsAndChannels_AlwaysAllow
}
var permisisonsEnabled = true
if case .giftsAutoSave = kind {
entries.append(.showGiftButton(presentationData.theme, presentationData.strings.Privacy_Gifts_ShowGiftButton, !isPremium, state.showGiftButton == true, true))
entries.append(.showGiftButtonInfo(presentationData.theme, presentationData.strings.Privacy_Gifts_ShowGiftButtonInfo.replacingOccurrences(of: "#", with: " # ")))
if state.disallowedGifts == TelegramDisallowedGifts.All {
permisisonsEnabled = false
}
}
if case .forwards = kind {
let linkEnabled: Bool
let tootipText: String
@ -1004,13 +1035,13 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
entries.append(.settingHeader(presentationData.theme, settingTitle))
if case .voiceMessages = kind {
entries.append(.everybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.setting == .everybody || !isPremium, false))
entries.append(.contacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.setting == .contacts && isPremium, !isPremium))
entries.append(.nobody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenNobody, state.setting == .nobody && isPremium, !isPremium))
entries.append(.everybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.setting == .everybody || !isPremium, false, permisisonsEnabled))
entries.append(.contacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.setting == .contacts && isPremium, !isPremium, permisisonsEnabled))
entries.append(.nobody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenNobody, state.setting == .nobody && isPremium, !isPremium, permisisonsEnabled))
} else {
entries.append(.everybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.setting == .everybody, false))
entries.append(.contacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.setting == .contacts, false))
entries.append(.nobody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenNobody, state.setting == .nobody, false))
entries.append(.everybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.setting == .everybody, false, permisisonsEnabled))
entries.append(.contacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.setting == .contacts, false, permisisonsEnabled))
entries.append(.nobody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenNobody, state.setting == .nobody, false, permisisonsEnabled))
}
if let settingInfoText = settingInfoText {
@ -1033,12 +1064,12 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
switch state.setting {
case .everybody:
entries.append(.disableFor(presentationData.theme, disableForText, stringForUserCount(state.disableFor, enableForPremium: false, enableForBots: false, strings: presentationData.strings)))
entries.append(.disableFor(presentationData.theme, disableForText, stringForUserCount(state.disableFor, enableForPremium: false, enableForBots: false, strings: presentationData.strings), permisisonsEnabled))
case .contacts:
entries.append(.disableFor(presentationData.theme, disableForText, stringForUserCount(state.disableFor, enableForPremium: false, enableForBots: false, strings: presentationData.strings)))
entries.append(.enableFor(presentationData.theme, enableForText, stringForUserCount(state.enableFor, enableForPremium: state.enableForPremium, enableForBots: state.enableForBots, strings: presentationData.strings)))
entries.append(.disableFor(presentationData.theme, disableForText, stringForUserCount(state.disableFor, enableForPremium: false, enableForBots: false, strings: presentationData.strings), permisisonsEnabled))
entries.append(.enableFor(presentationData.theme, enableForText, stringForUserCount(state.enableFor, enableForPremium: state.enableForPremium, enableForBots: state.enableForBots, strings: presentationData.strings), permisisonsEnabled))
case .nobody:
entries.append(.enableFor(presentationData.theme, enableForText, stringForUserCount(state.enableFor, enableForPremium: state.enableForPremium, enableForBots: state.enableForBots, strings: presentationData.strings)))
entries.append(.enableFor(presentationData.theme, enableForText, stringForUserCount(state.enableFor, enableForPremium: state.enableForPremium, enableForBots: state.enableForBots, strings: presentationData.strings), permisisonsEnabled))
}
let exceptionsInfo: String
if case .profilePhoto = kind {
@ -1120,14 +1151,12 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
}
if case .giftsAutoSave = kind {
//TODO:localize
entries.append(.disallowedGiftsHeader(presentationData.theme, "ACCEPTED GIFT TYPES"))
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(.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(.showGiftButtonInfo(presentationData.theme, "Display the # Gift icon in the message input field for both participants in all chats."))
entries.append(.disallowedGiftsHeader(presentationData.theme, presentationData.strings.Privacy_Gifts_AcceptedTypes.uppercased()))
entries.append(.disallowedGiftsUnlimited(presentationData.theme, presentationData.strings.Privacy_Gifts_AcceptedTypes_Unlimited, !isPremium, !(state.disallowedGifts?.contains(.unlimited) ?? false)))
entries.append(.disallowedGiftsLimited(presentationData.theme, presentationData.strings.Privacy_Gifts_AcceptedTypes_Limited, !isPremium, !(state.disallowedGifts?.contains(.limited) ?? false)))
entries.append(.disallowedGiftsUnique(presentationData.theme, presentationData.strings.Privacy_Gifts_AcceptedTypes_Unique, !isPremium, !(state.disallowedGifts?.contains(.unique) ?? false)))
entries.append(.disallowedGiftsPremium(presentationData.theme, presentationData.strings.Privacy_Gifts_AcceptedTypes_Premium, !isPremium, !(state.disallowedGifts?.contains(.premium) ?? false)))
entries.append(.disallowedGiftsInfo(presentationData.theme, presentationData.strings.Privacy_Gifts_AcceptedTypes_Info))
}
return entries
@ -1603,6 +1632,12 @@ public func selectivePrivacySettingsController(
} else {
updatedDisallowedGifts.remove(.unique)
}
case .premium:
if value {
updatedDisallowedGifts.insert(.premium)
} else {
updatedDisallowedGifts.remove(.premium)
}
default:
break
}
@ -1713,7 +1748,11 @@ public func selectivePrivacySettingsController(
disallowedGifts = value
}
if let value = state.showGiftButton {
showGiftButton = value
if disallowedGifts != TelegramDisallowedGifts.All {
showGiftButton = value
} else {
showGiftButton = false
}
}
}

View File

@ -232,7 +232,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, ASScrollView
}, editPeer: { _ in
}, openWebApp: { _ in
}, openPhotoSetup: {
}, openAdInfo: { _ in
}, openAdInfo: { _, _ in
}, openAccountFreezeInfo: {
})

View File

@ -381,7 +381,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, ASScrollViewDelegate {
}, editPeer: { _ in
}, openWebApp: { _ in
}, openPhotoSetup: {
}, openAdInfo: { _ in
}, openAdInfo: { _, _ in
}, openAccountFreezeInfo: {
})

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1224,6 +1224,20 @@ public func retryRequest<T>(signal: Signal<T, MTRpcError>) -> Signal<T, NoError>
|> retry(0.2, maxDelay: 5.0, onQueue: Queue.concurrentDefaultQueue())
}
public func retryRequestIfNotFrozen<T>(signal: Signal<T, MTRpcError>) -> Signal<T?, NoError> {
return signal
|> retry(retryOnError: { error in
if error.errorDescription == "FROZEN_METHOD_INVALID" {
return false
}
return true
}, delayIncrement: 0.2, maxDelay: 5.0, maxRetries: nil, onQueue: .concurrentDefaultQueue())
|> map(Optional.init)
|> `catch` { _ in
return .single(nil)
}
}
class Keychain: NSObject, MTKeychain {
let get: (String) -> Data?
let set: (String, Data) -> Void

View File

@ -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)

View File

@ -241,8 +241,7 @@ func fetchChatList(accountPeerId: PeerId, postbox: Postbox, network: Network, lo
folderId = groupId.rawValue
}
additionalPinnedChats = network.request(Api.functions.messages.getPinnedDialogs(folderId: folderId))
|> retryRequest
|> map(Optional.init)
|> retryRequestIfNotFrozen
} else {
additionalPinnedChats = .single(nil)
}

View File

@ -8,8 +8,11 @@ import MtProtoKit
func managedAutodownloadSettingsUpdates(accountManager: AccountManager<TelegramAccountManagerTypes>, network: Network) -> Signal<Void, NoError> {
let poll = Signal<Void, NoError> { subscriber in
return (network.request(Api.functions.account.getAutoDownloadSettings())
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<Void, NoError> in
guard let result else {
return .complete()
}
return updateAutodownloadSettingsInteractively(accountManager: accountManager, { _ -> AutodownloadSettings in
return AutodownloadSettings(apiAutodownloadSettings: result)
})

View File

@ -9,7 +9,7 @@ func managedConfigurationUpdates(accountManager: AccountManager<TelegramAccountM
let poll = Signal<Void, NoError> { subscriber in
return (combineLatest(
network.request(Api.functions.help.getConfig()) |> retryRequest,
network.request(Api.functions.messages.getDefaultHistoryTTL()) |> retryRequest
network.request(Api.functions.messages.getDefaultHistoryTTL()) |> retryRequestIfNotFrozen
)
|> mapToSignal { result, defaultHistoryTtl -> Signal<Void, NoError> in
return postbox.transaction { transaction -> Signal<Void, NoError> in
@ -83,6 +83,8 @@ func managedConfigurationUpdates(accountManager: AccountManager<TelegramAccountM
} else {
messageAutoremoveSeconds = nil
}
default:
messageAutoremoveSeconds = nil
}
updateGlobalMessageAutoremoveTimeoutSettings(transaction: transaction, { settings in
var settings = settings

View File

@ -21,7 +21,7 @@ public func updateGlobalNotificationSettingsInteractively(postbox: Postbox, _ f:
public func resetPeerNotificationSettings(network: Network) -> Signal<Void, NoError> {
return network.request(Api.functions.account.resetNotifySettings())
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { _ in return Signal<Void, NoError>.complete() }
}
@ -113,8 +113,11 @@ private func fetchedNotificationSettings(network: Network) -> Signal<GlobalNotif
let reactions = network.request(Api.functions.account.getReactionsNotifySettings())
return combineLatest(chats, users, channels, contactsJoinedMuted, reactions)
|> retryRequest
|> map { chats, users, channels, contactsJoinedMuted, reactions in
|> retryRequestIfNotFrozen
|> mapToSignal { data in
guard let (chats, users, channels, contactsJoinedMuted, reactions) = data else {
return .complete()
}
let chatsSettings: MessageNotificationSettings
switch chats {
case let .peerNotifySettings(_, showPreviews, _, muteUntil, iosSound, _, desktopSound, storiesMuted, storiesHideSender, storiesIosSound, _, storiesDesktopSound):
@ -306,7 +309,7 @@ private func fetchedNotificationSettings(network: Network) -> Signal<GlobalNotif
)
}
return GlobalNotificationSettingsSet(privateChats: userSettings, groupChats: chatsSettings, channels: channelSettings, reactionSettings: reactionSettings, contactsJoined: contactsJoinedMuted == .boolFalse)
return .single(GlobalNotificationSettingsSet(privateChats: userSettings, groupChats: chatsSettings, channels: channelSettings, reactionSettings: reactionSettings, contactsJoined: contactsJoinedMuted == .boolFalse))
}
}

View File

@ -45,8 +45,11 @@ private func managedRecentMedia(postbox: Postbox, network: Network, collectionId
func managedRecentStickers(postbox: Postbox, network: Network, forceFetch: Bool = false) -> Signal<Void, NoError> {
return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentStickers, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: forceFetch, fetch: { hash in
return network.request(Api.functions.messages.getRecentStickers(flags: 0, hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .recentStickersNotModified:
return .single(nil)
@ -68,8 +71,11 @@ func managedRecentStickers(postbox: Postbox, network: Network, forceFetch: Bool
func managedRecentGifs(postbox: Postbox, network: Network, forceFetch: Bool = false) -> Signal<Void, NoError> {
return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentGifs, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: forceFetch, fetch: { hash in
return network.request(Api.functions.messages.getSavedGifs(hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .savedGifsNotModified:
return .single(nil)
@ -91,8 +97,11 @@ func managedRecentGifs(postbox: Postbox, network: Network, forceFetch: Bool = fa
func managedSavedStickers(postbox: Postbox, network: Network, forceFetch: Bool = false) -> Signal<Void, NoError> {
return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudSavedStickers, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: true, forceFetch: forceFetch, fetch: { hash in
return network.request(Api.functions.messages.getFavedStickers(hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .favedStickersNotModified:
return .single(nil)
@ -133,8 +142,11 @@ func managedSavedStickers(postbox: Postbox, network: Network, forceFetch: Bool =
func managedGreetingStickers(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudGreetingStickers, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.messages.getStickers(emoticon: "👋⭐️", hash: 0))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .stickersNotModified:
return .single(nil)
@ -157,8 +169,11 @@ func managedGreetingStickers(postbox: Postbox, network: Network) -> Signal<Void,
func managedPremiumStickers(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudPremiumStickers, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.messages.getStickers(emoticon: "⭐️⭐️", hash: 0))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .stickersNotModified:
return .single(nil)
@ -181,8 +196,11 @@ func managedPremiumStickers(postbox: Postbox, network: Network) -> Signal<Void,
func managedAllPremiumStickers(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudAllPremiumStickers, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.messages.getStickers(emoticon: "📂⭐️", hash: 0))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .stickersNotModified:
return .single(nil)
@ -205,8 +223,11 @@ func managedAllPremiumStickers(postbox: Postbox, network: Network) -> Signal<Voi
func managedRecentStatusEmoji(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentStatusEmoji, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.account.getRecentEmojiStatuses(hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .emojiStatusesNotModified:
return .single(nil)
@ -235,8 +256,11 @@ func managedRecentStatusEmoji(postbox: Postbox, network: Network) -> Signal<Void
func managedFeaturedStatusEmoji(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudFeaturedStatusEmoji, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.account.getDefaultEmojiStatuses(hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .emojiStatusesNotModified:
return .single(nil)
@ -265,8 +289,11 @@ func managedFeaturedStatusEmoji(postbox: Postbox, network: Network) -> Signal<Vo
func managedFeaturedChannelStatusEmoji(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudFeaturedChannelStatusEmoji, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.account.getChannelDefaultEmojiStatuses(hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .emojiStatusesNotModified:
return .single(nil)
@ -295,8 +322,11 @@ func managedFeaturedChannelStatusEmoji(postbox: Postbox, network: Network) -> Si
func managedUniqueStarGifts(accountPeerId: PeerId, postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudUniqueStarGifts, extractItemId: { RecentStarGiftItemId($0).id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.account.getCollectibleEmojiStatuses(hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .emojiStatusesNotModified:
return .single(nil)
@ -345,8 +375,11 @@ func managedUniqueStarGifts(accountPeerId: PeerId, postbox: Postbox, network: Ne
func managedProfilePhotoEmoji(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.account.getDefaultProfilePhotoEmojis(hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .emojiListNotModified:
return .single(nil)
@ -373,8 +406,11 @@ func managedProfilePhotoEmoji(postbox: Postbox, network: Network) -> Signal<Void
func managedGroupPhotoEmoji(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.account.getDefaultGroupPhotoEmojis(hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .emojiListNotModified:
return .single(nil)
@ -401,8 +437,11 @@ func managedGroupPhotoEmoji(postbox: Postbox, network: Network) -> Signal<Void,
func managedBackgroundIconEmoji(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudFeaturedBackgroundIconEmoji, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.account.getDefaultBackgroundEmojis(hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .emojiListNotModified:
return .single(nil)
@ -429,8 +468,11 @@ func managedBackgroundIconEmoji(postbox: Postbox, network: Network) -> Signal<Vo
func managedDisabledChannelStatusIconEmoji(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudDisabledChannelStatusEmoji, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.account.getChannelRestrictedStatusEmojis(hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .emojiListNotModified:
return .single(nil)
@ -466,8 +508,11 @@ func managedRecentReactions(postbox: Postbox, network: Network) -> Signal<Void,
}
}, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.messages.getRecentReactions(limit: 100, hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .reactionsNotModified:
return .single(nil)
@ -523,8 +568,11 @@ func managedTopReactions(postbox: Postbox, network: Network) -> Signal<Void, NoE
}
}, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.messages.getTopReactions(limit: 32, hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .reactionsNotModified:
return .single(nil)
@ -580,8 +628,11 @@ func managedDefaultTagReactions(postbox: Postbox, network: Network) -> Signal<Vo
}
}, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.messages.getDefaultTagReactions(hash: hash))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
guard let result else {
return .single(nil)
}
switch result {
case .reactionsNotModified:
return .single(nil)

View File

@ -139,6 +139,7 @@ private func hashForStickerPackInfos(_ infos: [StickerPackCollectionInfo]) -> In
private enum SynchronizeInstalledStickerPacksError {
case restart
case done
case frozen
}
private func fetchStickerPack(network: Network, info: StickerPackCollectionInfo) -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> {
@ -479,10 +480,13 @@ private func continueSynchronizeInstalledStickerPacks(transaction: Transaction,
}
let sequence = request
|> retryRequest
|> retryRequestIfNotFrozen
|> mapError { _ -> SynchronizeInstalledStickerPacksError in
}
|> mapToSignal { result -> Signal<Void, SynchronizeInstalledStickerPacksError> in
guard let result else {
return .fail(.frozen)
}
return postbox.transaction { transaction -> Signal<Void, SynchronizeInstalledStickerPacksError> in
let checkLocalCollectionInfos = transaction.getItemCollectionsInfos(namespace: collectionNamespace).map { $0.1 as! StickerPackCollectionInfo }
if checkLocalCollectionInfos != localCollectionInfos {
@ -714,6 +718,8 @@ private func continueSynchronizeInstalledStickerPacks(transaction: Transaction,
return ((sequence
|> `catch` { error -> Signal<Void, SynchronizeInstalledStickerPacksError> in
switch error {
case .frozen:
return .fail(.frozen)
case .done:
return .fail(.done)
case .restart:

View File

@ -129,8 +129,11 @@ private func synchronizePinnedChats(transaction: Transaction, postbox: Postbox,
}
return network.request(Api.functions.messages.getPinnedDialogs(folderId: groupId.rawValue))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { dialogs -> Signal<Void, NoError> in
guard let dialogs else {
return .complete()
}
return postbox.transaction { transaction -> Signal<Void, NoError> in
var storeMessages: [StoreMessage] = []
var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:]

View File

@ -172,10 +172,14 @@ private func requestStarsRevenueStats(postbox: Postbox, network: Network, peerId
}
return network.request(Api.functions.payments.getStarsRevenueStats(flags: flags, peer: inputPeer))
|> retryRequestIfNotFrozen
|> map { result -> StarsRevenueStats? in
guard let result else {
return nil
}
return StarsRevenueStats(apiStarsRevenueStats: result, peerId: peerId)
}
|> retryRequest
}
}

View File

@ -1000,7 +1000,7 @@ public final class CachedUserData: CachedPeerData {
public let starRefProgram: TelegramStarRefProgram?
public let verification: PeerVerification?
public let sendPaidMessageStars: StarsAmount?
public let disallowedGifts: TelegramDisallowedGifts
public let disallowedGifts: TelegramDisallowedGifts?
public let peerIds: Set<PeerId>
public let messageIds: Set<MessageId>
@ -1042,10 +1042,10 @@ public final class CachedUserData: CachedPeerData {
self.starRefProgram = nil
self.verification = nil
self.sendPaidMessageStars = nil
self.disallowedGifts = []
self.disallowedGifts = nil
}
public init(about: String?, botInfo: BotInfo?, editableBotInfo: EditableBotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, themeEmoticon: String?, photo: CachedPeerProfilePhoto, personalPhoto: CachedPeerProfilePhoto, fallbackPhoto: CachedPeerProfilePhoto, voiceMessagesAvailable: Bool, wallpaper: TelegramWallpaper?, flags: CachedUserFlags, businessHours: TelegramBusinessHours?, businessLocation: TelegramBusinessLocation?, greetingMessage: TelegramBusinessGreetingMessage?, awayMessage: TelegramBusinessAwayMessage?, connectedBot: TelegramAccountConnectedBot?, businessIntro: CachedTelegramBusinessIntro, birthday: TelegramBirthday?, personalChannel: CachedTelegramPersonalChannel, botPreview: BotPreview?, starGiftsCount: Int32?, starRefProgram: TelegramStarRefProgram?, verification: PeerVerification?, sendPaidMessageStars: StarsAmount?, disallowedGifts: TelegramDisallowedGifts) {
public init(about: String?, botInfo: BotInfo?, editableBotInfo: EditableBotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, themeEmoticon: String?, photo: CachedPeerProfilePhoto, personalPhoto: CachedPeerProfilePhoto, fallbackPhoto: CachedPeerProfilePhoto, voiceMessagesAvailable: Bool, wallpaper: TelegramWallpaper?, flags: CachedUserFlags, businessHours: TelegramBusinessHours?, businessLocation: TelegramBusinessLocation?, greetingMessage: TelegramBusinessGreetingMessage?, awayMessage: TelegramBusinessAwayMessage?, connectedBot: TelegramAccountConnectedBot?, businessIntro: CachedTelegramBusinessIntro, birthday: TelegramBirthday?, personalChannel: CachedTelegramPersonalChannel, botPreview: BotPreview?, starGiftsCount: Int32?, starRefProgram: TelegramStarRefProgram?, verification: PeerVerification?, sendPaidMessageStars: StarsAmount?, disallowedGifts: TelegramDisallowedGifts?) {
self.about = about
self.botInfo = botInfo
self.editableBotInfo = editableBotInfo
@ -1153,7 +1153,7 @@ public final class CachedUserData: CachedPeerData {
self.sendPaidMessageStars = decoder.decodeCodable(StarsAmount.self, forKey: "sendPaidMessageStars")
self.disallowedGifts = TelegramDisallowedGifts(rawValue: decoder.decodeInt32ForKey("disallowedGifts", orElse: 0))
self.disallowedGifts = decoder.decodeOptionalInt32ForKey("disallowedGifts").flatMap { TelegramDisallowedGifts(rawValue: $0) }
}
public func encode(_ encoder: PostboxEncoder) {
@ -1283,7 +1283,11 @@ public final class CachedUserData: CachedPeerData {
encoder.encodeNil(forKey: "sendPaidMessageStars")
}
encoder.encodeInt32(self.disallowedGifts.rawValue, forKey: "disallowedGifts")
if let disallowedGifts = self.disallowedGifts {
encoder.encodeInt32(disallowedGifts.rawValue, forKey: "disallowedGifts")
} else {
encoder.encodeNil(forKey: "disallowedGifts")
}
}
public func isEqual(to: CachedPeerData) -> Bool {

View File

@ -569,6 +569,75 @@ public extension TelegramEngine {
)
}
}
public func subscribe<
T0: TelegramEngineDataItem,
T1: TelegramEngineDataItem,
T2: TelegramEngineDataItem,
T3: TelegramEngineDataItem,
T4: TelegramEngineDataItem,
T5: TelegramEngineDataItem,
T6: TelegramEngineDataItem,
T7: TelegramEngineDataItem,
T8: TelegramEngineDataItem,
T9: TelegramEngineDataItem,
T10: TelegramEngineDataItem
>(
_ t0: T0,
_ t1: T1,
_ t2: T2,
_ t3: T3,
_ t4: T4,
_ t5: T5,
_ t6: T6,
_ t7: T7,
_ t8: T8,
_ t9: T9,
_ t10: T10
) -> Signal<
(
T0.Result,
T1.Result,
T2.Result,
T3.Result,
T4.Result,
T5.Result,
T6.Result,
T7.Result,
T8.Result,
T9.Result,
T10.Result
),
NoError> {
return self._subscribe(items: [
t0 as! AnyPostboxViewDataItem,
t1 as! AnyPostboxViewDataItem,
t2 as! AnyPostboxViewDataItem,
t3 as! AnyPostboxViewDataItem,
t4 as! AnyPostboxViewDataItem,
t5 as! AnyPostboxViewDataItem,
t6 as! AnyPostboxViewDataItem,
t7 as! AnyPostboxViewDataItem,
t8 as! AnyPostboxViewDataItem,
t9 as! AnyPostboxViewDataItem,
t10 as! AnyPostboxViewDataItem
])
|> map { results -> (T0.Result, T1.Result, T2.Result, T3.Result, T4.Result, T5.Result, T6.Result, T7.Result, T8.Result, T9.Result, T10.Result) in
return (
results[0] as! T0.Result,
results[1] as! T1.Result,
results[2] as! T2.Result,
results[3] as! T3.Result,
results[4] as! T4.Result,
results[5] as! T5.Result,
results[6] as! T6.Result,
results[7] as! T7.Result,
results[8] as! T8.Result,
results[9] as! T9.Result,
results[10] as! T10.Result
)
}
}
public func get<

View File

@ -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)
@ -616,3 +616,11 @@ func _internal_markAdAction(account: Account, peerId: EnginePeer.Id, opaqueId: D
|> ignoreValues
let _ = signal.start()
}
func _internal_markAsSeen(account: Account, opaqueId: Data) -> Signal<Never, NoError> {
return account.network.request(Api.functions.messages.viewSponsoredMessage(randomId: Buffer(data: opaqueId)))
|> `catch` { _ -> Signal<Api.Bool, NoError> in
return .single(.boolFalse)
}
|> ignoreValues
}

View File

@ -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")

View File

@ -19,7 +19,7 @@ public enum ReportAdMessageError {
case premiumRequired
}
func _internal_reportAdMessage(account: Account, peerId: EnginePeer.Id, opaqueId: Data, option: Data?) -> Signal<ReportAdMessageResult, ReportAdMessageError> {
func _internal_reportAdMessage(account: Account, opaqueId: Data, option: Data?) -> Signal<ReportAdMessageResult, ReportAdMessageError> {
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" {

View File

@ -1504,8 +1504,8 @@ public extension TelegramEngine {
}).startStandalone()
}
public func reportAdMessage(peerId: EnginePeer.Id, opaqueId: Data, option: Data?) -> Signal<ReportAdMessageResult, ReportAdMessageError> {
return _internal_reportAdMessage(account: self.account, peerId: peerId, opaqueId: opaqueId, option: option)
public func reportAdMessage(opaqueId: Data, option: Data?) -> Signal<ReportAdMessageResult, ReportAdMessageError> {
return _internal_reportAdMessage(account: self.account, opaqueId: opaqueId, option: option)
}
public func reportContent(subject: ReportContentSubject, option: Data?, message: String?) -> Signal<ReportContentResult, ReportContentError> {
@ -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> {

View File

@ -445,9 +445,12 @@ private func _internal_requestStarsSubscriptions(account: Account, peerId: Engin
flags |= (1 << 0)
}
return account.network.request(Api.functions.payments.getStarsSubscriptions(flags: flags, peer: inputPeer, offset: offset))
|> retryRequest
|> retryRequestIfNotFrozen
|> castError(RequestStarsSubscriptionsError.self)
|> mapToSignal { result -> Signal<InternalStarsStatus, RequestStarsSubscriptionsError> in
guard let result else {
return .single(InternalStarsStatus(balance: .zero, subscriptionsMissingBalance: nil, subscriptions: [], nextSubscriptionsOffset: nil, transactions: [], nextTransactionsOffset: nil))
}
return account.postbox.transaction { transaction -> InternalStarsStatus in
switch result {
case let .starsStatus(_, balance, subscriptions, subscriptionsNextOffset, subscriptionsMissingBalance, _, _, chats, users):

View File

@ -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 = parsedPeers.get(apiPeer.peerId) else {
continue
}
result.append(
AdPeer(
opaqueId: randomId.makeData(),
peer: EnginePeer(peer),
subscribers: subscribers[peer.id],
sponsorInfo: sponsorInfo,
additionalInfo: additionalInfo
)

View File

@ -548,8 +548,11 @@ func _internal_adminedPublicChannels(account: Account, scope: AdminedPublicChann
let accountPeerId = account.peerId
return account.network.request(Api.functions.channels.getAdminedPublicChannels(flags: flags))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[TelegramAdminedPublicChannel], NoError> in
guard let result else {
return .single([])
}
return account.postbox.transaction { transaction -> [TelegramAdminedPublicChannel] in
let chats: [Api.Chat]
var subscriberCounts: [PeerId: Int] = [:]
@ -675,8 +678,11 @@ func _internal_channelsForPublicReaction(account: Account, useLocalCache: Bool)
}
|> mapToSignal { cachedPeers in
let remote: Signal<[Peer], NoError> = account.network.request(Api.functions.channels.getAdminedPublicChannels(flags: 0))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[Peer], NoError> in
guard let result else {
return .single([])
}
return account.postbox.transaction { transaction -> [Peer] in
let chats: [Api.Chat]
let parsedPeers: AccumulatedPeers

View File

@ -114,8 +114,11 @@ func _internal_updateBirthday(account: Account, birthday: TelegramBirthday?) ->
func managedContactBirthdays(stateManager: AccountStateManager) -> Signal<Never, NoError> {
let poll = stateManager.network.request(Api.functions.contacts.getBirthdays())
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<Never, NoError> in
guard let result else {
return .complete()
}
return stateManager.postbox.transaction { transaction -> Void in
if case let .contactBirthdays(contactBirthdays, users) = result {
updatePeers(transaction: transaction, accountPeerId: stateManager.accountPeerId, peers: AccumulatedPeers(users: users))

View File

@ -84,35 +84,38 @@ func _internal_channelMembers(postbox: Postbox, network: Network, accountPeerId:
}
}
return network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: apiFilter, offset: offset, limit: limit, hash: hash))
|> retryRequest
|> mapToSignal { result -> Signal<[RenderedChannelParticipant]?, NoError> in
return postbox.transaction { transaction -> [RenderedChannelParticipant]? in
var items: [RenderedChannelParticipant] = []
switch result {
case let .channelParticipants(_, participants, chats, users):
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users)
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
var peers: [PeerId: Peer] = [:]
for id in parsedPeers.allIds {
if let peer = transaction.getPeer(id) {
peers[peer.id] = peer
}
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[RenderedChannelParticipant]?, NoError> in
guard let result else {
return .single(nil)
}
return postbox.transaction { transaction -> [RenderedChannelParticipant]? in
var items: [RenderedChannelParticipant] = []
switch result {
case let .channelParticipants(_, participants, chats, users):
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users)
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
var peers: [PeerId: Peer] = [:]
for id in parsedPeers.allIds {
if let peer = transaction.getPeer(id) {
peers[peer.id] = peer
}
for participant in CachedChannelParticipants(apiParticipants: participants).participants {
if let peer = parsedPeers.get(participant.peerId) {
var renderedPresences: [PeerId: PeerPresence] = [:]
if let presence = transaction.getPeerPresence(peerId: participant.peerId) {
renderedPresences[participant.peerId] = presence
}
items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presences: renderedPresences))
}
for participant in CachedChannelParticipants(apiParticipants: participants).participants {
if let peer = parsedPeers.get(participant.peerId) {
var renderedPresences: [PeerId: PeerPresence] = [:]
if let presence = transaction.getPeerPresence(peerId: participant.peerId) {
renderedPresences[participant.peerId] = presence
}
items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presences: renderedPresences))
}
case .channelParticipantsNotModified:
return nil
}
return items
}
case .channelParticipantsNotModified:
return nil
}
return items
}
}
} else {
return .single([])

View File

@ -27,8 +27,11 @@ func _internal_notificationExceptionsList(accountPeerId: PeerId, postbox: Postbo
}
return network.request(Api.functions.account.getNotifyExceptions(flags: flags, peer: nil))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<NotificationExceptionsList, NoError> in
guard let result else {
return .single(NotificationExceptionsList(peers: [:], settings: [:]))
}
return postbox.transaction { transaction -> NotificationExceptionsList in
switch result {
case let .updates(updates, users, chats, _, _):

View File

@ -151,7 +151,7 @@ func _internal_updateRecentPeersEnabled(postbox: Postbox, network: Network, enab
func _internal_managedRecentlyUsedInlineBots(postbox: Postbox, network: Network, accountPeerId: PeerId) -> Signal<Void, NoError> {
let remotePeers = network.request(Api.functions.contacts.getTopPeers(flags: 1 << 2, offset: 0, limit: 16, hash: 0))
|> retryRequest
|> retryRequestIfNotFrozen
|> map { result -> (AccumulatedPeers, [(PeerId, Double)])? in
switch result {
case .topPeersDisabled:
@ -174,6 +174,8 @@ func _internal_managedRecentlyUsedInlineBots(postbox: Postbox, network: Network,
return (parsedPeers, peersWithRating)
case .topPeersNotModified:
break
default:
break
}
return (AccumulatedPeers(), [])
}

View File

@ -1530,6 +1530,10 @@ public extension TelegramEngine {
return _internal_searchAdPeers(account: self.account, query: query)
}
public func markAsSeen(ad opaqueId: Data) -> Signal<Never, NoError> {
return _internal_markAsSeen(account: self.account, opaqueId: opaqueId)
}
public func isPremiumRequiredToContact(_ peerIds: [EnginePeer.Id]) -> Signal<[EnginePeer.Id: RequirementToContact], NoError> {
return _internal_updateIsPremiumRequiredToContact(account: self.account, peerIds: peerIds)
}

View File

@ -76,8 +76,11 @@ func fetchAndUpdateSupplementalCachedPeerData(peerId rawPeerId: PeerId, accountP
}
} else if let inputPeer = apiInputPeer(peer) {
return network.request(Api.functions.messages.getPeerSettings(peer: inputPeer))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { peerSettings -> Signal<Bool, NoError> in
guard let peerSettings else {
return .single(false)
}
return postbox.transaction { transaction -> Bool in
let parsedPeers: AccumulatedPeers
@ -397,7 +400,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 +410,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)
@ -451,8 +457,11 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
}
} else if peerId.namespace == Namespaces.Peer.CloudGroup {
return network.request(Api.functions.messages.getFullChat(chatId: peerId.id._internalGetInt64Value()))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<Bool, NoError> in
guard let result else {
return .single(false)
}
return postbox.transaction { transaction -> Bool in
switch result {
case let .chatFull(fullChat, chats, users):

View File

@ -78,8 +78,11 @@ public final class BlockedPeersContext {
}
self.disposable.set((self.account.network.request(Api.functions.contacts.getBlocked(flags: flags, offset: Int32(self._state.peers.count), limit: limit))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<(peers: [RenderedPeer], canLoadMore: Bool, totalCount: Int?), NoError> in
guard let result else {
return .single((peers: [], canLoadMore: false, totalCount: 0))
}
return postbox.transaction { transaction -> (peers: [RenderedPeer], canLoadMore: Bool, totalCount: Int?) in
switch result {
case let .blocked(blocked, chats, users):

View File

@ -5,8 +5,11 @@ import SwiftSignalKit
func requestRecentAccountSessions(account: Account) -> Signal<([RecentAccountSession], Int32), NoError> {
return account.network.request(Api.functions.account.getAuthorizations())
|> retryRequest
|> retryRequestIfNotFrozen
|> map { result -> ([RecentAccountSession], Int32) in
guard let result else {
return ([], 1)
}
var sessions: [RecentAccountSession] = []
var ttlDays: Int32 = 1
switch result {

View File

@ -22,25 +22,28 @@ public struct WebAuthorization : Equatable {
func webSessions(network: Network) -> Signal<([WebAuthorization], [PeerId: Peer]), NoError> {
return network.request(Api.functions.account.getWebAuthorizations())
|> retryRequest
|> map { result -> ([WebAuthorization], [PeerId : Peer]) in
var sessions: [WebAuthorization] = []
var peers:[PeerId : Peer] = [:]
switch result {
case let .webAuthorizations(authorizations, users):
for authorization in authorizations {
switch authorization {
case let .webAuthorization(hash, botId, domain, browser, platform, dateCreated, dateActive, ip, region):
sessions.append(WebAuthorization(hash: hash, botId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), domain: domain, browser: browser, platform: platform, dateCreated: dateCreated, dateActive: dateActive, ip: ip, region: region))
}
}
for user in users {
let peer = TelegramUser(user: user)
peers[peer.id] = peer
|> retryRequestIfNotFrozen
|> map { result -> ([WebAuthorization], [PeerId : Peer]) in
guard let result else {
return ([], [:])
}
var sessions: [WebAuthorization] = []
var peers:[PeerId : Peer] = [:]
switch result {
case let .webAuthorizations(authorizations, users):
for authorization in authorizations {
switch authorization {
case let .webAuthorization(hash, botId, domain, browser, platform, dateCreated, dateActive, ip, region):
sessions.append(WebAuthorization(hash: hash, botId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), domain: domain, browser: browser, platform: platform, dateCreated: dateCreated, dateActive: dateActive, ip: ip, region: region))
}
}
return (sessions, peers)
for user in users {
let peer = TelegramUser(user: user)
peers[peer.id] = peer
}
}
return (sessions, peers)
}
}

View File

@ -34,7 +34,7 @@ func _internal_updateGlobalPrivacySettings(account: Account) -> Signal<Never, No
}
var disallowedGifts: TelegramDisallowedGifts = []
if case let .disallowedStarGiftsSettings(giftFlags) = disallowedStarGifts {
if case let .disallowedGiftsSettings(giftFlags) = disallowedStarGifts {
if (giftFlags & (1 << 0)) != 0 {
disallowedGifts.insert(.unlimited)
}
@ -44,6 +44,9 @@ func _internal_updateGlobalPrivacySettings(account: Account) -> Signal<Never, No
if (giftFlags & (1 << 2)) != 0 {
disallowedGifts.insert(.unique)
}
if (giftFlags & (1 << 3)) != 0 {
disallowedGifts.insert(.premium)
}
}
globalSettings = GlobalPrivacySettings(
@ -256,7 +259,7 @@ func _internal_requestAccountPrivacySettings(account: Account) -> Signal<Account
}
var disallowedGifts: TelegramDisallowedGifts = []
if case let .disallowedStarGiftsSettings(giftFlags) = disallowedStarGifts {
if case let .disallowedGiftsSettings(giftFlags) = disallowedStarGifts {
if (giftFlags & (1 << 0)) != 0 {
disallowedGifts.insert(.unlimited)
}
@ -266,6 +269,9 @@ func _internal_requestAccountPrivacySettings(account: Account) -> Signal<Account
if (giftFlags & (1 << 2)) != 0 {
disallowedGifts.insert(.unique)
}
if (giftFlags & (1 << 3)) != 0 {
disallowedGifts.insert(.premium)
}
}
globalSettings = GlobalPrivacySettings(
@ -407,12 +413,15 @@ func _internal_updateGlobalPrivacySettings(account: Account, settings: GlobalPri
if settings.disallowedGifts.contains(.unique) {
giftFlags |= 1 << 2
}
if settings.disallowedGifts.contains(.premium) {
giftFlags |= 1 << 3
}
}
flags |= 1 << 6
let disallowedStargifts = Api.DisallowedStarGiftsSettings.disallowedStarGiftsSettings(flags: giftFlags)
let disallowedStargifts = Api.DisallowedGiftsSettings.disallowedGiftsSettings(flags: giftFlags)
return account.network.request(Api.functions.account.setGlobalPrivacySettings(
settings: .globalPrivacySettings(flags: flags, noncontactPeersPaidStars: noncontactPeersPaidStars, disallowedStargifts: disallowedStargifts)
settings: .globalPrivacySettings(flags: flags, noncontactPeersPaidStars: noncontactPeersPaidStars, disallowedGifts: disallowedStargifts)
))
|> retryRequest
|> ignoreValues

View File

@ -34,8 +34,11 @@ public final class ChatThemes: Codable, Equatable {
func _internal_getChatThemes(accountManager: AccountManager<TelegramAccountManagerTypes>, network: Network, forceUpdate: Bool = false, onlyCached: Bool = false) -> Signal<[TelegramTheme], NoError> {
let fetch: ([TelegramTheme]?, Int64?) -> Signal<[TelegramTheme], NoError> = { current, hash in
return network.request(Api.functions.account.getChatThemes(hash: hash ?? 0))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<[TelegramTheme], NoError> in
guard let result else {
return .complete()
}
switch result {
case let .themes(hash, apiThemes):
let result = apiThemes.compactMap { TelegramTheme(apiTheme: $0) }

View File

@ -7,8 +7,11 @@ import TelegramApi
public func telegramWallpapers(postbox: Postbox, network: Network, forceUpdate: Bool = false) -> Signal<[TelegramWallpaper], NoError> {
let fetch: ([TelegramWallpaper]?, Int64?) -> Signal<[TelegramWallpaper], NoError> = { current, hash in
network.request(Api.functions.account.getWallPapers(hash: hash ?? 0))
|> retryRequest
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<([TelegramWallpaper], Int64), NoError> in
guard let result else {
return .single(([], -1))
}
switch result {
case let .wallPapers(hash, wallpapers):
var items: [TelegramWallpaper] = []

View File

@ -43,6 +43,7 @@ public enum PresentationResourceKey: Int32 {
case itemListDisclosureLocked
case itemListCheckIcon
case itemListSecondaryCheckIcon
case itemListDisabledCheckIcon
case itemListPlusIcon
case itemListRoundPlusIcon
case itemListAccentDeleteIcon

View File

@ -57,6 +57,12 @@ public struct PresentationResourcesItemList {
})
}
public static func disabledCheckIconImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.itemListDisabledCheckIcon.rawValue, { theme in
return generateItemListCheckIcon(color: theme.list.itemDisabledTextColor)
})
}
public static func plusIconImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.itemListPlusIcon.rawValue, { theme in
return generateItemListPlusIcon(theme.list.itemAccentColor)

View File

@ -193,10 +193,9 @@ private final class ScrollContent: CombinedComponent {
adsText = strings.AdsInfo_Bot_Ads_Text
infoRawText = strings.AdsInfo_Bot_Launch_Text
case .search:
//TODO:localize
respectText = "Ads like this do not use your personal information and are based on the search query you entered."
adsText = strings.AdsInfo_Bot_Ads_Text
infoRawText = "Anyone can create an ad to display in search results for any query. Check out the Telegram Ad Platform for details. [Learn More >]()"
respectText = strings.AdsInfo_Search_Respect_Text
adsText = strings.AdsInfo_Search_Ads_Text
infoRawText = strings.AdsInfo_Search_Launch_Text
}
var items: [AnyComponentWithIdentity<Empty>] = []
@ -1277,22 +1276,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)
}
)
)

View File

@ -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<EnvironmentType>(
content: AnyComponent<EnvironmentType>(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,

View File

@ -686,7 +686,7 @@ public final class ChatInlineSearchResultsListComponent: Component {
},
openPhotoSetup: {
},
openAdInfo: { _ in
openAdInfo: { _, _ in
},
openAccountFreezeInfo: {
}

View File

@ -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

View File

@ -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)
@ -687,6 +692,10 @@ final class GiftOptionsScreenComponent: Component {
}
self.component = component
if let disallowedGifts = self.state?.disallowedGifts, disallowedGifts == .All {
controller()?.dismiss()
}
if (state.starGifts ?? []).isEmpty && !(state.transferStarGifts ?? []).isEmpty {
self.starsFilter = .transfer
}
@ -714,7 +723,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
@ -1339,19 +1348,26 @@ final class GiftOptionsScreenComponent: Component {
self.disposable = combineLatest(
queue: Queue.mainQueue(),
context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer.init(id: peerId),
TelegramEngine.EngineData.Item.Peer.Peer.init(id: peerId)
),
context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Peer.DisallowedGifts(id: peerId)
),
availableProducts,
context.engine.payments.cachedStarGifts(),
self.starGiftsContext.state
).start(next: { [weak self] data, availableProducts, starGifts, profileGiftsState in
).start(next: { [weak self] peer, disallowedGifts, availableProducts, starGifts, profileGiftsState in
guard let self else {
return
}
self.peer = data.0
self.disallowedGifts = data.1 ?? []
if disallowedGifts == nil && self.peer == nil, case .user = peer {
let _ = context.engine.peers.fetchAndUpdateCachedPeerData(peerId: peerId).startStandalone()
}
self.peer = peer
self.disallowedGifts = disallowedGifts ?? []
if peerId != context.account.peerId {
if availableProducts.isEmpty {
var premiumProducts: [PremiumGiftProduct] = []
@ -1425,7 +1441,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
}
}
}
}

View File

@ -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
@ -390,8 +390,13 @@ final class GiftSetupScreenComponent: Component {
let completion = component.completion
let signal = BotCheckoutController.InputData.fetch(context: component.context, source: source)
|> `catch` { _ -> Signal<BotCheckoutController.InputData, SendBotPaymentFormError> in
return .fail(.generic)
|> `catch` { error -> Signal<BotCheckoutController.InputData, SendBotPaymentFormError> in
switch error {
case .disallowedStarGifts:
return .fail(.disallowedStarGift)
default:
return .fail(.generic)
}
}
|> mapToSignal { inputData -> Signal<SendBotPaymentResult, SendBotPaymentFormError> in
return component.context.engine.payments.sendStarsPaymentForm(formId: inputData.form.id, source: source)
@ -403,7 +408,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 +560,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 +693,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 +751,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 +918,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 +1070,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 +1149,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 +1276,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 +1698,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

View File

@ -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()
})
}

View File

@ -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<EnvironmentType>.self)
let animateOut = StoredActionSlot(Action<Void>.self)
let sheetExternalState = SheetComponent<EnvironmentType>.ExternalState()
return { context in
let environment = context.environment[EnvironmentType.self]
let controller = environment.controller
let sheet = sheet.update(
component: SheetComponent<EnvironmentType>(
content: AnyComponent<EnvironmentType>(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<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
view.dismissAnimated()
}
}
}

View File

@ -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)
}
})
})))

View File

@ -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

View File

@ -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: {
}

View File

@ -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 {

View File

@ -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) {
@ -14360,7 +14363,7 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom
action: nil
)),
environment: {},
containerSize: CGSize(width: 28.0, height: 28.0)
containerSize: CGSize(width: 24.0, height: 24.0)
)
if let view = self.emojiStatusView.view {
if view.superview == nil {

View File

@ -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,
@ -510,6 +514,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
}
} else {
var dismissImpl: (() -> Void)?
let controller = GiftViewScreen(
context: self.context,
subject: .profileGift(self.peerId, product),
@ -542,15 +547,58 @@ 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] unpinnedReference in
guard let self else {
return
}
dismissImpl?()
var replacingTitle = ""
for gift in gifts {
if gift.reference == unpinnedReference, case let .unique(uniqueGift) = gift.gift {
replacingTitle = "\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, params.presentationData.dateTimeFormat.groupingSeparator))"
}
}
var updatedPinnedGifts = self.pinnedReferences
if let index = updatedPinnedGifts.firstIndex(of: unpinnedReference), let reference = product.reference {
updatedPinnedGifts[index] = reference
}
self.profileGifts.updatePinnedToTopStarGifts(references: updatedPinnedGifts)
var title = ""
if case let .unique(uniqueGift) = product.gift {
title = "\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, params.presentationData.dateTimeFormat.groupingSeparator))"
}
let _ = self.scrollToTop()
Queue.mainQueue().after(0.35) {
let toastTitle = params.presentationData.strings.PeerInfo_Gifts_ToastPinned_TitleNew(title).string
let toastText = params.presentationData.strings.PeerInfo_Gifts_ToastPinned_ReplacingText(replacingTitle).string
self.parentController?.present(UndoOverlayController(presentationData: params.presentationData, content: .universal(animation: "anim_toastpin", scale: 0.06, colors: [:], title: toastTitle, text: toastText, customUndoText: nil, timeout: 5), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}
}
)
self.parentController?.push(controller)
}
return false
}
if let reference = product.reference {
self.profileGifts.updateStarGiftPinnedToTop(reference: reference, pinnedToTop: pinnedToTop)
var title = ""
if case let .unique(uniqueGift) = product.gift {
title = "\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, params.presentationData.dateTimeFormat.groupingSeparator))"
}
if pinnedToTop {
let _ = self.scrollToTop()
Queue.mainQueue().after(0.35) {
let toastTitle = params.presentationData.strings.PeerInfo_Gifts_ToastPinned_Title
let toastTitle = params.presentationData.strings.PeerInfo_Gifts_ToastPinned_TitleNew(title).string
let toastText = params.presentationData.strings.PeerInfo_Gifts_ToastPinned_Text
self.parentController?.present(UndoOverlayController(presentationData: params.presentationData, content: .universal(animation: "anim_toastpin", scale: 0.06, colors: [:], title: toastTitle, text: toastText, customUndoText: nil, timeout: 5), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}
@ -568,6 +616,9 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
}
)
dismissImpl = { [weak controller] in
controller?.dismissAnimated()
}
self.parentController?.push(controller)
}
},
@ -657,7 +708,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
@ -1021,7 +1075,11 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
toastTitle = nil
toastText = strings.PeerInfo_Gifts_ToastUnpinned_Text
} else {
toastTitle = strings.PeerInfo_Gifts_ToastPinned_Title
var title = ""
if case let .unique(uniqueGift) = gift.gift {
title = "\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, presentationData.dateTimeFormat.groupingSeparator))"
}
toastTitle = strings.PeerInfo_Gifts_ToastPinned_TitleNew(title).string
toastText = strings.PeerInfo_Gifts_ToastPinned_Text
}
self.parentController?.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: !pinnedToTop ? "anim_toastunpin" : "anim_toastpin", scale: 0.06, colors: [:], title: toastTitle, text: toastText, customUndoText: nil, timeout: 5), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))

View File

@ -111,7 +111,7 @@ private final class SheetContent: CombinedComponent {
let title = title.update(
component: BalancedTextComponent(
text: .plain(NSAttributedString(string: "Your Account is Frozen", font: titleFont, textColor: textColor)),
text: .plain(NSAttributedString(string: strings.FrozenAccount_Title, font: titleFont, textColor: textColor)),
horizontalAlignment: .center,
maximumNumberOfLines: 0,
lineSpacing: 0.1
@ -125,15 +125,14 @@ private final class SheetContent: CombinedComponent {
contentSize.height += title.size.height
contentSize.height += spacing - 2.0
//TODO:localize
var items: [AnyComponentWithIdentity<Empty>] = []
items.append(
AnyComponentWithIdentity(
id: "ads",
id: "violation",
component: AnyComponent(ParagraphComponent(
title: "Violation of Terms",
title: strings.FrozenAccount_Violation_Title,
titleColor: textColor,
text: "Your account was frozen for breaking Telegram's Terms and Conditions.",
text: strings.FrozenAccount_Violation_Text,
textColor: secondaryTextColor,
iconName: "Account Freeze/Violation",
iconColor: linkColor
@ -142,11 +141,11 @@ private final class SheetContent: CombinedComponent {
)
items.append(
AnyComponentWithIdentity(
id: "split",
id: "readOnly",
component: AnyComponent(ParagraphComponent(
title: "Read-Only Mode",
title: strings.FrozenAccount_ReadOnly_Title,
titleColor: textColor,
text: "You can access your account but can't send messages or take actions.",
text: strings.FrozenAccount_ReadOnly_Text,
textColor: secondaryTextColor,
iconName: "Ads/Privacy",
iconColor: linkColor
@ -156,19 +155,17 @@ private final class SheetContent: CombinedComponent {
let dateString = stringForFullDate(timestamp: component.configuration.freezeUntilDate ?? 0, strings: strings, dateTimeFormat: environment.dateTimeFormat)
items.append(
AnyComponentWithIdentity(
id: "withdrawal",
id: "appeal",
component: AnyComponent(ParagraphComponent(
title: "Appeal Before Deactivation",
title: strings.FrozenAccount_Appeal_Title,
titleColor: textColor,
text: "Appeal via [@SpamBot]() before \(dateString), or your account will be deleted.",
text: strings.FrozenAccount_Appeal_Text(dateString).string,
textColor: secondaryTextColor,
iconName: "Account Freeze/Appeal",
iconColor: linkColor,
action: {
component.submitAppeal()
component.dismiss()
Queue.mainQueue().after(0.5) {
component.submitAppeal()
}
}
))
)
@ -185,7 +182,7 @@ private final class SheetContent: CombinedComponent {
contentSize.height += list.size.height
contentSize.height += spacing + 2.0
let buttonAttributedString = NSMutableAttributedString(string: "Submit an Appeal", font: Font.semibold(17.0), textColor: environment.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
let buttonAttributedString = NSMutableAttributedString(string: strings.FrozenAccount_SubmitAppeal, font: Font.semibold(17.0), textColor: environment.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
let actionButton = actionButton.update(
component: ButtonComponent(
background: ButtonComponent.Background(
@ -201,10 +198,8 @@ private final class SheetContent: CombinedComponent {
isEnabled: true,
displaysProgress: false,
action: {
component.submitAppeal()
component.dismiss()
Queue.mainQueue().after(0.5) {
component.submitAppeal()
}
}
),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
@ -217,7 +212,7 @@ private final class SheetContent: CombinedComponent {
contentSize.height += actionButton.size.height
contentSize.height += 8.0
let closeAttributedString = NSMutableAttributedString(string: "Understood", font: Font.regular(17.0), textColor: environment.theme.list.itemCheckColors.fillColor, paragraphAlignment: .center)
let closeAttributedString = NSMutableAttributedString(string: strings.FrozenAccount_Understood, font: Font.regular(17.0), textColor: environment.theme.list.itemCheckColors.fillColor, paragraphAlignment: .center)
let closeButton = closeButton.update(
component: ButtonComponent(
background: ButtonComponent.Background(
@ -390,10 +385,12 @@ public final class AccountFreezeInfoScreen: ViewControllerComponentContainer {
self.navigationPresentation = .flatModal
submitAppealImpl = { [weak self] in
guard let self, let url = configuration.freezeAppealUrl else {
guard let self, let navigationController = self.navigationController as? NavigationController, let url = configuration.freezeAppealUrl else {
return
}
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: self.navigationController as? NavigationController, dismissInput: {})
Queue.mainQueue().after(0.4) {
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: navigationController, dismissInput: {})
}
}
}

View File

@ -212,7 +212,7 @@ final class GreetingMessageListItemComponent: Component {
},
openPhotoSetup: {
},
openAdInfo: { _ in
openAdInfo: { _, _ in
},
openAccountFreezeInfo: {
}

View File

@ -233,7 +233,7 @@ final class QuickReplySetupScreenComponent: Component {
},
openPhotoSetup: {
},
openAdInfo: { _ in
openAdInfo: { _, _ in
},
openAccountFreezeInfo: {
}

View File

@ -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<AdditionalPeerList.Category>()
@ -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,199 @@ final class ChatbotSetupScreenComponent: Component {
if !self.hasAccessToAllChatsByDefault {
contentHeight += excludedUsersContentHeight
}
var permissionsItems: [AnyComponentWithIdentity<Empty>] = []
for permission in self.permissions {
var value = permission.value == true
var titleItems: [AnyComponentWithIdentity<Empty>] = []
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<Empty>] = []
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<Empty>] = []
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 {
for subpermission in subpermissions {
if subpermission.enabled {
subpermission.value = value
}
if let subpermissions = permission.subpermissions {
value = true
var selectedCount = 0
for subpermission in subpermissions {
if let key = subpermission.key {
if self.botRights.contains(key) {
selectedCount += 1
} else {
value = false
}
} else if let value = permission.value {
permission.value = value
} else if subpermission.value == true {
selectedCount += 1
}
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 = subpermission.key {
value = self.botRights.contains(key)
} else if subpermission.value == true {
value = true
}
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

View File

@ -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)

View File

@ -15,7 +15,7 @@ import TelegramPresentationData
import TelegramNotices
extension ChatControllerImpl {
func presentAccountFrozenInfoIfNeeded() -> Bool {
func presentAccountFrozenInfoIfNeeded(delay: Bool = false) -> Bool {
if self.context.isFrozen {
let accountFreezeConfiguration = AccountFreezeConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
if let freezeAppealUrl = accountFreezeConfiguration.freezeAppealUrl {
@ -24,7 +24,16 @@ extension ChatControllerImpl {
return false
}
}
self.push(self.context.sharedContext.makeAccountFreezeInfoScreen(context: self.context))
let present = {
self.push(self.context.sharedContext.makeAccountFreezeInfoScreen(context: self.context))
}
if delay {
Queue.mainQueue().after(0.3) {
present()
}
} else {
present()
}
return true
}
return false

View File

@ -1866,7 +1866,7 @@ extension ChatControllerImpl {
return
}
guard !strongSelf.presentAccountFrozenInfoIfNeeded() else {
guard !strongSelf.presentAccountFrozenInfoIfNeeded(delay: true) else {
completion(.immediate, {})
return
}
@ -2120,7 +2120,7 @@ extension ChatControllerImpl {
})
}, deleteMessages: { [weak self] messages, contextController, completion in
if let strongSelf = self, !messages.isEmpty {
guard !strongSelf.presentAccountFrozenInfoIfNeeded() else {
guard !strongSelf.presentAccountFrozenInfoIfNeeded(delay: true) else {
completion(.default)
return
}
@ -2192,6 +2192,10 @@ extension ChatControllerImpl {
}
}, forwardMessages: { [weak self] messages in
if let strongSelf = self, !messages.isEmpty {
guard !strongSelf.presentAccountFrozenInfoIfNeeded(delay: true) else {
return
}
strongSelf.commitPurposefulAction()
let forwardMessageIds = messages.map { $0.id }.sorted()
strongSelf.forwardMessages(messageIds: forwardMessageIds)
@ -3229,6 +3233,11 @@ extension ChatControllerImpl {
self?.unblockPeer()
}, pinMessage: { [weak self] messageId, contextController in
if let strongSelf = self, let currentPeerId = strongSelf.chatLocation.peerId {
guard !strongSelf.presentAccountFrozenInfoIfNeeded(delay: true) else {
contextController?.dismiss(completion: nil)
return
}
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
if strongSelf.canManagePin() {
let pinAction: (Bool, Bool) -> Void = { notify, forThisPeerOnlyIfPossible in

View File

@ -132,6 +132,10 @@ extension ChatControllerImpl {
case .cancelMessageSelection:
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } })
case .clearHistory:
guard !self.presentAccountFrozenInfoIfNeeded() else {
return
}
if case let .peer(peerId) = self.chatLocation {
let beginClear: (InteractiveHistoryClearingType) -> Void = { [weak self] type in
self?.beginClearHistory(type: type)

View File

@ -333,7 +333,7 @@ extension ChatControllerImpl {
}
controller?.dismissWithoutContent()
guard !self.presentAccountFrozenInfoIfNeeded() else {
guard !self.presentAccountFrozenInfoIfNeeded(delay: true) else {
return
}
self.presentTagPremiumPaywall()
@ -344,7 +344,7 @@ extension ChatControllerImpl {
return
}
guard !self.presentAccountFrozenInfoIfNeeded() else {
guard !self.presentAccountFrozenInfoIfNeeded(delay: true) else {
controller?.dismiss(completion: {})
return
}

View File

@ -50,17 +50,22 @@ extension ChatControllerImpl {
func enqueueGifData(_ data: Data) {
self.enqueueMediaMessageDisposable.set((legacyEnqueueGifMessage(account: self.context.account, data: data) |> deliverOnMainQueue).startStrict(next: { [weak self] message in
if let strongSelf = self {
let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
})
strongSelf.presentPaidMessageAlertIfNeeded(completion: { [weak self] postpone in
guard let strongSelf = self else {
return
}
}, nil)
strongSelf.sendMessages([message].map { $0.withUpdatedReplyToMessageId(replyMessageSubject?.subjectModel) })
let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
})
}
}, nil)
strongSelf.sendMessages([message].map { $0.withUpdatedReplyToMessageId(replyMessageSubject?.subjectModel) })
})
}
}))
}
@ -68,17 +73,22 @@ extension ChatControllerImpl {
func enqueueVideoData(_ data: Data) {
self.enqueueMediaMessageDisposable.set((legacyEnqueueGifMessage(account: self.context.account, data: data) |> deliverOnMainQueue).startStrict(next: { [weak self] message in
if let strongSelf = self {
let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
})
strongSelf.presentPaidMessageAlertIfNeeded(completion: { [weak self] postpone in
guard let strongSelf = self else {
return
}
}, nil)
strongSelf.sendMessages([message].map { $0.withUpdatedReplyToMessageId(replyMessageSubject?.subjectModel) })
let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
})
}
}, nil)
strongSelf.sendMessages([message].map { $0.withUpdatedReplyToMessageId(replyMessageSubject?.subjectModel) })
})
}
}))
}
@ -98,17 +108,22 @@ extension ChatControllerImpl {
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/webp", size: Int64(data.count), attributes: fileAttributes, alternativeRepresentations: [])
let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: strongSelf.chatLocation.threadId, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
})
strongSelf.presentPaidMessageAlertIfNeeded(completion: { [weak self] postpone in
guard let strongSelf = self else {
return
}
}, nil)
strongSelf.sendMessages([message].map { $0.withUpdatedReplyToMessageId(replyMessageSubject?.subjectModel) })
let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
})
}
}, nil)
strongSelf.sendMessages([message].map { $0.withUpdatedReplyToMessageId(replyMessageSubject?.subjectModel) }, postpone: postpone)
})
}
}))
}
@ -116,23 +131,28 @@ extension ChatControllerImpl {
func enqueueStickerFile(_ file: TelegramMediaFile) {
let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), threadId: self.chatLocation.threadId, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
let replyMessageSubject = self.presentationInterfaceState.interfaceState.replyMessageSubject
self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
})
self.presentPaidMessageAlertIfNeeded(completion: { [weak self] postpone in
guard let self else {
return
}
}, nil)
self.sendMessages([message].map { $0.withUpdatedReplyToMessageId(replyMessageSubject?.subjectModel) })
Queue.mainQueue().after(3.0) {
if let message = self.chatDisplayNode.historyNode.lastVisbleMesssage(), let file = message.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile, file.isSticker {
self.context.engine.stickers.addRecentlyUsedSticker(fileReference: .message(message: MessageReference(message), media: file))
let replyMessageSubject = self.presentationInterfaceState.interfaceState.replyMessageSubject
self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
})
}
}, nil)
self.sendMessages([message].map { $0.withUpdatedReplyToMessageId(replyMessageSubject?.subjectModel) })
Queue.mainQueue().after(3.0) {
if let message = self.chatDisplayNode.historyNode.lastVisbleMesssage(), let file = message.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile, file.isSticker {
self.context.engine.stickers.addRecentlyUsedSticker(fileReference: .message(message: MessageReference(message), media: file))
}
}
}
})
}
func enqueueAnimatedStickerData(_ data: Data) {
@ -223,9 +243,7 @@ extension ChatControllerImpl {
fileAttributes.append(.Video(duration: animatedImage.duration, size: PixelDimensions(width: 512, height: 512), flags: [], preloadSize: nil, coverTime: nil, videoCodec: nil))
let previewRepresentations: [TelegramMediaImageRepresentation] = []
// if let thumbnailResource {
// previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil))
// }
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
self.context.account.postbox.mediaBox.copyResourceData(resource.id, fromTempPath: path)
@ -235,7 +253,5 @@ extension ChatControllerImpl {
break
}
})
// self.stickerVideoExport = videoExport
}
}

View File

@ -3402,7 +3402,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let self else {
return
}
guard !self.presentAccountFrozenInfoIfNeeded() else {
guard !self.presentAccountFrozenInfoIfNeeded(delay: true) else {
return
}
self.presentScheduleTimePicker(completion: { [weak self] time in
@ -3433,7 +3433,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let self else {
return
}
guard !self.presentAccountFrozenInfoIfNeeded() else {
guard !self.presentAccountFrozenInfoIfNeeded(delay: true) else {
return
}
@ -6397,7 +6397,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if case let .known(value) = cachedData.businessIntro {
businessIntro = value
}
alwaysShowGiftButton = cachedData.flags.contains(.displayGiftButton)
if cachedData.disallowedGifts != .All {
alwaysShowGiftButton = cachedData.flags.contains(.displayGiftButton)
}
} else if let cachedData = peerView.cachedData as? CachedGroupData {
var invitedBy: Peer?
if let invitedByPeerId = cachedData.invitedBy {
@ -9265,8 +9267,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)

View File

@ -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,

View File

@ -89,10 +89,9 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
if let context = self.context {
accountFreezeConfiguration = AccountFreezeConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
}
//TODO:localize
if let _ = accountFreezeConfiguration?.freezeUntilDate {
self.textNode.attributedText = NSAttributedString(string: "You account is frozen", font: Font.semibold(15.0), textColor: interfaceState.theme.list.itemDestructiveColor)
self.subtitleNode.attributedText = NSAttributedString(string: "Tap to view details", font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor)
self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelFrozenAccount_Title, font: Font.semibold(15.0), textColor: interfaceState.theme.list.itemDestructiveColor)
self.subtitleNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelFrozenAccount_Text, font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor)
isUserInteractionEnabled = true
} else if case let .replyThread(message) = interfaceState.chatLocation, message.peerId == self.context?.account.peerId {
self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelStatusAuthorHidden, font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor)

View File

@ -295,7 +295,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, ASScrollViewDe
}, editPeer: { _ in
}, openWebApp: { _ in
}, openPhotoSetup: {
}, openAdInfo: { _ in
}, openAdInfo: { _, _ in
}, openAccountFreezeInfo: {
})
interaction.searchTextHighightState = searchQuery

View File

@ -182,7 +182,7 @@ private struct CommandChatInputContextPanelEntry: Comparable, Identifiable {
},
openPhotoSetup: {
},
openAdInfo: { _ in
openAdInfo: { _, _ in
},
openAccountFreezeInfo: {
}