mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Chatlist suggestions
This commit is contained in:
parent
084bb5bcd5
commit
e77402d7b3
@ -3368,6 +3368,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
}, openAdInfo: { node, adPeer in
|
}, openAdInfo: { node, adPeer in
|
||||||
interaction.openAdInfo(node, adPeer)
|
interaction.openAdInfo(node, adPeer)
|
||||||
}, openAccountFreezeInfo: {
|
}, openAccountFreezeInfo: {
|
||||||
|
}, openUrl: { _ in
|
||||||
})
|
})
|
||||||
chatListInteraction.isSearchMode = true
|
chatListInteraction.isSearchMode = true
|
||||||
|
|
||||||
@ -5363,6 +5364,7 @@ public final class ChatListSearchShimmerNode: ASDisplayNode {
|
|||||||
}, openPhotoSetup: {
|
}, openPhotoSetup: {
|
||||||
}, openAdInfo: { _, _ in
|
}, openAdInfo: { _, _ in
|
||||||
}, openAccountFreezeInfo: {
|
}, openAccountFreezeInfo: {
|
||||||
|
}, openUrl: { _ in
|
||||||
})
|
})
|
||||||
var isInlineMode = false
|
var isInlineMode = false
|
||||||
if case .topics = key {
|
if case .topics = key {
|
||||||
|
@ -163,6 +163,7 @@ public final class ChatListShimmerNode: ASDisplayNode {
|
|||||||
}, openPhotoSetup: {
|
}, openPhotoSetup: {
|
||||||
}, openAdInfo: { _, _ in
|
}, openAdInfo: { _, _ in
|
||||||
}, openAccountFreezeInfo: {
|
}, openAccountFreezeInfo: {
|
||||||
|
}, openUrl: { _ in
|
||||||
})
|
})
|
||||||
interaction.isInlineMode = isInlineMode
|
interaction.isInlineMode = isInlineMode
|
||||||
|
|
||||||
|
@ -116,6 +116,7 @@ public final class ChatListNodeInteraction {
|
|||||||
let openPhotoSetup: () -> Void
|
let openPhotoSetup: () -> Void
|
||||||
let openAdInfo: (ASDisplayNode, AdPeer) -> Void
|
let openAdInfo: (ASDisplayNode, AdPeer) -> Void
|
||||||
let openAccountFreezeInfo: () -> Void
|
let openAccountFreezeInfo: () -> Void
|
||||||
|
let openUrl: (String) -> Void
|
||||||
|
|
||||||
public var searchTextHighightState: String?
|
public var searchTextHighightState: String?
|
||||||
var highlightedChatLocation: ChatListHighlightedLocation?
|
var highlightedChatLocation: ChatListHighlightedLocation?
|
||||||
@ -175,7 +176,8 @@ public final class ChatListNodeInteraction {
|
|||||||
openWebApp: @escaping (TelegramUser) -> Void,
|
openWebApp: @escaping (TelegramUser) -> Void,
|
||||||
openPhotoSetup: @escaping () -> Void,
|
openPhotoSetup: @escaping () -> Void,
|
||||||
openAdInfo: @escaping (ASDisplayNode, AdPeer) -> Void,
|
openAdInfo: @escaping (ASDisplayNode, AdPeer) -> Void,
|
||||||
openAccountFreezeInfo: @escaping () -> Void
|
openAccountFreezeInfo: @escaping () -> Void,
|
||||||
|
openUrl: @escaping (String) -> Void
|
||||||
) {
|
) {
|
||||||
self.activateSearch = activateSearch
|
self.activateSearch = activateSearch
|
||||||
self.peerSelected = peerSelected
|
self.peerSelected = peerSelected
|
||||||
@ -223,6 +225,7 @@ public final class ChatListNodeInteraction {
|
|||||||
self.openPhotoSetup = openPhotoSetup
|
self.openPhotoSetup = openPhotoSetup
|
||||||
self.openAdInfo = openAdInfo
|
self.openAdInfo = openAdInfo
|
||||||
self.openAccountFreezeInfo = openAccountFreezeInfo
|
self.openAccountFreezeInfo = openAccountFreezeInfo
|
||||||
|
self.openUrl = openUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,6 +778,8 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
|||||||
nodeInteraction?.openPhotoSetup()
|
nodeInteraction?.openPhotoSetup()
|
||||||
case .accountFreeze:
|
case .accountFreeze:
|
||||||
nodeInteraction?.openAccountFreezeInfo()
|
nodeInteraction?.openAccountFreezeInfo()
|
||||||
|
case let .link(url, _, _):
|
||||||
|
nodeInteraction?.openUrl(url)
|
||||||
}
|
}
|
||||||
case .hide:
|
case .hide:
|
||||||
nodeInteraction?.dismissNotice(notice)
|
nodeInteraction?.dismissNotice(notice)
|
||||||
@ -1123,6 +1128,8 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
|||||||
nodeInteraction?.openPhotoSetup()
|
nodeInteraction?.openPhotoSetup()
|
||||||
case .accountFreeze:
|
case .accountFreeze:
|
||||||
nodeInteraction?.openAccountFreezeInfo()
|
nodeInteraction?.openAccountFreezeInfo()
|
||||||
|
case let .link(url, _, _):
|
||||||
|
nodeInteraction?.openUrl(url)
|
||||||
}
|
}
|
||||||
case .hide:
|
case .hide:
|
||||||
nodeInteraction?.dismissNotice(notice)
|
nodeInteraction?.dismissNotice(notice)
|
||||||
@ -1906,6 +1913,12 @@ public final class ChatListNode: ListView {
|
|||||||
self?.openAdInfo?(node, adPeer)
|
self?.openAdInfo?(node, adPeer)
|
||||||
}, openAccountFreezeInfo: { [weak self] in
|
}, openAccountFreezeInfo: { [weak self] in
|
||||||
self?.openAccountFreezeInfo?()
|
self?.openAccountFreezeInfo?()
|
||||||
|
}, openUrl: { [weak self] url in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: false, presentationData: presentationData, navigationController: self.context.sharedContext.mainWindow?.viewController as? NavigationController, dismissInput: {})
|
||||||
})
|
})
|
||||||
nodeInteraction.isInlineMode = isInlineMode
|
nodeInteraction.isInlineMode = isInlineMode
|
||||||
|
|
||||||
@ -2134,6 +2147,8 @@ public final class ChatListNode: ListView {
|
|||||||
}
|
}
|
||||||
return .birthdayPremiumGift(peers: todayBirthdayPeers, birthdays: birthdays)
|
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 {
|
} else {
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,7 @@ public enum ChatListNotice: Equatable {
|
|||||||
case starsSubscriptionLowBalance(amount: StarsAmount, peers: [EnginePeer])
|
case starsSubscriptionLowBalance(amount: StarsAmount, peers: [EnginePeer])
|
||||||
case setupPhoto(EnginePeer)
|
case setupPhoto(EnginePeer)
|
||||||
case accountFreeze
|
case accountFreeze
|
||||||
|
case link(url: String, title: String, subtitle: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ChatListNodeEntry: Comparable, Identifiable {
|
enum ChatListNodeEntry: Comparable, Identifiable {
|
||||||
|
@ -291,6 +291,9 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
|||||||
case .accountFreeze:
|
case .accountFreeze:
|
||||||
titleString = NSAttributedString(string: item.strings.ChatList_FrozenAccount_Title, font: titleFont, textColor: item.theme.list.itemDestructiveColor)
|
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)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
var leftInset: CGFloat = sideInset
|
var leftInset: CGFloat = sideInset
|
||||||
@ -383,7 +386,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
let hasCloseButton: Bool
|
let hasCloseButton: Bool
|
||||||
switch item.notice {
|
switch item.notice {
|
||||||
case .xmasPremiumGift, .setupBirthday, .birthdayPremiumGift, .premiumGrace, .starsSubscriptionLowBalance, .setupPhoto:
|
case .xmasPremiumGift, .setupBirthday, .birthdayPremiumGift, .premiumGrace, .starsSubscriptionLowBalance, .setupPhoto, .link:
|
||||||
hasCloseButton = true
|
hasCloseButton = true
|
||||||
default:
|
default:
|
||||||
hasCloseButton = false
|
hasCloseButton = false
|
||||||
|
@ -234,6 +234,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, ASScrollView
|
|||||||
}, openPhotoSetup: {
|
}, openPhotoSetup: {
|
||||||
}, openAdInfo: { _, _ in
|
}, openAdInfo: { _, _ in
|
||||||
}, openAccountFreezeInfo: {
|
}, openAccountFreezeInfo: {
|
||||||
|
}, openUrl: { _ in
|
||||||
})
|
})
|
||||||
|
|
||||||
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)
|
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)
|
||||||
|
@ -383,6 +383,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
}, openPhotoSetup: {
|
}, openPhotoSetup: {
|
||||||
}, openAdInfo: { _, _ in
|
}, openAdInfo: { _, _ in
|
||||||
}, openAccountFreezeInfo: {
|
}, openAccountFreezeInfo: {
|
||||||
|
}, openUrl: { _ in
|
||||||
})
|
})
|
||||||
|
|
||||||
func makeChatListItem(
|
func makeChatListItem(
|
||||||
|
@ -72,6 +72,112 @@ public final class PromoChatListItem: AdditionalChatListItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public final class ServerSuggestionInfo: Codable, Equatable {
|
||||||
|
public final class Item: Codable, Equatable {
|
||||||
|
public final class Text: Codable, Equatable {
|
||||||
|
public let string: String
|
||||||
|
public let entities: [MessageTextEntity]
|
||||||
|
|
||||||
|
public init(string: String, entities: [MessageTextEntity]) {
|
||||||
|
self.string = string
|
||||||
|
self.entities = entities
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: Text, rhs: Text) -> Bool {
|
||||||
|
if lhs.string != rhs.string {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.entities != rhs.entities {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Action: Codable, Equatable {
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case link
|
||||||
|
}
|
||||||
|
|
||||||
|
case link(url: String)
|
||||||
|
|
||||||
|
public init(from decoder: any Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
self = .link(url: try container.decode(String.self, forKey: .link))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public let id: String
|
||||||
|
public let title: Text
|
||||||
|
public let text: Text
|
||||||
|
public let action: Action
|
||||||
|
|
||||||
|
public init(id: String, title: Text, text: Text, action: Action) {
|
||||||
|
self.id = id
|
||||||
|
self.title = title
|
||||||
|
self.text = text
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||||
|
if lhs.id != rhs.id {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.title != rhs.title {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.text != rhs.text {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.action != rhs.action {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public let legacyItems: [String]
|
||||||
|
public let items: [Item]
|
||||||
|
public let dismissedIds: [String]
|
||||||
|
|
||||||
|
public init(legacyItems: [String], items: [Item], dismissedIds: [String]) {
|
||||||
|
self.legacyItems = legacyItems
|
||||||
|
self.items = items
|
||||||
|
self.dismissedIds = dismissedIds
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: ServerSuggestionInfo, rhs: ServerSuggestionInfo) -> Bool {
|
||||||
|
if lhs.items != rhs.items {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ServerSuggestionInfo.Item.Text {
|
||||||
|
convenience init(_ apiText: Api.TextWithEntities) {
|
||||||
|
switch apiText {
|
||||||
|
case let .textWithEntities(text, entities):
|
||||||
|
self.init(string: text, entities: messageTextEntitiesFromApiEntities(entities))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ServerSuggestionInfo.Item {
|
||||||
|
convenience init(_ apiItem: Api.PendingSuggestion) {
|
||||||
|
switch apiItem {
|
||||||
|
case let .pendingSuggestion(suggestion, title, description, url):
|
||||||
|
self.init(
|
||||||
|
id: suggestion,
|
||||||
|
title: ServerSuggestionInfo.Item.Text(title),
|
||||||
|
text: ServerSuggestionInfo.Item.Text(description),
|
||||||
|
action: .link(url: url)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func managedPromoInfoUpdates(accountPeerId: PeerId, postbox: Postbox, network: Network, viewTracker: AccountViewTracker) -> Signal<Void, NoError> {
|
func managedPromoInfoUpdates(accountPeerId: PeerId, postbox: Postbox, network: Network, viewTracker: AccountViewTracker) -> Signal<Void, NoError> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
let queue = Queue()
|
let queue = Queue()
|
||||||
@ -88,24 +194,38 @@ func managedPromoInfoUpdates(accountPeerId: PeerId, postbox: Postbox, network: N
|
|||||||
switch data {
|
switch data {
|
||||||
case .promoDataEmpty:
|
case .promoDataEmpty:
|
||||||
transaction.replaceAdditionalChatListItems([])
|
transaction.replaceAdditionalChatListItems([])
|
||||||
case let .promoData(_, _, peer, chats, users, psaType, psaMessage):
|
case let .promoData(flags, expires, peer, psaType, psaMessage, pendingSuggestions, dismissedSuggestions, customPendingSuggestion, chats, users):
|
||||||
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users)
|
let _ = expires
|
||||||
|
|
||||||
|
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users)
|
||||||
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
|
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
|
||||||
|
|
||||||
let kind: PromoChatListItem.Kind
|
var kind: PromoChatListItem.Kind?
|
||||||
if let psaType = psaType {
|
if let psaType {
|
||||||
kind = .psa(type: psaType, message: psaMessage)
|
kind = .psa(type: psaType, message: psaMessage)
|
||||||
} else {
|
} else if ((flags & 1) << 0) != 0 {
|
||||||
kind = .proxy
|
kind = .proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
var additionalChatListItems: [AdditionalChatListItem] = []
|
var additionalChatListItems: [AdditionalChatListItem] = []
|
||||||
if let parsedPeer = transaction.getPeer(peer.peerId) {
|
if let kind, let peer, let parsedPeer = transaction.getPeer(peer.peerId) {
|
||||||
additionalChatListItems.append(PromoChatListItem(peerId: parsedPeer.id, kind: kind))
|
additionalChatListItems.append(PromoChatListItem(peerId: parsedPeer.id, kind: kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction.replaceAdditionalChatListItems(additionalChatListItems)
|
transaction.replaceAdditionalChatListItems(additionalChatListItems)
|
||||||
|
|
||||||
|
var customItems: [ServerSuggestionInfo.Item] = []
|
||||||
|
if let customPendingSuggestion {
|
||||||
|
customItems.append(ServerSuggestionInfo.Item(customPendingSuggestion))
|
||||||
|
}
|
||||||
|
let suggestionInfo = ServerSuggestionInfo(
|
||||||
|
legacyItems: pendingSuggestions,
|
||||||
|
items: customItems,
|
||||||
|
dismissedIds: dismissedSuggestions
|
||||||
|
)
|
||||||
|
|
||||||
|
transaction.updatePreferencesEntry(key: PreferencesKeys.serverSuggestionInfo(), { _ in
|
||||||
|
return PreferencesEntry(suggestionInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,21 +3,124 @@ import Postbox
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import TelegramApi
|
import TelegramApi
|
||||||
|
|
||||||
public enum ServerProvidedSuggestion: String {
|
public enum ServerProvidedSuggestion: Hashable {
|
||||||
case autoarchivePopular = "AUTOARCHIVE_POPULAR"
|
case autoarchivePopular
|
||||||
case newcomerTicks = "NEWCOMER_TICKS"
|
case newcomerTicks
|
||||||
case validatePhoneNumber = "VALIDATE_PHONE_NUMBER"
|
case validatePhoneNumber
|
||||||
case validatePassword = "VALIDATE_PASSWORD"
|
case validatePassword
|
||||||
case setupPassword = "SETUP_PASSWORD"
|
case setupPassword
|
||||||
case upgradePremium = "PREMIUM_UPGRADE"
|
case upgradePremium
|
||||||
case annualPremium = "PREMIUM_ANNUAL"
|
case annualPremium
|
||||||
case restorePremium = "PREMIUM_RESTORE"
|
case restorePremium
|
||||||
case xmasPremiumGift = "PREMIUM_CHRISTMAS"
|
case xmasPremiumGift
|
||||||
case setupBirthday = "BIRTHDAY_SETUP"
|
case setupBirthday
|
||||||
case todayBirthdays = "BIRTHDAY_CONTACTS_TODAY"
|
case todayBirthdays
|
||||||
case gracePremium = "PREMIUM_GRACE"
|
case gracePremium
|
||||||
case starsSubscriptionLowBalance = "STARS_SUBSCRIPTION_LOW_BALANCE"
|
case starsSubscriptionLowBalance
|
||||||
case setupPhoto = "USERPIC_SETUP"
|
case setupPhoto
|
||||||
|
case link(url: String, title: String, subtitle: String)
|
||||||
|
|
||||||
|
public init?(string: String) {
|
||||||
|
switch string {
|
||||||
|
case "AUTOARCHIVE_POPULAR":
|
||||||
|
self = .autoarchivePopular
|
||||||
|
case "NEWCOMER_TICKS":
|
||||||
|
self = .newcomerTicks
|
||||||
|
case "VALIDATE_PHONE_NUMBER":
|
||||||
|
self = .validatePhoneNumber
|
||||||
|
case "VALIDATE_PASSWORD":
|
||||||
|
self = .validatePassword
|
||||||
|
case "SETUP_PASSWORD":
|
||||||
|
self = .setupPassword
|
||||||
|
case "PREMIUM_UPGRADE":
|
||||||
|
self = .upgradePremium
|
||||||
|
case "PREMIUM_ANNUAL":
|
||||||
|
self = .annualPremium
|
||||||
|
case "PREMIUM_RESTORE":
|
||||||
|
self = .restorePremium
|
||||||
|
case "PREMIUM_CHRISTMAS":
|
||||||
|
self = .xmasPremiumGift
|
||||||
|
case "BIRTHDAY_SETUP":
|
||||||
|
self = .setupBirthday
|
||||||
|
case "BIRTHDAY_CONTACTS_TODAY":
|
||||||
|
self = .todayBirthdays
|
||||||
|
case "PREMIUM_GRACE":
|
||||||
|
self = .gracePremium
|
||||||
|
case "STARS_SUBSCRIPTION_LOW_BALANCE":
|
||||||
|
self = .starsSubscriptionLowBalance
|
||||||
|
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 {
|
||||||
|
switch self {
|
||||||
|
case .autoarchivePopular:
|
||||||
|
return "AUTOARCHIVE_POPULAR"
|
||||||
|
case .newcomerTicks:
|
||||||
|
return "NEWCOMER_TICKS"
|
||||||
|
case .validatePhoneNumber:
|
||||||
|
return "VALIDATE_PHONE_NUMBER"
|
||||||
|
case .validatePassword:
|
||||||
|
return "VALIDATE_PASSWORD"
|
||||||
|
case .setupPassword:
|
||||||
|
return "SETUP_PASSWORD"
|
||||||
|
case .upgradePremium:
|
||||||
|
return "PREMIUM_UPGRADE"
|
||||||
|
case .annualPremium:
|
||||||
|
return "PREMIUM_ANNUAL"
|
||||||
|
case .restorePremium:
|
||||||
|
return "PREMIUM_RESTORE"
|
||||||
|
case .xmasPremiumGift:
|
||||||
|
return "PREMIUM_CHRISTMAS"
|
||||||
|
case .setupBirthday:
|
||||||
|
return "BIRTHDAY_SETUP"
|
||||||
|
case .todayBirthdays:
|
||||||
|
return "BIRTHDAY_CONTACTS_TODAY"
|
||||||
|
case .gracePremium:
|
||||||
|
return "PREMIUM_GRACE"
|
||||||
|
case .starsSubscriptionLowBalance:
|
||||||
|
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_{}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var dismissedSuggestionsPromise = ValuePromise<[AccountRecordId: Set<ServerProvidedSuggestion>]>([:])
|
private var dismissedSuggestionsPromise = ValuePromise<[AccountRecordId: Set<ServerProvidedSuggestion>]>([:])
|
||||||
@ -38,11 +141,20 @@ func _internal_getServerProvidedSuggestions(account: Account) -> Signal<[ServerP
|
|||||||
guard let appConfiguration = view.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) else {
|
guard let appConfiguration = view.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
guard let data = appConfiguration.data, var listItems = data["pending_suggestions"] as? [String] else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
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 {
|
guard let data = appConfiguration.data, let listItems = data["pending_suggestions"] as? [String] else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return listItems.compactMap { item -> ServerProvidedSuggestion? in
|
return listItems.compactMap { item -> ServerProvidedSuggestion? in
|
||||||
return ServerProvidedSuggestion(rawValue: item)
|
return ServerProvidedSuggestion(string: item)
|
||||||
}.filter { !dismissedSuggestions.contains($0) }
|
}.filter { !dismissedSuggestions.contains($0) }
|
||||||
}
|
}
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
@ -64,7 +176,7 @@ func _internal_getServerDismissedSuggestions(account: Account) -> Signal<[Server
|
|||||||
listItems.append(contentsOf: listItemsValues)
|
listItems.append(contentsOf: listItemsValues)
|
||||||
}
|
}
|
||||||
var items = listItems.compactMap { item -> ServerProvidedSuggestion? in
|
var items = listItems.compactMap { item -> ServerProvidedSuggestion? in
|
||||||
return ServerProvidedSuggestion(rawValue: item)
|
return ServerProvidedSuggestion(string: item)
|
||||||
}
|
}
|
||||||
items.append(contentsOf: dismissedSuggestions)
|
items.append(contentsOf: dismissedSuggestions)
|
||||||
return items
|
return items
|
||||||
@ -78,7 +190,7 @@ func _internal_dismissServerProvidedSuggestion(account: Account, suggestion: Ser
|
|||||||
} else {
|
} else {
|
||||||
dismissedSuggestions[account.id] = Set([suggestion])
|
dismissedSuggestions[account.id] = Set([suggestion])
|
||||||
}
|
}
|
||||||
return account.network.request(Api.functions.help.dismissSuggestion(peer: .inputPeerEmpty, suggestion: suggestion.rawValue))
|
return account.network.request(Api.functions.help.dismissSuggestion(peer: .inputPeerEmpty, suggestion: suggestion.stringValue))
|
||||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||||
return .single(.boolFalse)
|
return .single(.boolFalse)
|
||||||
}
|
}
|
||||||
|
@ -315,6 +315,7 @@ private enum PreferencesKeyValues: Int32 {
|
|||||||
case starGifts = 41
|
case starGifts = 41
|
||||||
case botStorageState = 42
|
case botStorageState = 42
|
||||||
case secureBotStorageState = 43
|
case secureBotStorageState = 43
|
||||||
|
case serverSuggestionInfo = 44
|
||||||
}
|
}
|
||||||
|
|
||||||
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
|
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
|
||||||
@ -558,6 +559,12 @@ public struct PreferencesKeys {
|
|||||||
key.setInt32(0, value: PreferencesKeyValues.secureBotStorageState.rawValue)
|
key.setInt32(0, value: PreferencesKeyValues.secureBotStorageState.rawValue)
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func serverSuggestionInfo() -> ValueBoxKey {
|
||||||
|
let key = ValueBoxKey(length: 4 + 8)
|
||||||
|
key.setInt32(0, value: PreferencesKeyValues.serverSuggestionInfo.rawValue)
|
||||||
|
return key
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum SharedDataKeyValues: Int32 {
|
private enum SharedDataKeyValues: Int32 {
|
||||||
|
@ -202,6 +202,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
|||||||
case captionAboveMediaTooltip = 75
|
case captionAboveMediaTooltip = 75
|
||||||
case channelSendGiftTooltip = 76
|
case channelSendGiftTooltip = 76
|
||||||
case starGiftWearTips = 77
|
case starGiftWearTips = 77
|
||||||
|
case channelSuggestTooltip = 78
|
||||||
|
|
||||||
var key: ValueBoxKey {
|
var key: ValueBoxKey {
|
||||||
let v = ValueBoxKey(length: 4)
|
let v = ValueBoxKey(length: 4)
|
||||||
@ -559,6 +560,10 @@ private struct ApplicationSpecificNoticeKeys {
|
|||||||
static func starGiftWearTips() -> NoticeEntryKey {
|
static func starGiftWearTips() -> NoticeEntryKey {
|
||||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.starGiftWearTips.key)
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.starGiftWearTips.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func channelSuggestTooltip() -> NoticeEntryKey {
|
||||||
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.channelSuggestTooltip.key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ApplicationSpecificNotice {
|
public struct ApplicationSpecificNotice {
|
||||||
@ -2368,6 +2373,33 @@ public struct ApplicationSpecificNotice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func getChannelSuggestTooltip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
||||||
|
return accountManager.transaction { transaction -> Int32 in
|
||||||
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.channelSuggestTooltip())?.get(ApplicationSpecificCounterNotice.self) {
|
||||||
|
return value.value
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func incrementChannelSuggestTooltip(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int = 1) -> Signal<Int, NoError> {
|
||||||
|
return accountManager.transaction { transaction -> Int in
|
||||||
|
var currentValue: Int32 = 0
|
||||||
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.channelSuggestTooltip())?.get(ApplicationSpecificCounterNotice.self) {
|
||||||
|
currentValue = value.value
|
||||||
|
}
|
||||||
|
let previousValue = currentValue
|
||||||
|
currentValue += Int32(count)
|
||||||
|
|
||||||
|
if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) {
|
||||||
|
transaction.setNotice(ApplicationSpecificNoticeKeys.channelSuggestTooltip(), entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Int(previousValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static func getStarGiftWearTips(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
public static func getStarGiftWearTips(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
||||||
return accountManager.transaction { transaction -> Int32 in
|
return accountManager.transaction { transaction -> Int32 in
|
||||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.starGiftWearTips())?.get(ApplicationSpecificCounterNotice.self) {
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.starGiftWearTips())?.get(ApplicationSpecificCounterNotice.self) {
|
||||||
|
@ -689,6 +689,7 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
openAdInfo: { _, _ in
|
openAdInfo: { _, _ in
|
||||||
},
|
},
|
||||||
openAccountFreezeInfo: {
|
openAccountFreezeInfo: {
|
||||||
|
}, openUrl: { _ in
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.chatListNodeInteraction = chatListNodeInteraction
|
self.chatListNodeInteraction = chatListNodeInteraction
|
||||||
|
@ -189,6 +189,7 @@ public final class LoadingOverlayNode: ASDisplayNode {
|
|||||||
}, openPhotoSetup: {
|
}, openPhotoSetup: {
|
||||||
}, openAdInfo: { _, _ in
|
}, openAdInfo: { _, _ in
|
||||||
}, openAccountFreezeInfo: {
|
}, openAccountFreezeInfo: {
|
||||||
|
}, openUrl: { _ in
|
||||||
})
|
})
|
||||||
|
|
||||||
let items = (0 ..< 1).map { _ -> ChatListItem in
|
let items = (0 ..< 1).map { _ -> ChatListItem in
|
||||||
@ -551,6 +552,8 @@ private final class PeerInfoScreenPersonalChannelItemNode: PeerInfoScreenItemNod
|
|||||||
openAdInfo: { _, _ in
|
openAdInfo: { _, _ in
|
||||||
},
|
},
|
||||||
openAccountFreezeInfo: {
|
openAccountFreezeInfo: {
|
||||||
|
},
|
||||||
|
openUrl: { _ in
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -215,6 +215,8 @@ final class GreetingMessageListItemComponent: Component {
|
|||||||
openAdInfo: { _, _ in
|
openAdInfo: { _, _ in
|
||||||
},
|
},
|
||||||
openAccountFreezeInfo: {
|
openAccountFreezeInfo: {
|
||||||
|
},
|
||||||
|
openUrl: { _ in
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.chatListNodeInteraction = chatListNodeInteraction
|
self.chatListNodeInteraction = chatListNodeInteraction
|
||||||
|
@ -236,6 +236,8 @@ final class QuickReplySetupScreenComponent: Component {
|
|||||||
openAdInfo: { _, _ in
|
openAdInfo: { _, _ in
|
||||||
},
|
},
|
||||||
openAccountFreezeInfo: {
|
openAccountFreezeInfo: {
|
||||||
|
},
|
||||||
|
openUrl: { _ in
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -877,6 +877,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, ASScrollViewDelegate
|
|||||||
}, openPhotoSetup: {
|
}, openPhotoSetup: {
|
||||||
}, openAdInfo: { _, _ in
|
}, openAdInfo: { _, _ in
|
||||||
}, openAccountFreezeInfo: {
|
}, openAccountFreezeInfo: {
|
||||||
|
}, openUrl: { _ in
|
||||||
})
|
})
|
||||||
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)
|
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)
|
||||||
|
|
||||||
|
@ -297,6 +297,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, ASScrollViewDe
|
|||||||
}, openPhotoSetup: {
|
}, openPhotoSetup: {
|
||||||
}, openAdInfo: { _, _ in
|
}, openAdInfo: { _, _ in
|
||||||
}, openAccountFreezeInfo: {
|
}, openAccountFreezeInfo: {
|
||||||
|
}, openUrl: { _ in
|
||||||
})
|
})
|
||||||
interaction.searchTextHighightState = searchQuery
|
interaction.searchTextHighightState = searchQuery
|
||||||
self.interaction = interaction
|
self.interaction = interaction
|
||||||
|
@ -185,6 +185,8 @@ private struct CommandChatInputContextPanelEntry: Comparable, Identifiable {
|
|||||||
openAdInfo: { _, _ in
|
openAdInfo: { _, _ in
|
||||||
},
|
},
|
||||||
openAccountFreezeInfo: {
|
openAccountFreezeInfo: {
|
||||||
|
},
|
||||||
|
openUrl: { _ in
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user