From 46d9465841099c75bf51c325f9329a1b02832b97 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Tue, 29 Apr 2025 00:32:23 +0200 Subject: [PATCH] Chat list notices --- .../Sources/ChatListController.swift | 6 +- .../Sources/Node/ChatListNode.swift | 37 +++---- .../Sources/Node/ChatListNodeEntries.swift | 2 +- .../Sources/Node/ChatListNoticeItem.swift | 35 ++++-- .../ChangePhoneNumberCodeController.swift | 2 +- .../Sources/ChangePhoneNumberController.swift | 2 +- .../State/ManagedProxyInfoUpdates.swift | 8 ++ .../TelegramCore/Sources/Suggestions.swift | 103 ++++++------------ .../Notices/TelegramEngineNotices.swift | 4 +- .../Sources/PeerInfoScreen.swift | 8 +- .../Chat/ChatControllerLoadDisplayNode.swift | 2 +- .../Sources/SharedAccountContext.swift | 4 +- 12 files changed, 98 insertions(+), 115 deletions(-) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index fe88c71cd7..052e707e58 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -2501,13 +2501,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController guard let strongSelf = self else { return } - strongSelf.dismissAutoarchiveDisposable.set(strongSelf.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .autoarchivePopular).startStrict()) + strongSelf.dismissAutoarchiveDisposable.set(strongSelf.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.autoarchivePopular.id).startStrict()) }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.ChatList_AutoarchiveSuggestion_OpenSettings, action: { guard let strongSelf = self else { return } - strongSelf.dismissAutoarchiveDisposable.set(strongSelf.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .autoarchivePopular).startStrict()) + strongSelf.dismissAutoarchiveDisposable.set(strongSelf.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.autoarchivePopular.id).startStrict()) strongSelf.push(strongSelf.context.sharedContext.makePrivacyAndSecurityController(context: strongSelf.context)) }) ], actionLayout: .vertical, parseMarkdown: true), in: .window(.root)) @@ -6114,7 +6114,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController func openBirthdaySetup() { let context = self.context - let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: .setupBirthday).startStandalone() + let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupBirthday.id).startStandalone() let settingsPromise: Promise if let rootController = self.context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface, let current = rootController.getPrivacySettings() { diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 68e11448cf..302bb3a8cf 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -778,7 +778,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL nodeInteraction?.openPhotoSetup() case .accountFreeze: nodeInteraction?.openAccountFreezeInfo() - case let .link(url, _, _): + case let .link(_, url, _, _): nodeInteraction?.openUrl(url) } case .hide: @@ -1128,7 +1128,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL nodeInteraction?.openPhotoSetup() case .accountFreeze: nodeInteraction?.openAccountFreezeInfo() - case let .link(url, _, _): + case let .link(_, url, _, _): nodeInteraction?.openUrl(url) } case .hide: @@ -1731,7 +1731,7 @@ public final class ChatListNode: ListView { } Queue.mainQueue().after(0.6) { [weak self] in if let self { - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .setupPassword).startStandalone() + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPassword.id).startStandalone() } } let controller = self.context.sharedContext.makeSetupTwoFactorAuthController(context: self.context) @@ -1742,9 +1742,9 @@ public final class ChatListNode: ListView { } Queue.mainQueue().after(0.6) { [weak self] in if let self { - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .annualPremium).startStandalone() - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .upgradePremium).startStandalone() - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .restorePremium).startStandalone() + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.annualPremium.id).startStandalone() + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.upgradePremium.id).startStandalone() + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.restorePremium.id).startStandalone() } } let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .ads, forceDark: false, dismissed: nil) @@ -1875,26 +1875,28 @@ public final class ChatListNode: ListView { let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } switch notice { case .xmasPremiumGift: - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .xmasPremiumGift).startStandalone() + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.xmasPremiumGift.id).startStandalone() self.present?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, timeout: 5.0, customUndoText: nil), elevatedLayout: false, action: { _ in return true })) case .setupBirthday: - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .setupBirthday).startStandalone() + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupBirthday.id).startStandalone() self.present?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.ChatList_BirthdayInSettingsInfo, timeout: 5.0, customUndoText: nil), elevatedLayout: false, action: { _ in return true })) case .birthdayPremiumGift: - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .todayBirthdays).startStandalone() + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.todayBirthdays.id).startStandalone() self.present?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, timeout: 5.0, customUndoText: nil), elevatedLayout: false, action: { _ in return true })) case .premiumGrace: - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .gracePremium).startStandalone() + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.gracePremium.id).startStandalone() case .setupPhoto: - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .setupPhoto).startStandalone() + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPhoto.id).startStandalone() case .starsSubscriptionLowBalance: - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .starsSubscriptionLowBalance).startStandalone() + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.starsSubscriptionLowBalance.id).startStandalone() + case let .link(id, _, _, _): + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: id).startStandalone() default: break } @@ -2063,7 +2065,7 @@ public final class ChatListNode: ListView { return lhs < rhs } - if dismissedSuggestions.contains(.todayBirthdays) { + if dismissedSuggestions.contains(ServerProvidedSuggestion.todayBirthdays.id) { todayBirthdayPeerIds = [] } @@ -2147,8 +2149,8 @@ public final class ChatListNode: ListView { } return .birthdayPremiumGift(peers: todayBirthdayPeers, birthdays: birthdays) } - } else if case let .link(url, title, subtitle) = suggestions.first(where: { if case .link = $0 { return true } else { return false} }) { - return .single(.link(url: url, title: title, subtitle: subtitle)) + } else if case let .link(id, url, title, subtitle) = suggestions.first(where: { if case .link = $0 { return true } else { return false} }) { + return .single(.link(id: id, url: url, title: title, subtitle: subtitle)) } else { return .single(nil) } @@ -2156,11 +2158,6 @@ public final class ChatListNode: ListView { |> distinctUntilChanged self.suggestedChatListNotice.set(suggestedChatListNoticeSignal) - - /*#if DEBUG - let testNotice: Signal = Signal.single(.setupPassword) |> then(Signal.complete() |> delay(1.0, queue: .mainQueue())) |> then(Signal.single(.xmasPremiumGift)) |> then(Signal.complete() |> delay(1.0, queue: .mainQueue())) |> restart - self.suggestedChatListNotice.set(testNotice) - #endif*/ }).strict() let storageInfo: Signal diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift index a1f89e5b08..726792f7ab 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift @@ -93,7 +93,7 @@ public enum ChatListNotice: Equatable { case starsSubscriptionLowBalance(amount: StarsAmount, peers: [EnginePeer]) case setupPhoto(EnginePeer) case accountFreeze - case link(url: String, title: String, subtitle: String) + case link(id: String, url: String, title: ServerSuggestionInfo.Item.Text, subtitle: ServerSuggestionInfo.Item.Text) } enum ChatListNodeEntry: Comparable, Identifiable { diff --git a/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift b/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift index a4f19a1965..d7373fa66d 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift @@ -85,13 +85,21 @@ class ChatListNoticeItem: ListViewItem { private let separatorHeight = 1.0 / UIScreen.main.scale private let titleFont = Font.semibold(15.0) +private let titleBoldFont = Font.bold(15.0) +private let titleItalicFont = Font.semiboldItalic(15.0) +private let titleBoldItalicFont = Font.semiboldItalic(15.0) + private let textFont = Font.regular(15.0) +private let textBoldFont = Font.semibold(15.0) +private let textItalicFont = Font.italic(15.0) +private let textBoldItalicFont = Font.semiboldItalic(15.0) + private let smallTextFont = Font.regular(14.0) final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { private let contentContainer: ASDisplayNode private let titleNode: TextNodeWithEntities - private let textNode: TextNode + private let textNode: TextNodeWithEntities private let arrowNode: ASImageNode private let separatorNode: ASDisplayNode @@ -118,7 +126,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { self.contentContainer = ASDisplayNode() self.titleNode = TextNodeWithEntities() - self.textNode = TextNode() + self.textNode = TextNodeWithEntities() self.arrowNode = ASImageNode() self.separatorNode = ASDisplayNode() @@ -128,7 +136,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { self.clipsToBounds = true self.contentContainer.addSubnode(self.titleNode.textNode) - self.contentContainer.addSubnode(self.textNode) + self.contentContainer.addSubnode(self.textNode.textNode) self.contentContainer.addSubnode(self.arrowNode) self.addSubnode(self.contentContainer) @@ -154,7 +162,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { let previousItem = self.item let makeTitleLayout = TextNodeWithEntities.asyncLayout(self.titleNode) - let makeTextLayout = TextNode.asyncLayout(self.textNode) + let makeTextLayout = TextNodeWithEntities.asyncLayout(self.textNode) let makeOkButtonTextLayout = TextNode.asyncLayout(self.okButtonText) let makeCancelButtonTextLayout = TextNode.asyncLayout(self.cancelButtonText) @@ -291,9 +299,9 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { case .accountFreeze: 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) - case let .link(_, title, subtitle): - titleString = NSAttributedString(string: title, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor) - textString = NSAttributedString(string: subtitle, font: smallTextFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + case let .link(_, _, title, subtitle): + titleString = stringWithAppliedEntities(title.string, entities: title.entities, baseColor: item.theme.list.itemPrimaryTextColor, linkColor: item.theme.list.itemAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleBoldFont, italicFont: titleItalicFont, boldItalicFont: titleBoldItalicFont, fixedFont: titleFont, blockQuoteFont: titleFont, message: nil) + textString = stringWithAppliedEntities(subtitle.string, entities: subtitle.entities, baseColor: item.theme.list.itemPrimaryTextColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFont, blockQuoteFont: textFont, message: nil) } var leftInset: CGFloat = sideInset @@ -333,12 +341,15 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { strongSelf.titleNode.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: verticalInset), size: titleLayout.0.size) } - let _ = textLayout.1() + let _ = textLayout.1(TextNodeWithEntities.Arguments(context: item.context, cache: item.context.animationCache, renderer: item.context.animationRenderer, placeholderColor: .white, attemptSynchronous: true)) + + strongSelf.titleNode.visibilityRect = CGRect(origin: CGPoint(), size: CGSize(width: 1000000.0, height: 1000000.0)) + strongSelf.textNode.visibilityRect = CGRect(origin: CGPoint(), size: CGSize(width: 1000000.0, height: 1000000.0)) if case .center = alignment { - strongSelf.textNode.frame = CGRect(origin: CGPoint(x: floor((params.width - textLayout.0.size.width) * 0.5), y: strongSelf.titleNode.textNode.frame.maxY + spacing), size: textLayout.0.size) + strongSelf.textNode.textNode.frame = CGRect(origin: CGPoint(x: floor((params.width - textLayout.0.size.width) * 0.5), y: strongSelf.titleNode.textNode.frame.maxY + spacing), size: textLayout.0.size) } else { - strongSelf.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: strongSelf.titleNode.textNode.frame.maxY + spacing), size: textLayout.0.size) + strongSelf.textNode.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: strongSelf.titleNode.textNode.frame.maxY + spacing), size: textLayout.0.size) } if !avatarPeers.isEmpty { @@ -434,8 +445,8 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { let buttonWidth: CGFloat = floor(buttonsWidth * 0.5) let buttonHeight: CGFloat = 32.0 - let okButtonFrame = CGRect(origin: CGPoint(x: floor((params.width - buttonsWidth) * 0.5), y: strongSelf.textNode.frame.maxY + 6.0), size: CGSize(width: buttonWidth, height: buttonHeight)) - let cancelButtonFrame = CGRect(origin: CGPoint(x: okButtonFrame.maxX, y: strongSelf.textNode.frame.maxY + 6.0), size: CGSize(width: buttonWidth, height: buttonHeight)) + let okButtonFrame = CGRect(origin: CGPoint(x: floor((params.width - buttonsWidth) * 0.5), y: strongSelf.textNode.textNode.frame.maxY + 6.0), size: CGSize(width: buttonWidth, height: buttonHeight)) + let cancelButtonFrame = CGRect(origin: CGPoint(x: okButtonFrame.maxX, y: strongSelf.textNode.textNode.frame.maxY + 6.0), size: CGSize(width: buttonWidth, height: buttonHeight)) okButton.frame = okButtonFrame cancelButton.frame = cancelButtonFrame diff --git a/submodules/SettingsUI/Sources/ChangePhoneNumberCodeController.swift b/submodules/SettingsUI/Sources/ChangePhoneNumberCodeController.swift index 199624f520..6cd24015eb 100644 --- a/submodules/SettingsUI/Sources/ChangePhoneNumberCodeController.swift +++ b/submodules/SettingsUI/Sources/ChangePhoneNumberCodeController.swift @@ -285,7 +285,7 @@ func changePhoneNumberCodeController(context: AccountContext, phoneNumber: Strin let presentationData = context.sharedContext.currentPresentationData.with { $0 } presentControllerImpl?(OverlayStatusController(theme: presentationData.theme, type: .success), nil) - let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: .validatePhoneNumber).start() + let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.validatePhoneNumber.id).start() dismissImpl?() })) diff --git a/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift b/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift index f547f2390a..2b1c339bf4 100644 --- a/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift +++ b/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift @@ -84,7 +84,7 @@ public func ChangePhoneNumberController(context: AccountContext) -> ViewControll }, completed: { [weak codeController] in codeController?.present(OverlayStatusController(theme: presentationData.theme, type: .success), in: .window(.root)) - let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: .validatePhoneNumber).start() + let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.validatePhoneNumber.id).start() if let navigationController = codeController?.navigationController as? NavigationController { var viewControllers = navigationController.viewControllers diff --git a/submodules/TelegramCore/Sources/State/ManagedProxyInfoUpdates.swift b/submodules/TelegramCore/Sources/State/ManagedProxyInfoUpdates.swift index ae67340a00..b745a5b107 100644 --- a/submodules/TelegramCore/Sources/State/ManagedProxyInfoUpdates.swift +++ b/submodules/TelegramCore/Sources/State/ManagedProxyInfoUpdates.swift @@ -106,6 +106,14 @@ public final class ServerSuggestionInfo: Codable, Equatable { let container = try decoder.container(keyedBy: CodingKeys.self) self = .link(url: try container.decode(String.self, forKey: .link)) } + + public func encode(to encoder: any Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + switch self { + case let .link(url): + try container.encode(url, forKey: .link) + } + } } public let id: String diff --git a/submodules/TelegramCore/Sources/Suggestions.swift b/submodules/TelegramCore/Sources/Suggestions.swift index ec6dd7ba48..bb88bb98c9 100644 --- a/submodules/TelegramCore/Sources/Suggestions.swift +++ b/submodules/TelegramCore/Sources/Suggestions.swift @@ -3,7 +3,7 @@ import Postbox import SwiftSignalKit import TelegramApi -public enum ServerProvidedSuggestion: Hashable { +public enum ServerProvidedSuggestion: Equatable { case autoarchivePopular case newcomerTicks case validatePhoneNumber @@ -18,9 +18,9 @@ public enum ServerProvidedSuggestion: Hashable { case gracePremium case starsSubscriptionLowBalance case setupPhoto - case link(url: String, title: String, subtitle: String) + case link(id: String, url: String, title: ServerSuggestionInfo.Item.Text, subtitle: ServerSuggestionInfo.Item.Text) - public init?(string: String) { + init?(string: String) { switch string { case "AUTOARCHIVE_POPULAR": self = .autoarchivePopular @@ -51,33 +51,11 @@ public enum ServerProvidedSuggestion: Hashable { case "USERPIC_SETUP": self = .setupPhoto default: - if string.hasPrefix("LINK_") { - let rawString = string.dropFirst(Int("LINK_".count)) - if let dict = try? JSONSerialization.jsonObject(with: rawString.data(using: .utf8) ?? Data()) as? [String: Any] { - var url: String? - var title: String? - var subtitle: String? - - if let urlValue = dict["url"] as? String { - url = urlValue - } - if let titleValue = dict["title"] as? String { - title = titleValue - } - if let subtitleValue = dict["subtitle"] as? String { - subtitle = subtitleValue - } - if let url = url, let title = title, let subtitle = subtitle { - self = .link(url: url, title: title, subtitle: subtitle) - return - } - } - } return nil } } - var stringValue: String { + public var id: String { switch self { case .autoarchivePopular: return "AUTOARCHIVE_POPULAR" @@ -107,90 +85,79 @@ public enum ServerProvidedSuggestion: Hashable { return "STARS_SUBSCRIPTION_LOW_BALANCE" case .setupPhoto: return "USERPIC_SETUP" - case let .link(url, title, subtitle): - let dict: [String: String] = [ - "url": url, - "title": title, - "subtitle": subtitle - ] - if let data = try? JSONSerialization.data(withJSONObject: dict, options: []), let string = String(data: data, encoding: .utf8) { - return "LINK_\(string)" - } else { - // Fallback or error handling, though unlikely to fail with basic strings - return "LINK_{}" - } + case let .link(id, _, _, _): + return id } } } -private var dismissedSuggestionsPromise = ValuePromise<[AccountRecordId: Set]>([:]) -private var dismissedSuggestions: [AccountRecordId: Set] = [:] { +private var dismissedSuggestionsPromise = ValuePromise<[AccountRecordId: Set]>([:]) +private var dismissedSuggestions: [AccountRecordId: Set] = [:] { didSet { dismissedSuggestionsPromise.set(dismissedSuggestions) } } func _internal_getServerProvidedSuggestions(account: Account) -> Signal<[ServerProvidedSuggestion], NoError> { - let key: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.appConfiguration])) + let key: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.serverSuggestionInfo()])) return combineLatest(account.postbox.combinedView(keys: [key]), dismissedSuggestionsPromise.get()) |> map { views, dismissedSuggestionsValue -> [ServerProvidedSuggestion] in let dismissedSuggestions = dismissedSuggestionsValue[account.id] ?? Set() guard let view = views.views[key] as? PreferencesView else { return [] } - guard let appConfiguration = view.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) else { + guard let serverSuggestionInfo = view.values[PreferencesKeys.serverSuggestionInfo()]?.get(ServerSuggestionInfo.self) else { return [] } - - #if DEBUG - guard let data = appConfiguration.data, var listItems = data["pending_suggestions"] as? [String] else { - return [] + + var items: [ServerProvidedSuggestion] = [] + for item in serverSuggestionInfo.legacyItems { + if let value = ServerProvidedSuggestion(string: item) { + items.append(value) + } } - listItems.insert("LINK_{\"url\": \"https://t.me/durov\", \"title\": \"📣 Stay updated!\", \"subtitle\": \"Subscribe to the channel of Telegram's founder.\"}", at: 0) - #else - guard let data = appConfiguration.data, let listItems = data["pending_suggestions"] as? [String] else { - return [] + for item in serverSuggestionInfo.items { + switch item.action { + case let .link(url): + items.append(.link( + id: item.id, + url: url, + title: item.title, + subtitle: item.text + )) + } } - #endif - - return listItems.compactMap { item -> ServerProvidedSuggestion? in - return ServerProvidedSuggestion(string: item) - }.filter { !dismissedSuggestions.contains($0) } + + return items.filter({ !dismissedSuggestions.contains($0.id) }) } |> distinctUntilChanged } -func _internal_getServerDismissedSuggestions(account: Account) -> Signal<[ServerProvidedSuggestion], NoError> { - let key: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.appConfiguration])) +func _internal_getServerDismissedSuggestions(account: Account) -> Signal<[String], NoError> { + let key: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.serverSuggestionInfo()])) return combineLatest(account.postbox.combinedView(keys: [key]), dismissedSuggestionsPromise.get()) - |> map { views, dismissedSuggestionsValue -> [ServerProvidedSuggestion] in + |> map { views, dismissedSuggestionsValue -> [String] in let dismissedSuggestions = dismissedSuggestionsValue[account.id] ?? Set() guard let view = views.views[key] as? PreferencesView else { return [] } - guard let appConfiguration = view.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) else { + guard let serverSuggestionInfo = view.values[PreferencesKeys.serverSuggestionInfo()]?.get(ServerSuggestionInfo.self) else { return [] } - var listItems: [String] = [] - if let data = appConfiguration.data, let listItemsValues = data["dismissed_suggestions"] as? [String] { - listItems.append(contentsOf: listItemsValues) - } - var items = listItems.compactMap { item -> ServerProvidedSuggestion? in - return ServerProvidedSuggestion(string: item) - } + var items: [String] = serverSuggestionInfo.dismissedIds items.append(contentsOf: dismissedSuggestions) return items } |> distinctUntilChanged } -func _internal_dismissServerProvidedSuggestion(account: Account, suggestion: ServerProvidedSuggestion) -> Signal { +func _internal_dismissServerProvidedSuggestion(account: Account, suggestion: String) -> Signal { if let _ = dismissedSuggestions[account.id] { dismissedSuggestions[account.id]?.insert(suggestion) } else { dismissedSuggestions[account.id] = Set([suggestion]) } - return account.network.request(Api.functions.help.dismissSuggestion(peer: .inputPeerEmpty, suggestion: suggestion.stringValue)) + return account.network.request(Api.functions.help.dismissSuggestion(peer: .inputPeerEmpty, suggestion: suggestion)) |> `catch` { _ -> Signal in return .single(.boolFalse) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Notices/TelegramEngineNotices.swift b/submodules/TelegramCore/Sources/TelegramEngine/Notices/TelegramEngineNotices.swift index b61fab28f6..e99676a0a6 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Notices/TelegramEngineNotices.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Notices/TelegramEngineNotices.swift @@ -25,11 +25,11 @@ public extension TelegramEngine { return _internal_getServerProvidedSuggestions(account: self.account) } - public func getServerDismissedSuggestions() -> Signal<[ServerProvidedSuggestion], NoError> { + public func getServerDismissedSuggestions() -> Signal<[String], NoError> { return _internal_getServerDismissedSuggestions(account: self.account) } - public func dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion) -> Signal { + public func dismissServerProvidedSuggestion(suggestion: String) -> Signal { return _internal_dismissServerProvidedSuggestion(account: self.account, suggestion: suggestion) } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 764de13781..15b2105611 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -849,7 +849,7 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p } })) items[.phone]!.append(PeerInfoScreenActionItem(id: 1, text: presentationData.strings.Settings_KeepPhoneNumber(phoneNumber).string, action: { - let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: .validatePhoneNumber).startStandalone() + let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.validatePhoneNumber.id).startStandalone() })) items[.phone]!.append(PeerInfoScreenActionItem(id: 2, text: presentationData.strings.Settings_ChangePhoneNumber, action: { interaction.openSettings(.phoneNumber) @@ -858,7 +858,7 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p items[.phone]!.append(PeerInfoScreenInfoItem(id: 0, title: presentationData.strings.Settings_CheckPasswordTitle, text: .markdown(presentationData.strings.Settings_CheckPasswordText), linkAction: { _ in })) items[.phone]!.append(PeerInfoScreenActionItem(id: 1, text: presentationData.strings.Settings_KeepPassword, action: { - let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: .validatePassword).startStandalone() + let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.validatePassword.id).startStandalone() })) items[.phone]!.append(PeerInfoScreenActionItem(id: 2, text: presentationData.strings.Settings_TryEnterPassword, action: { interaction.openSettings(.rememberPassword) @@ -10370,7 +10370,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro guard let self else { return } - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .setupPassword).startStandalone() + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPassword.id).startStandalone() }) let controller = self.context.sharedContext.makeSetupTwoFactorAuthController(context: self.context) @@ -10498,7 +10498,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return twoStepVerificationUnlockSettingsController(context: context, mode: .access(intro: false, data: .single(TwoStepVerificationUnlockSettingsControllerData.access(configuration: TwoStepVerificationAccessConfiguration(configuration: configuration, password: nil))))) } controller.passwordRemembered = { - let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: .validatePassword).startStandalone() + let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.validatePassword.id).startStandalone() } push(controller) case .emojiStatus: diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index 33e9eee299..e4795c62ec 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -4815,7 +4815,7 @@ extension ChatControllerImpl { self?.displayChecksTooltip() } self.shouldDisplayChecksTooltip = false - self.checksTooltipDisposable.set(self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .newcomerTicks).startStrict()) + self.checksTooltipDisposable.set(self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.newcomerTicks.id).startStrict()) } if let shouldDisplayProcessingVideoTooltip = self.shouldDisplayProcessingVideoTooltip { diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index b6c6367094..814506b77f 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2856,7 +2856,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { guard let controller else { return } - let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: .setupBirthday).startStandalone() + let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupBirthday.id).startStandalone() let settingsPromise: Promise if let rootController = context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface, let current = rootController.getPrivacySettings() { @@ -3047,7 +3047,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { guard let controller else { return } - let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: .setupBirthday).startStandalone() + let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupBirthday.id).startStandalone() let settingsPromise: Promise if let rootController = context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface, let current = rootController.getPrivacySettings() {