mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Update
This commit is contained in:
parent
b20652120e
commit
b8ac955792
20
MODULE.bazel.lock
generated
20
MODULE.bazel.lock
generated
@ -81,12 +81,12 @@
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.17/source.json": "4db99b3f55c90ab28d14552aa0632533e3e8e5e9aea0f5c24ac0014282c2a7c5",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.1.1/source.json": "d61627377bd7dd1da4652063e368d9366fc9a73920bfa396798ad92172cf645c",
|
||||
"https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
|
||||
"https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8",
|
||||
"https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e",
|
||||
@ -100,8 +100,8 @@
|
||||
"https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab",
|
||||
"https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2",
|
||||
"https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.12.0/MODULE.bazel": "8e6590b961f2defdfc2811c089c75716cb2f06c8a4edeb9a8d85eaa64ee2a761",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.12.0/source.json": "cbd5d55d9d38d4008a7d00bee5b5a5a4b6031fcd4a56515c9accbcd42c7be2ba",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.11.0/MODULE.bazel": "c3d280bc5ff1038dcb3bacb95d3f6b83da8dd27bba57820ec89ea4085da767ad",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.11.0/source.json": "302b52a39259a85aa06ca3addb9787864ca3e03b432a5f964ea68244397e7544",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939",
|
||||
"https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7",
|
||||
@ -151,15 +151,15 @@
|
||||
"https://bcr.bazel.build/modules/xcodeproj/8.27.3/MODULE.bazel": "49276599207dae3df1e4336c2067505323dfb0606b53ef63e144087d1226e0eb",
|
||||
"https://bcr.bazel.build/modules/xcodeproj/8.27.3/source.json": "bbbb718187dcbdfbb3a9a0ec7d49446cdf48c67657cafd79b5cf33aa8918f608",
|
||||
"https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
|
||||
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca",
|
||||
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806",
|
||||
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79",
|
||||
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d",
|
||||
"https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198"
|
||||
},
|
||||
"selectedYankedVersions": {},
|
||||
"moduleExtensions": {
|
||||
"@@apple_support+//crosstool:setup.bzl%apple_cc_configure_extension": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "RjubjYIojbv0PxTpnoknalV9QzT9asbV7elDuN7m2A4=",
|
||||
"bzlTransitiveDigest": "IK7QnlhcNBu2jc4wZoGZeDTu3keF2LldFiFUINRcKvo=",
|
||||
"usagesDigest": "lfcV4HxPD+NLaRIT/v7BtSGFgE7c9xrWU7jDiwBAxzo=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
@ -190,7 +190,7 @@
|
||||
},
|
||||
"@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "hUTp2w+RUVdL7ma5esCXZJAFnX7vLbVfLd7FwnQI6bU=",
|
||||
"bzlTransitiveDigest": "sFhcgPbDQehmbD1EOXzX4H1q/CD5df8zwG4kp4jbvr8=",
|
||||
"usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
@ -290,7 +290,7 @@
|
||||
},
|
||||
"@@rules_xcodeproj+//xcodeproj:extensions.bzl%internal": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "6MYik+6MZUO7rOzaI0dUJYVD8dJrR1Q2rT+5vo1j73U=",
|
||||
"bzlTransitiveDigest": "+kmqZtEKFY8zgqpV6mrwdQkTJqGUZhL8b3ZMsxrqSyc=",
|
||||
"usagesDigest": "fvsnMonVwKDYnBfww4bXuYie3WU0d9VSqT2gePSdQco=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
@ -312,7 +312,7 @@
|
||||
},
|
||||
"@@rules_xcodeproj+//xcodeproj:extensions.bzl%non_module_deps": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "6MYik+6MZUO7rOzaI0dUJYVD8dJrR1Q2rT+5vo1j73U=",
|
||||
"bzlTransitiveDigest": "+kmqZtEKFY8zgqpV6mrwdQkTJqGUZhL8b3ZMsxrqSyc=",
|
||||
"usagesDigest": "jzxYhnOC9BE0dJ0biFLfxWXi/+R19uAAZkJ0p9CY0JI=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
|
@ -116,6 +116,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/Ads/AdsInfoScreen",
|
||||
"//submodules/TelegramUI/Components/Ads/AdsReportScreen",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/TelegramUI/Components/AnimatedTextComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -37,6 +37,7 @@ import ComponentFlow
|
||||
import MultilineTextComponent
|
||||
import ButtonComponent
|
||||
import BundleIconComponent
|
||||
import AnimatedTextComponent
|
||||
|
||||
private enum ChatListRecentEntryStableId: Hashable {
|
||||
case topPeers
|
||||
@ -1296,19 +1297,19 @@ public struct ChatListSearchContainerTransition {
|
||||
public let isEmpty: Bool
|
||||
public let isLoading: Bool
|
||||
public let query: String?
|
||||
public let approvedGlobalPostQuery: String?
|
||||
public let approvedGlobalPostQueryState: ApprovedGlobalPostQueryState?
|
||||
public let remainingGlobalSearches: Int
|
||||
public let globalSearchUnlockTimestamp: Int32?
|
||||
public var animated: Bool
|
||||
|
||||
public init(deletions: [ListViewDeleteItem], insertions: [ListViewInsertItem], updates: [ListViewUpdateItem], displayingResults: Bool, isEmpty: Bool, isLoading: Bool, query: String?, approvedGlobalPostQuery: String?, remainingGlobalSearches: Int, globalSearchUnlockTimestamp: Int32?, animated: Bool) {
|
||||
public init(deletions: [ListViewDeleteItem], insertions: [ListViewInsertItem], updates: [ListViewUpdateItem], displayingResults: Bool, isEmpty: Bool, isLoading: Bool, query: String?, approvedGlobalPostQueryState: ApprovedGlobalPostQueryState?, remainingGlobalSearches: Int, globalSearchUnlockTimestamp: Int32?, animated: Bool) {
|
||||
self.deletions = deletions
|
||||
self.insertions = insertions
|
||||
self.updates = updates
|
||||
self.displayingResults = displayingResults
|
||||
self.isEmpty = isEmpty
|
||||
self.isLoading = isLoading
|
||||
self.approvedGlobalPostQuery = approvedGlobalPostQuery
|
||||
self.approvedGlobalPostQueryState = approvedGlobalPostQueryState
|
||||
self.remainingGlobalSearches = remainingGlobalSearches
|
||||
self.globalSearchUnlockTimestamp = globalSearchUnlockTimestamp
|
||||
self.query = query
|
||||
@ -1374,7 +1375,7 @@ public func chatListSearchContainerPreparedTransition(
|
||||
toggleExpandGlobalResults: @escaping () -> Void,
|
||||
searchPeer: @escaping (EnginePeer) -> Void,
|
||||
searchQuery: String?,
|
||||
approvedGlobalPostQuery: String?,
|
||||
approvedGlobalPostQueryState: ApprovedGlobalPostQueryState?,
|
||||
remainingGlobalSearches: Int,
|
||||
globalSearchUnlockTimestamp: Int32?,
|
||||
searchOptions: ChatListSearchOptions?,
|
||||
@ -1392,7 +1393,7 @@ public func chatListSearchContainerPreparedTransition(
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, requestPeerType: requestPeerType, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused, openStories: openStories, openPublicPosts: openPublicPosts, openMessagesFilter: openMessagesFilter, switchMessagesFilter: switchMessagesFilter), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, requestPeerType: requestPeerType, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused, openStories: openStories, openPublicPosts: openPublicPosts, openMessagesFilter: openMessagesFilter, switchMessagesFilter: switchMessagesFilter), directionHint: nil) }
|
||||
|
||||
return ChatListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, displayingResults: displayingResults, isEmpty: isEmpty, isLoading: isLoading, query: searchQuery, approvedGlobalPostQuery: approvedGlobalPostQuery, remainingGlobalSearches: remainingGlobalSearches, globalSearchUnlockTimestamp: globalSearchUnlockTimestamp, animated: animated)
|
||||
return ChatListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, displayingResults: displayingResults, isEmpty: isEmpty, isLoading: isLoading, query: searchQuery, approvedGlobalPostQueryState: approvedGlobalPostQueryState, remainingGlobalSearches: remainingGlobalSearches, globalSearchUnlockTimestamp: globalSearchUnlockTimestamp, animated: animated)
|
||||
}
|
||||
|
||||
private struct ChatListSearchListPaneNodeState: Equatable {
|
||||
@ -1609,6 +1610,16 @@ final class GlobalPeerSearchContext {
|
||||
}
|
||||
}
|
||||
|
||||
public struct ApprovedGlobalPostQueryState: Equatable {
|
||||
public var query: String
|
||||
public var price: Int?
|
||||
|
||||
public init(query: String, price: Int?) {
|
||||
self.query = query
|
||||
self.price = price
|
||||
}
|
||||
}
|
||||
|
||||
struct TelegramGlobalPostSearchState: Equatable {
|
||||
var remainingSearches: Int
|
||||
var unlockTimestamp: Int32?
|
||||
@ -1664,7 +1675,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
|
||||
private var searchQueryValue: String?
|
||||
private var searchOptionsValue: ChatListSearchOptions?
|
||||
private var approvedGlobalPostQueryValue: String?
|
||||
private var approvedGlobalPostQueryStateValue: ApprovedGlobalPostQueryState?
|
||||
private var globalPostSearchStateValue: TelegramGlobalPostSearchState
|
||||
private var globalPostSearchUnlockTimer: Foundation.Timer?
|
||||
private var isPremium: Bool = false
|
||||
@ -1725,7 +1736,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
|
||||
private let searchScopePromise = ValuePromise<TelegramSearchPeersScope>(.everywhere)
|
||||
|
||||
private let approvedGlobalPostQuery = ValuePromise<String?>(nil, ignoreRepeated: true)
|
||||
private let approvedGlobalPostQueryState = ValuePromise<ApprovedGlobalPostQueryState?>(nil, ignoreRepeated: true)
|
||||
private let globalPostSearchState = Promise<TelegramGlobalPostSearchState>()
|
||||
|
||||
init(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, interaction: ChatListSearchInteraction, key: ChatListSearchPaneKey, peersFilter: ChatListNodePeersFilter, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, location: ChatListControllerLocation, searchQuery: Signal<String?, NoError>, searchOptions: Signal<ChatListSearchOptions?, NoError>, navigationController: NavigationController?, parentController: ViewController?, globalPeerSearchContext: GlobalPeerSearchContext?) {
|
||||
@ -2065,7 +2076,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
|
||||
var defaultFoundRemoteMessagesSignal: Signal<([FoundRemoteMessages], Bool), NoError> = .single(([FoundRemoteMessages(messages: [], readCounters: [:], threadsData: [:], totalCount: 0)], false))
|
||||
if key == .globalPosts {
|
||||
let searchSignal = context.engine.messages.searchMessages(location: .general(scope: .globalPosts, tags: nil, minDate: nil, maxDate: nil), query: "", state: nil, limit: 50)
|
||||
let searchSignal = context.engine.messages.searchMessages(location: .general(scope: .globalPosts(allowPaidStars: nil), tags: nil, minDate: nil, maxDate: nil), query: "", state: nil, limit: 50)
|
||||
|> map { resultData -> ChatListSearchMessagesResult in
|
||||
let (result, updatedState) = resultData
|
||||
|
||||
@ -2086,8 +2097,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
let defaultFoundRemoteMessages = Promise<([FoundRemoteMessages], Bool)>()
|
||||
defaultFoundRemoteMessages.set(defaultFoundRemoteMessagesSignal)
|
||||
|
||||
let foundItems: Signal<([ChatListSearchEntry], Bool)?, NoError> = combineLatest(queue: .mainQueue(), searchQuery, self.approvedGlobalPostQuery.get(), searchOptions, self.searchScopePromise.get(), downloadItems, globalPostSearchStateType)
|
||||
|> mapToSignal { [weak self] query, approvedGlobalPostQuery, options, searchScope, downloadItems, _ -> Signal<([ChatListSearchEntry], Bool)?, NoError> in
|
||||
let foundItems: Signal<([ChatListSearchEntry], Bool, String?)?, NoError> = combineLatest(queue: .mainQueue(), searchQuery, self.approvedGlobalPostQueryState.get(), searchOptions, self.searchScopePromise.get(), downloadItems, globalPostSearchStateType)
|
||||
|> debounceOnMainThread
|
||||
|> mapToSignal { [weak self] query, approvedGlobalPostQueryState, options, searchScope, downloadItems, _ -> Signal<([ChatListSearchEntry], Bool, String?)?, NoError> in
|
||||
if query == nil && options == nil && [.chats, .topics, .channels, .apps].contains(key) {
|
||||
let _ = currentRemotePeers.swap(nil)
|
||||
return .single(nil)
|
||||
@ -2114,7 +2126,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}
|
||||
|
||||
return combineLatest(queue: .mainQueue(), presentationDataPromise.get(), selectionPromise.get())
|
||||
|> map { presentationData, selectionState -> ([ChatListSearchEntry], Bool)? in
|
||||
|> map { presentationData, selectionState -> ([ChatListSearchEntry], Bool, String?)? in
|
||||
var entries: [ChatListSearchEntry] = []
|
||||
var existingMessageIds = Set<MessageId>()
|
||||
|
||||
@ -2182,7 +2194,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
|
||||
entries.append(.message(message, peer, nil, nil, presentationData, 1, selectionState?.contains(message.id), false, .downloaded(timestamp: item.timestamp, index: message.index), (item.resourceId, item.size, false), .recentlyDownloaded, false, nil, false, .everywhere))
|
||||
}
|
||||
return (entries.sorted(), false)
|
||||
return (entries.sorted(), false, query)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2685,7 +2697,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
foundRemoteMessages = .single(([FoundRemoteMessages(messages: [], readCounters: [:], threadsData: [:], totalCount: 0)], false))
|
||||
} else if key == .apps {
|
||||
foundRemoteMessages = .single(([FoundRemoteMessages(messages: [], readCounters: [:], threadsData: [:], totalCount: 0)], false))
|
||||
} else if key == .globalPosts && (finalQuery.isEmpty || approvedGlobalPostQuery != finalQuery) {
|
||||
} else if key == .globalPosts && (finalQuery.isEmpty || approvedGlobalPostQueryState?.query != finalQuery) {
|
||||
if finalQuery.isEmpty {
|
||||
foundRemoteMessages = defaultFoundRemoteMessages.get()
|
||||
} else {
|
||||
@ -2699,11 +2711,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
let searchSignals: [Signal<(SearchMessagesResult, SearchMessagesState), NoError>]
|
||||
|
||||
if key == .globalPosts {
|
||||
#if DEBUG && false
|
||||
searchSignals = [context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: nil, minDate: nil, maxDate: nil), query: finalQuery, state: nil, limit: 50)]
|
||||
#else
|
||||
searchSignals = [context.engine.messages.searchMessages(location: .general(scope: .globalPosts, tags: nil, minDate: nil, maxDate: nil), query: finalQuery, state: nil, limit: 50)]
|
||||
#endif
|
||||
searchSignals = [context.engine.messages.searchMessages(location: .general(scope: .globalPosts(allowPaidStars: approvedGlobalPostQueryState?.price), tags: nil, minDate: nil, maxDate: nil), query: finalQuery, state: nil, limit: 50)]
|
||||
} else {
|
||||
searchSignals = searchLocations.map { searchLocation in
|
||||
return context.engine.messages.searchMessages(location: searchLocation, query: finalQuery, state: nil, limit: 50)
|
||||
@ -2863,7 +2871,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
foundThreads,
|
||||
adsHiddenPromise.get()
|
||||
)
|
||||
|> map { accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, foundPublicMessages, presentationData, searchState, selectionState, resolvedMessage, recentPeers, allAndFoundThreads, adsHidden -> ([ChatListSearchEntry], Bool)? in
|
||||
|> map { accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, foundPublicMessages, presentationData, searchState, selectionState, resolvedMessage, recentPeers, allAndFoundThreads, adsHidden -> ([ChatListSearchEntry], Bool, String?)? in
|
||||
let isSearching = foundRemotePeers.3 || foundRemoteMessages.1 || foundPublicMessages.1
|
||||
var entries: [ChatListSearchEntry] = []
|
||||
var index = 0
|
||||
@ -3309,7 +3317,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
entries.append(.addContact(finalQuery, presentationData.theme, presentationData.strings))
|
||||
}
|
||||
|
||||
return (entries, isSearching)
|
||||
return (entries, isSearching, query)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3597,6 +3605,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
listInteraction.preferredStoryHighQuality = context.sharedContext.currentAutomaticMediaDownloadSettings.highQualityStories
|
||||
|
||||
let previousSearchItems = Atomic<[ChatListSearchEntry]?>(value: nil)
|
||||
let previousSearchQuery = Atomic<String?>(value: nil)
|
||||
let previousSelectedMessages = Atomic<Set<EngineMessage.Id>?>(value: nil)
|
||||
let previousExpandGlobalSearch = Atomic<Bool>(value: false)
|
||||
let previousAdsHidden = Atomic<Bool>(value: false)
|
||||
@ -3607,8 +3616,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
return
|
||||
}
|
||||
|
||||
if let searchQueryValue = self.searchQueryValue, searchQueryValue == self.approvedGlobalPostQueryValue {
|
||||
self.approvedGlobalPostQuery.set(nil)
|
||||
if let searchQueryValue = self.searchQueryValue, searchQueryValue == self.approvedGlobalPostQueryStateValue?.query {
|
||||
self.approvedGlobalPostQueryState.set(nil)
|
||||
}
|
||||
self.searchQueryValue = query
|
||||
|
||||
@ -3616,12 +3625,12 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
chatListInteraction?.searchTextHighightState = query
|
||||
})
|
||||
self.globalPostSearchState.set(.single(self.globalPostSearchStateValue))
|
||||
self.approvedSearchQueryDisposable = (combineLatest(queue: .mainQueue(), self.approvedGlobalPostQuery.get(), self.globalPostSearchState.get(), isPremium)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] approvedGlobalPostQuery, globalPostSearchState, isPremium in
|
||||
self.approvedSearchQueryDisposable = (combineLatest(queue: .mainQueue(), self.approvedGlobalPostQueryState.get(), self.globalPostSearchState.get(), isPremium)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] approvedGlobalPostQueryState, globalPostSearchState, isPremium in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.approvedGlobalPostQueryValue = approvedGlobalPostQuery
|
||||
self.approvedGlobalPostQueryStateValue = approvedGlobalPostQueryState
|
||||
self.globalPostSearchStateValue = globalPostSearchState
|
||||
self.isPremium = isPremium
|
||||
|
||||
@ -3664,8 +3673,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
})
|
||||
|
||||
|
||||
self.searchDisposable.set((foundItems |> mapToSignal { items -> Signal<([ChatListSearchEntry], Bool)?, NoError> in
|
||||
guard let (items, isSearching) = items else {
|
||||
self.searchDisposable.set((foundItems |> mapToSignal { items -> Signal<([ChatListSearchEntry], Bool, String?)?, NoError> in
|
||||
guard let (items, isSearching, query) = items else {
|
||||
return .single(nil)
|
||||
}
|
||||
var storyStatsIds: [EnginePeer.Id] = []
|
||||
@ -3708,7 +3717,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
requiresPremiumForMessagingPeerIds.map(TelegramEngine.EngineData.Item.Peer.IsPremiumRequiredForMessaging.init(id:))
|
||||
)
|
||||
)
|
||||
|> map { stats, requiresPremiumForMessaging -> ([ChatListSearchEntry], Bool)? in
|
||||
|> map { stats, requiresPremiumForMessaging -> ([ChatListSearchEntry], Bool, String?)? in
|
||||
var requiresPremiumForMessaging = requiresPremiumForMessaging
|
||||
if !peersFilter.contains(.onlyWriteable) {
|
||||
requiresPremiumForMessaging = [:]
|
||||
@ -3731,7 +3740,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
break
|
||||
}
|
||||
}
|
||||
return (mappedItems, isSearching)
|
||||
return (mappedItems, isSearching, query)
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] foundItems in
|
||||
@ -3743,6 +3752,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
var entriesAndFlags = foundItems?.0
|
||||
|
||||
let isSearching = foundItems?.1 ?? false
|
||||
let currentQuery = foundItems?.2
|
||||
strongSelf._isSearching.set(isSearching)
|
||||
|
||||
if strongSelf.tagMask == .photoOrVideo {
|
||||
@ -3773,6 +3783,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}
|
||||
|
||||
let previousEntries = previousSearchItems.swap(entriesAndFlags)
|
||||
let previousQuery = previousSearchQuery.swap(currentQuery)
|
||||
let newEntries = entriesAndFlags ?? []
|
||||
|
||||
let selectionChanged = (previousSelectedMessageIds == nil) != (strongSelf.selectedMessages == nil)
|
||||
@ -3780,7 +3791,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
let adsHiddenChanged = previousAdsHidden != strongSelf.adsHidden
|
||||
|
||||
let animated = selectionChanged || expandGlobalSearchChanged || adsHiddenChanged
|
||||
let firstTime = previousEntries == nil
|
||||
let firstTime = previousEntries == nil || previousQuery != currentQuery
|
||||
var transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: newEntries, displayingResults: entriesAndFlags != nil, isEmpty: !isSearching && (entriesAndFlags?.isEmpty ?? false), isLoading: isSearching, animated: animated, context: context, presentationData: strongSelf.presentationData, enableHeaders: true, filter: peersFilter, requestPeerType: requestPeerType, location: location, key: strongSelf.key, tagMask: tagMask, interaction: chatListInteraction, listInteraction: listInteraction, peerContextAction: { message, node, rect, gesture, location in
|
||||
interaction.peerContextAction?(message, node, rect, gesture, location)
|
||||
}, toggleExpandLocalResults: {
|
||||
@ -3804,7 +3815,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}, searchPeer: { peer in
|
||||
},
|
||||
searchQuery: strongSelf.searchQueryValue,
|
||||
approvedGlobalPostQuery: strongSelf.approvedGlobalPostQueryValue,
|
||||
approvedGlobalPostQueryState: strongSelf.approvedGlobalPostQueryStateValue,
|
||||
remainingGlobalSearches: strongSelf.globalPostSearchStateValue.remainingSearches,
|
||||
globalSearchUnlockTimestamp: strongSelf.globalPostSearchStateValue.unlockTimestamp,
|
||||
searchOptions: strongSelf.searchOptionsValue, messageContextAction: { message, node, rect, gesture, paneKey, downloadResource in
|
||||
@ -5114,17 +5125,47 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: false, dismissed: nil)
|
||||
navigationController.pushViewController(controller)
|
||||
} else {
|
||||
if let searchQueryValue = self.searchQueryValue, !searchQueryValue.isEmpty, self.approvedGlobalPostQueryValue != searchQueryValue {
|
||||
self.approvedGlobalPostQuery.set(searchQueryValue)
|
||||
if let searchQueryValue = self.searchQueryValue, !searchQueryValue.isEmpty, self.approvedGlobalPostQueryStateValue?.query != searchQueryValue {
|
||||
var price: Int?
|
||||
|
||||
var globalPostSearchStateValue = self.globalPostSearchStateValue
|
||||
if globalPostSearchStateValue.remainingSearches == 0 {
|
||||
//TODO:localize
|
||||
price = 10
|
||||
}
|
||||
|
||||
self.approvedGlobalPostQueryState.set(ApprovedGlobalPostQueryState(
|
||||
query: searchQueryValue,
|
||||
price: price
|
||||
))
|
||||
|
||||
|
||||
if globalPostSearchStateValue.remainingSearches > 0 {
|
||||
globalPostSearchStateValue.remainingSearches -= 1
|
||||
}
|
||||
if globalPostSearchStateValue.remainingSearches == 0 {
|
||||
globalPostSearchStateValue.unlockTimestamp = Int32(Date().timeIntervalSince1970) + 30
|
||||
if globalPostSearchStateValue.remainingSearches == 0 {
|
||||
globalPostSearchStateValue.unlockTimestamp = Int32(Date().timeIntervalSince1970) + 30
|
||||
}
|
||||
}
|
||||
self.globalPostSearchState.set(.single(globalPostSearchStateValue))
|
||||
|
||||
if let price {
|
||||
//TODO:localize
|
||||
if let controller = self.navigationController?.topViewController as? ViewController {
|
||||
controller.present(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .starsSent(context: self.context, title: "", text: [AnimatedTextComponent.Item(
|
||||
id: AnyHashable(0),
|
||||
isUnbreakable: true,
|
||||
content: .text("\(price) Stars spent on extra search."),
|
||||
)], hasUndo: false),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
action: { action in
|
||||
return true
|
||||
}
|
||||
), in: .current)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5336,7 +5377,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
self.enqueuedTransitions.remove(at: 0)
|
||||
|
||||
var options = ListViewDeleteAndInsertOptions()
|
||||
if isFirstTime && [.chats, .topics, .channels, .apps].contains(self.key) {
|
||||
if isFirstTime && [.chats, .topics, .channels, .apps, .globalPosts].contains(self.key) {
|
||||
options.insert(.PreferSynchronousDrawing)
|
||||
options.insert(.PreferSynchronousResourceLoading)
|
||||
} else if transition.animated {
|
||||
@ -5374,7 +5415,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
emptyResultsText = "Type a keyword to search all posts\nfrom public channels."
|
||||
emptyResultsButtonSubtitleText = "Global search is a Premium feature."
|
||||
} else if let query = transition.query, !query.isEmpty {
|
||||
if transition.approvedGlobalPostQuery == query {
|
||||
if transition.approvedGlobalPostQueryState?.query == query {
|
||||
emptyResultsButtonContent = nil
|
||||
emptyResultsTitle = strongSelf.presentationData.strings.ChatList_Search_NoResults
|
||||
emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsQueryDescription(query).string
|
||||
@ -6288,6 +6329,8 @@ private final class EmptyResultsButtonPaidSearchContent: Component {
|
||||
private var timer: Foundation.Timer?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
private var cachedStarImage: UIImage?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
@ -6299,14 +6342,25 @@ private final class EmptyResultsButtonPaidSearchContent: Component {
|
||||
func update(component: EmptyResultsButtonPaidSearchContent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
let subtitleSpacing: CGFloat = 1.0
|
||||
|
||||
if self.cachedStarImage == nil || self.component?.theme !== component.theme {
|
||||
self.cachedStarImage = generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: component.theme.list.itemCheckColors.foregroundColor)
|
||||
}
|
||||
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let attributedString = NSMutableAttributedString(attributedString: NSAttributedString(string: "Search for * \(component.price)", font: Font.semibold(17.0), textColor: component.theme.list.itemCheckColors.foregroundColor))
|
||||
if let range = attributedString.string.range(of: "*"), let starImage = self.cachedStarImage {
|
||||
attributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: attributedString.string))
|
||||
attributedString.addAttribute(.foregroundColor, value: component.theme.list.itemCheckColors.foregroundColor, range: NSRange(range, in: attributedString.string))
|
||||
attributedString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedString.string))
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: "Search for \(component.price) Stars", font: Font.semibold(17.0), textColor: component.theme.list.itemCheckColors.foregroundColor))
|
||||
text: .plain(attributedString)
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: 100.0)
|
||||
@ -6349,8 +6403,9 @@ private final class EmptyResultsButtonPaidSearchContent: Component {
|
||||
contentSize.width = max(titleSize.width, subtitleSize.width)
|
||||
contentSize.height = titleSize.height + subtitleSpacing + subtitleSize.height
|
||||
}
|
||||
contentSize.width = max(contentSize.width, availableSize.width)
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((contentSize.width - titleSize.width)), y: 0.0), size: titleSize)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((contentSize.width - titleSize.width) * 0.5), y: 0.0), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
self.addSubview(titleView)
|
||||
@ -6358,7 +6413,7 @@ private final class EmptyResultsButtonPaidSearchContent: Component {
|
||||
titleView.frame = titleFrame
|
||||
}
|
||||
|
||||
let subtitleFrame = CGRect(origin: CGPoint(x: floor((contentSize.width - subtitleSize.width)), y: titleFrame.maxY + subtitleSpacing), size: subtitleSize)
|
||||
let subtitleFrame = CGRect(origin: CGPoint(x: floor((contentSize.width - subtitleSize.width) * 0.5), y: titleFrame.maxY + subtitleSpacing), size: subtitleSize)
|
||||
if let subtitleView = self.subtitle.view {
|
||||
if subtitleView.superview == nil {
|
||||
self.addSubview(subtitleView)
|
||||
@ -6512,3 +6567,30 @@ private func stringForRemainingTime(_ duration: Int32) -> String {
|
||||
}
|
||||
return durationString
|
||||
}
|
||||
|
||||
func debounceOnMainThread<T, E>(_ signal: Signal<T, E>) -> Signal<T, E> {
|
||||
return Signal { subscriber in
|
||||
let value = Atomic<T?>(value: nil)
|
||||
|
||||
let flushValue: () -> Void = {
|
||||
let v = value.swap(nil)
|
||||
if let v {
|
||||
subscriber.putNext(v)
|
||||
}
|
||||
}
|
||||
|
||||
return signal.start(next: { v in
|
||||
let previous = value.swap(v)
|
||||
if previous == nil {
|
||||
DispatchQueue.main.async {
|
||||
flushValue()
|
||||
}
|
||||
}
|
||||
}, error: { e in
|
||||
subscriber.putError(e)
|
||||
}, completed: {
|
||||
flushValue()
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -473,7 +473,7 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation
|
||||
folderId = nil
|
||||
}
|
||||
|
||||
if case let .general(scope, _, _, _) = location, case .globalPosts = scope {
|
||||
if case let .general(scope, _, _, _) = location, case let .globalPosts(allowPaidStars) = scope {
|
||||
remoteSearchResult = account.postbox.transaction { transaction -> (Int32, MessageIndex?, Api.InputPeer) in
|
||||
var lowerBound: MessageIndex?
|
||||
if let state = state, let message = state.main.messages.last {
|
||||
@ -488,7 +488,10 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation
|
||||
|> mapToSignal { (nextRate, lowerBound, inputPeer) in
|
||||
var flags: Int32 = 0
|
||||
flags |= 1 << 1
|
||||
return account.network.request(Api.functions.channels.searchPosts(flags: flags, hashtag: nil, query: query, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit, allowPaidStars: nil), automaticFloodWait: false)
|
||||
if allowPaidStars != nil {
|
||||
flags |= 1 << 2
|
||||
}
|
||||
return account.network.request(Api.functions.channels.searchPosts(flags: flags, hashtag: nil, query: query, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit, allowPaidStars: allowPaidStars.flatMap(Int64.init)), automaticFloodWait: false)
|
||||
|> map { result -> (Api.messages.Messages?, Api.messages.Messages?) in
|
||||
return (result, nil)
|
||||
}
|
||||
|
@ -18,12 +18,12 @@ public struct FoundPeer: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public enum TelegramSearchPeersScope {
|
||||
public enum TelegramSearchPeersScope: Equatable {
|
||||
case everywhere
|
||||
case channels
|
||||
case groups
|
||||
case privateChats
|
||||
case globalPosts
|
||||
case globalPosts(allowPaidStars: Int?)
|
||||
}
|
||||
|
||||
public func _internal_searchPeers(accountPeerId: PeerId, postbox: Postbox, network: Network, query: String, scope: TelegramSearchPeersScope) -> Signal<([FoundPeer], [FoundPeer]), NoError> {
|
||||
|
@ -152,8 +152,17 @@ public final class PeerInfoRatingComponent: Component {
|
||||
let baseHeight: CGFloat = 20.0
|
||||
let innerInset: CGFloat = 2.0
|
||||
|
||||
let compactLabelSize = self.compactLabel.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.compactLabel, font: Font.medium(11.0), textColor: .black))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
|
||||
let expandedSize = CGSize(width: 174.0, height: baseHeight)
|
||||
let collapsedSize = CGSize(width: baseHeight, height: baseHeight)
|
||||
let collapsedSize = CGSize(width: max(baseHeight, compactLabelSize.width + 6.0 * 2.0), height: baseHeight)
|
||||
|
||||
if self.backgroundView.image == nil {
|
||||
self.backgroundView.image = generateStretchableFilledCircleImage(diameter: baseHeight, color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
@ -189,19 +198,11 @@ public final class PeerInfoRatingComponent: Component {
|
||||
self.foregroundClippedView.backgroundColor = component.foregroundColor
|
||||
transition.setFrame(view: self.foregroundClippedShapeView, frame: foregroundFrame)
|
||||
|
||||
let compactLabelSize = self.compactLabel.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.compactLabel, font: Font.medium(11.0), textColor: .black))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
if let compactLabelView = self.compactLabel.view {
|
||||
if compactLabelView.superview == nil {
|
||||
self.foregroundMaskView.addSubview(compactLabelView)
|
||||
}
|
||||
compactLabelView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((baseHeight - innerInset * 2.0 - compactLabelSize.width) * 0.5), y: floorToScreenPixels((baseHeight - innerInset * 2.0 - compactLabelSize.height) * 0.5) + UIScreenPixel), size: compactLabelSize)
|
||||
compactLabelView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((collapsedSize.width - innerInset * 2.0 - compactLabelSize.width) * 0.5), y: floorToScreenPixels((baseHeight - innerInset * 2.0 - compactLabelSize.height) * 0.5) + UIScreenPixel), size: compactLabelSize)
|
||||
alphaTransition.setAlpha(view: compactLabelView, alpha: component.isExpanded ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
@ -284,50 +285,57 @@ public final class PeerInfoRatingComponent: Component {
|
||||
})
|
||||
}
|
||||
|
||||
let tooltipController: TooltipScreen
|
||||
if let current = self.tooltipController {
|
||||
tooltipController = current
|
||||
if !component.tooltipLabel.isEmpty {
|
||||
let tooltipController: TooltipScreen
|
||||
if let current = self.tooltipController {
|
||||
tooltipController = current
|
||||
} else {
|
||||
tooltipController = TooltipScreen(
|
||||
context: component.context,
|
||||
account: component.context.account,
|
||||
sharedContext: component.context.sharedContext,
|
||||
text: .attributedString(text: NSAttributedString(string: component.tooltipLabel, font: Font.semibold(11.0), textColor: .white)),
|
||||
style: .customBlur(component.tooltipBackgroundColor, -4.0),
|
||||
arrowStyle: .small,
|
||||
location: .point(CGRect(origin: CGPoint(x: 100.0, y: 100.0), size: CGSize()), .bottom),
|
||||
displayDuration: .infinite,
|
||||
isShimmering: true,
|
||||
cornerRadius: 10.0,
|
||||
shouldDismissOnTouch: { _, _ in
|
||||
return .ignore
|
||||
}
|
||||
)
|
||||
self.tooltipController = tooltipController
|
||||
|
||||
tooltipController.containerLayoutUpdated(ContainerViewLayout(
|
||||
size: CGSize(width: 200.0, height: 200.0),
|
||||
metrics: LayoutMetrics(),
|
||||
deviceMetrics: DeviceMetrics.iPhoneXSMax,
|
||||
intrinsicInsets: UIEdgeInsets(),
|
||||
safeInsets: UIEdgeInsets(),
|
||||
additionalInsets: UIEdgeInsets(),
|
||||
statusBarHeight: nil,
|
||||
inputHeight: nil,
|
||||
inputHeightIsInteractivellyChanging: false,
|
||||
inVoiceOver: false
|
||||
), transition: .immediate)
|
||||
|
||||
self.layer.addSublayer(tooltipController.view.layer)
|
||||
tooltipController.viewWillAppear(false)
|
||||
tooltipController.viewDidAppear(false)
|
||||
tooltipController.setIgnoreAppearanceMethodInvocations(true)
|
||||
tooltipController.view.isUserInteractionEnabled = false
|
||||
}
|
||||
|
||||
transition.setFrame(view: tooltipController.view, frame: CGRect(origin: CGPoint(), size: CGSize(width: 200.0, height: 200.0)).offsetBy(dx: -200.0 * 0.5 + foregroundFrame.width + 2.0, dy: -200.0 * 0.5))
|
||||
alphaTransition.setAlpha(view: tooltipController.view, alpha: component.isExpanded ? 1.0 : 0.0)
|
||||
} else {
|
||||
tooltipController = TooltipScreen(
|
||||
context: component.context,
|
||||
account: component.context.account,
|
||||
sharedContext: component.context.sharedContext,
|
||||
text: .attributedString(text: NSAttributedString(string: component.tooltipLabel, font: Font.semibold(11.0), textColor: .white)),
|
||||
style: .customBlur(component.tooltipBackgroundColor, -4.0),
|
||||
arrowStyle: .small,
|
||||
location: .point(CGRect(origin: CGPoint(x: 100.0, y: 100.0), size: CGSize()), .bottom),
|
||||
displayDuration: .infinite,
|
||||
isShimmering: true,
|
||||
cornerRadius: 10.0,
|
||||
shouldDismissOnTouch: { _, _ in
|
||||
return .ignore
|
||||
}
|
||||
)
|
||||
self.tooltipController = tooltipController
|
||||
|
||||
tooltipController.containerLayoutUpdated(ContainerViewLayout(
|
||||
size: CGSize(width: 200.0, height: 200.0),
|
||||
metrics: LayoutMetrics(),
|
||||
deviceMetrics: DeviceMetrics.iPhoneXSMax,
|
||||
intrinsicInsets: UIEdgeInsets(),
|
||||
safeInsets: UIEdgeInsets(),
|
||||
additionalInsets: UIEdgeInsets(),
|
||||
statusBarHeight: nil,
|
||||
inputHeight: nil,
|
||||
inputHeightIsInteractivellyChanging: false,
|
||||
inVoiceOver: false
|
||||
), transition: .immediate)
|
||||
|
||||
self.layer.addSublayer(tooltipController.view.layer)
|
||||
tooltipController.viewWillAppear(false)
|
||||
tooltipController.viewDidAppear(false)
|
||||
tooltipController.setIgnoreAppearanceMethodInvocations(true)
|
||||
tooltipController.view.isUserInteractionEnabled = false
|
||||
if let tooltipController = self.tooltipController {
|
||||
self.tooltipController = nil
|
||||
tooltipController.view.layer.removeFromSuperlayer()
|
||||
}
|
||||
}
|
||||
|
||||
transition.setFrame(view: tooltipController.view, frame: CGRect(origin: CGPoint(), size: CGSize(width: 200.0, height: 200.0)).offsetBy(dx: -200.0 * 0.5 + foregroundFrame.width + 2.0, dy: -200.0 * 0.5))
|
||||
alphaTransition.setAlpha(view: tooltipController.view, alpha: component.isExpanded ? 1.0 : 0.0)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
@ -1917,7 +1917,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let apparentBackgroundHeight = (1.0 - transitionFraction) * backgroundHeight + transitionFraction * transitionSourceHeight
|
||||
|
||||
var subtitleRatingSize: CGSize?
|
||||
if !"".isEmpty, let cachedData = cachedData as? CachedUserData, let starRating = cachedData.starRating {
|
||||
if let cachedData = cachedData as? CachedUserData, let starRating = cachedData.starRating {
|
||||
let subtitleRating: ComponentView<Empty>
|
||||
var subtitleRatingTransition = ComponentTransition(transition)
|
||||
if let current = self.subtitleRating {
|
||||
@ -1978,7 +1978,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
|
||||
if self.subtitleRatingIsExpanded, let controller = self.controller, let presentationData = self.presentationData, !self.didDisplayRatingTooltip {
|
||||
self.didDisplayRatingTooltip = true
|
||||
controller.presentInGlobalOverlay(UndoOverlayController(
|
||||
controller.present(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(
|
||||
title: nil,
|
||||
@ -1987,10 +1987,21 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
customUndoText: "Learn More"
|
||||
),
|
||||
position: .top,
|
||||
action: { _ in
|
||||
action: { [weak self] action in
|
||||
guard let self else {
|
||||
return true
|
||||
}
|
||||
|
||||
if case .undo = action {
|
||||
var infoUrl = "https://telegram.org/blog/telegram-stars"
|
||||
if let data = self.context.currentAppConfiguration.with({ $0 }).data, let value = data["stars_rating_learnmore_url"] as? String {
|
||||
infoUrl = value
|
||||
}
|
||||
self.context.sharedContext.applicationBindings.openUrl(infoUrl)
|
||||
}
|
||||
return true
|
||||
}
|
||||
))
|
||||
), in: .current)
|
||||
}
|
||||
}
|
||||
)),
|
||||
|
@ -115,7 +115,7 @@ extension ChatControllerImpl {
|
||||
let textItems: [AnimatedTextComponent.Item] = [
|
||||
AnimatedTextComponent.Item(id: 0, content: .text(text))
|
||||
]
|
||||
let controller = UndoOverlayController(presentationData: self.presentationData, content: .starsSent(context: self.context, title: title, text: textItems), elevatedLayout: false, position: .top, action: { [weak self] action in
|
||||
let controller = UndoOverlayController(presentationData: self.presentationData, content: .starsSent(context: self.context, title: title, text: textItems, hasUndo: true), elevatedLayout: false, position: .top, action: { [weak self] action in
|
||||
guard let self else {
|
||||
return false
|
||||
}
|
||||
|
@ -535,9 +535,9 @@ extension ChatControllerImpl {
|
||||
|
||||
self.currentSendStarsUndoMessageId = messageId
|
||||
if let current = self.currentSendStarsUndoController {
|
||||
current.content = .starsSent(context: self.context, title: title, text: textItems)
|
||||
current.content = .starsSent(context: self.context, title: title, text: textItems, hasUndo: true)
|
||||
} else {
|
||||
let controller = UndoOverlayController(presentationData: self.presentationData, content: .starsSent(context: self.context, title: title, text: textItems), elevatedLayout: false, position: .top, action: { [weak self] action in
|
||||
let controller = UndoOverlayController(presentationData: self.presentationData, content: .starsSent(context: self.context, title: title, text: textItems, hasUndo: true), elevatedLayout: false, position: .top, action: { [weak self] action in
|
||||
guard let self else {
|
||||
return false
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ public enum UndoOverlayContent {
|
||||
case copy(text: String)
|
||||
case mediaSaved(text: String)
|
||||
case paymentSent(currencyValue: String, itemTitle: String)
|
||||
case starsSent(context: AccountContext, title: String, text: [AnimatedTextComponent.Item])
|
||||
case starsSent(context: AccountContext, title: String, text: [AnimatedTextComponent.Item], hasUndo: Bool)
|
||||
case inviteRequestSent(title: String, text: String)
|
||||
case image(image: UIImage, title: String?, text: String, round: Bool, undoText: String?)
|
||||
case notificationSoundAdded(title: String, text: String, action: (() -> Void)?)
|
||||
|
@ -452,7 +452,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
self.textNode.attributedText = string
|
||||
displayUndo = false
|
||||
self.originalRemainingSeconds = 5
|
||||
case let .starsSent(_, title, textItems):
|
||||
case let .starsSent(_, title, textItems, hasUndo):
|
||||
self.avatarNode = nil
|
||||
self.iconNode = nil
|
||||
self.iconCheckNode = nil
|
||||
@ -478,11 +478,13 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
self.animatedTextItems = textItems
|
||||
|
||||
displayUndo = true
|
||||
self.originalRemainingSeconds = 4.9
|
||||
displayUndo = hasUndo
|
||||
self.originalRemainingSeconds = hasUndo ? 4.9 : 3.0
|
||||
isUserInteractionEnabled = true
|
||||
|
||||
self.statusNode = RadialStatusNode(backgroundNodeColor: .clear)
|
||||
if hasUndo {
|
||||
self.statusNode = RadialStatusNode(backgroundNodeColor: .clear)
|
||||
}
|
||||
case let .messagesUnpinned(title, text, undo, isHidden):
|
||||
self.avatarNode = nil
|
||||
self.iconNode = nil
|
||||
@ -1562,8 +1564,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
switch content {
|
||||
case .removedChat:
|
||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||
case .starsSent:
|
||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||
case let .starsSent(_, _, _, hasUndo):
|
||||
if hasUndo {
|
||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||
}
|
||||
|
||||
if self.textNode.tapAttributeAction != nil || displayUndo {
|
||||
self.isUserInteractionEnabled = true
|
||||
@ -1835,7 +1839,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
|
||||
}
|
||||
self.textNode.attributedText = attributedText
|
||||
case let .starsSent(_, title, textItems):
|
||||
case let .starsSent(_, title, textItems, _):
|
||||
self.animatedTextItems = textItems
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
|
||||
|
Loading…
x
Reference in New Issue
Block a user