mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 17:30:12 +00:00
Message search improvements
This commit is contained in:
parent
3d4be40ddb
commit
91b9c28670
@ -4752,3 +4752,5 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
|
|
||||||
"Channel.EditAdmin.PermissionDeleteMessagesOfOthers" = "Delete Messages of Others";
|
"Channel.EditAdmin.PermissionDeleteMessagesOfOthers" = "Delete Messages of Others";
|
||||||
"Channel.AdminLog.CanDeleteMessagesOfOthers" = "Delete Messages of Others";
|
"Channel.AdminLog.CanDeleteMessagesOfOthers" = "Delete Messages of Others";
|
||||||
|
|
||||||
|
"ChatSearch.ResultsTooltip" = "Tap to view as a list.";
|
||||||
|
|||||||
@ -1021,7 +1021,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
@objc private func composePressed() {
|
@objc private func composePressed() {
|
||||||
let controller = self.context.sharedContext.makeComposeController(context: self.context)
|
let controller = self.context.sharedContext.makeComposeController(context: self.context)
|
||||||
self.present(controller, in: .window(.root))
|
self.present(controller, in: .window(.root))
|
||||||
//(self.navigationController as? NavigationController)?.replaceAllButRootController(self.context.sharedContext.makeComposeController(context: self.context), animated: true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
|
public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
|
||||||
@ -1087,7 +1086,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
var sourceRect = selectedNode.view.superview!.convert(selectedNode.frame, to: sourceView)
|
var sourceRect = selectedNode.view.superview!.convert(selectedNode.frame, to: sourceView)
|
||||||
sourceRect.size.height -= UIScreenPixel
|
sourceRect.size.height -= UIScreenPixel
|
||||||
switch item.content {
|
switch item.content {
|
||||||
case let .peer(_, peer, _, _, _, _, _, _, _, _):
|
case let .peer(_, peer, _, _, _, _, _, _, _, _, _):
|
||||||
if peer.peerId.namespace != Namespaces.Peer.SecretChat {
|
if peer.peerId.namespace != Namespaces.Peer.SecretChat {
|
||||||
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
|
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
|
||||||
chatController.canReadHistory.set(false)
|
chatController.canReadHistory.set(false)
|
||||||
|
|||||||
@ -437,7 +437,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
case let .message(message, peer, readState, presentationData):
|
case let .message(message, peer, readState, presentationData):
|
||||||
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(message: message, peer: peer, combinedReadState: readState, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: true), editing: false, hasActiveRevealControls: false, selected: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
|
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(message: message, peer: peer, combinedReadState: readState, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: true, displayAsMessage: false), editing: false, hasActiveRevealControls: false, selected: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
|
||||||
case let .addContact(phoneNumber, theme, strings):
|
case let .addContact(phoneNumber, theme, strings):
|
||||||
return ContactsAddItem(theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: {
|
return ContactsAddItem(theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: {
|
||||||
interaction.addContact(phoneNumber)
|
interaction.addContact(phoneNumber)
|
||||||
@ -544,6 +544,7 @@ public enum ChatListSearchContextActionSource {
|
|||||||
|
|
||||||
public final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
public final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
|
private var interaction: ChatListNodeInteraction?
|
||||||
|
|
||||||
private let recentListNode: ListView
|
private let recentListNode: ListView
|
||||||
private let listNode: ListView
|
private let listNode: ListView
|
||||||
@ -886,6 +887,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
gesture?.cancel()
|
gesture?.cancel()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
self.interaction = interaction
|
||||||
|
|
||||||
let previousRecentItems = Atomic<[ChatListRecentEntry]?>(value: nil)
|
let previousRecentItems = Atomic<[ChatListRecentEntry]?>(value: nil)
|
||||||
let hasRecentPeers = recentPeers(account: context.account)
|
let hasRecentPeers = recentPeers(account: context.account)
|
||||||
@ -1020,6 +1022,13 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.updatedRecentPeersDisposable.dispose()
|
||||||
|
self.recentDisposable.dispose()
|
||||||
|
self.searchDisposable.dispose()
|
||||||
|
self.presentationDataDisposable?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
override public func didLoad() {
|
override public func didLoad() {
|
||||||
super.didLoad()
|
super.didLoad()
|
||||||
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
||||||
@ -1031,12 +1040,6 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
|
||||||
self.updatedRecentPeersDisposable.dispose()
|
|
||||||
self.recentDisposable.dispose()
|
|
||||||
self.searchDisposable.dispose()
|
|
||||||
self.presentationDataDisposable?.dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updateTheme(theme: PresentationTheme) {
|
private func updateTheme(theme: PresentationTheme) {
|
||||||
self.backgroundColor = self.filter.contains(.excludeRecent) ? nil : theme.chatList.backgroundColor
|
self.backgroundColor = self.filter.contains(.excludeRecent) ? nil : theme.chatList.backgroundColor
|
||||||
@ -1054,11 +1057,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func searchTextUpdated(text: String) {
|
override public func searchTextUpdated(text: String) {
|
||||||
if text.isEmpty {
|
let searchQuery: String? = !text.isEmpty ? text : nil
|
||||||
self.searchQuery.set(.single(nil))
|
self.interaction?.searchTextHighightState = searchQuery
|
||||||
} else {
|
self.searchQuery.set(.single(searchQuery))
|
||||||
self.searchQuery.set(.single(text))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func enqueueRecentTransition(_ transition: ChatListSearchContainerRecentTransition, firstTime: Bool) {
|
private func enqueueRecentTransition(_ transition: ChatListSearchContainerRecentTransition, firstTime: Bool) {
|
||||||
@ -1124,7 +1125,6 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
let hadValidLayout = self.validLayout != nil
|
let hadValidLayout = self.validLayout != nil
|
||||||
self.validLayout = layout
|
self.validLayout = layout
|
||||||
|
|
||||||
|
|
||||||
let topInset = navigationBarHeight
|
let topInset = navigationBarHeight
|
||||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: layout.size.width, height: layout.size.height - topInset)))
|
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: layout.size.width, height: layout.size.height - topInset)))
|
||||||
|
|
||||||
@ -1202,7 +1202,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
bounds = selectedItemNode.bounds
|
bounds = selectedItemNode.bounds
|
||||||
}
|
}
|
||||||
switch item.content {
|
switch item.content {
|
||||||
case let .peer(message, peer, _, _, _, _, _, _, _, _):
|
case let .peer(message, peer, _, _, _, _, _, _, _, _, _):
|
||||||
return (selectedItemNode.view, bounds, message?.id ?? peer.peerId)
|
return (selectedItemNode.view, bounds, message?.id ?? peer.peerId)
|
||||||
case let .groupReference(groupId, _, _, _, _):
|
case let .groupReference(groupId, _, _, _, _):
|
||||||
return (selectedItemNode.view, bounds, groupId)
|
return (selectedItemNode.view, bounds, groupId)
|
||||||
|
|||||||
@ -18,12 +18,12 @@ import ChatListSearchItemNode
|
|||||||
import ContextUI
|
import ContextUI
|
||||||
|
|
||||||
public enum ChatListItemContent {
|
public enum ChatListItemContent {
|
||||||
case peer(message: Message?, peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool, ignoreUnreadBadge: Bool)
|
case peer(message: Message?, peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool, ignoreUnreadBadge: Bool, displayAsMessage: Bool)
|
||||||
case groupReference(groupId: PeerGroupId, peers: [ChatListGroupReferencePeer], message: Message?, unreadState: PeerGroupUnreadCountersCombinedSummary, hiddenByDefault: Bool)
|
case groupReference(groupId: PeerGroupId, peers: [ChatListGroupReferencePeer], message: Message?, unreadState: PeerGroupUnreadCountersCombinedSummary, hiddenByDefault: Bool)
|
||||||
|
|
||||||
public var chatLocation: ChatLocation? {
|
public var chatLocation: ChatLocation? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .peer(_, peer, _, _, _, _, _, _, _, _):
|
case let .peer(_, peer, _, _, _, _, _, _, _, _, _):
|
||||||
return .peer(peer.peerId)
|
return .peer(peer.peerId)
|
||||||
case .groupReference:
|
case .groupReference:
|
||||||
return nil
|
return nil
|
||||||
@ -120,7 +120,7 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
|
|||||||
|
|
||||||
public func selected(listView: ListView) {
|
public func selected(listView: ListView) {
|
||||||
switch self.content {
|
switch self.content {
|
||||||
case let .peer(message, peer, _, _, _, _, _, _, isAd, _):
|
case let .peer(message, peer, _, _, _, _, _, _, isAd, _, _):
|
||||||
if let message = message, let peer = peer.peer {
|
if let message = message, let peer = peer.peer {
|
||||||
self.interaction.messageSelected(peer, message, isAd)
|
self.interaction.messageSelected(peer, message, isAd)
|
||||||
} else if let peer = peer.peer {
|
} else if let peer = peer.peer {
|
||||||
@ -289,6 +289,28 @@ private let separatorHeight = 1.0 / UIScreen.main.scale
|
|||||||
|
|
||||||
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 26.0)!
|
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 26.0)!
|
||||||
|
|
||||||
|
private final class CachedChatListSearchResult {
|
||||||
|
let text: String
|
||||||
|
let searchQuery: String
|
||||||
|
let resultRanges: [Range<String.Index>]
|
||||||
|
|
||||||
|
init(text: String, searchQuery: String, resultRanges: [Range<String.Index>]) {
|
||||||
|
self.text = text
|
||||||
|
self.searchQuery = searchQuery
|
||||||
|
self.resultRanges = resultRanges
|
||||||
|
}
|
||||||
|
|
||||||
|
func matches(text: String, searchQuery: String) -> Bool {
|
||||||
|
if self.text != text {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if self.searchQuery != searchQuery {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ChatListItemNode: ItemListRevealOptionsItemNode {
|
class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||||
var item: ChatListItem?
|
var item: ChatListItem?
|
||||||
|
|
||||||
@ -319,6 +341,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
private var peerPresenceManager: PeerPresenceStatusManager?
|
private var peerPresenceManager: PeerPresenceStatusManager?
|
||||||
|
|
||||||
|
private var cachedChatListSearchResult: CachedChatListSearchResult?
|
||||||
|
|
||||||
var layoutParams: (ChatListItem, first: Bool, last: Bool, firstWithHeader: Bool, nextIsPinned: Bool, ListViewItemLayoutParams, countersSize: CGFloat)?
|
var layoutParams: (ChatListItem, first: Bool, last: Bool, firstWithHeader: Bool, nextIsPinned: Bool, ListViewItemLayoutParams, countersSize: CGFloat)?
|
||||||
private var contentImageMedia: Media?
|
private var contentImageMedia: Media?
|
||||||
|
|
||||||
@ -500,8 +524,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
var peer: Peer?
|
var peer: Peer?
|
||||||
switch item.content {
|
switch item.content {
|
||||||
case let .peer(_, peerValue, _, _, _, _, _, _, _, _):
|
case let .peer(message, peerValue, _, _, _, _, _, _, _, _, displayAsMessage):
|
||||||
|
if displayAsMessage, let author = message?.author as? TelegramUser {
|
||||||
|
peer = author
|
||||||
|
} else {
|
||||||
peer = peerValue.chatMainPeer
|
peer = peerValue.chatMainPeer
|
||||||
|
}
|
||||||
case let .groupReference(groupReference):
|
case let .groupReference(groupReference):
|
||||||
if let previousItem = previousItem, case let .groupReference(previousGroupReference) = previousItem.content, groupReference.hiddenByDefault != previousGroupReference.hiddenByDefault {
|
if let previousItem = previousItem, case let .groupReference(previousGroupReference) = previousItem.content, groupReference.hiddenByDefault != previousGroupReference.hiddenByDefault {
|
||||||
UIView.transition(with: self.avatarNode.view, duration: 0.3, options: [.transitionCrossDissolve], animations: {
|
UIView.transition(with: self.avatarNode.view, duration: 0.3, options: [.transitionCrossDissolve], animations: {
|
||||||
@ -614,6 +642,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
let currentItem = self.layoutParams?.0
|
let currentItem = self.layoutParams?.0
|
||||||
let currentContentImageMedia = self.contentImageMedia
|
let currentContentImageMedia = self.contentImageMedia
|
||||||
|
let currentChatListSearchResult = self.cachedChatListSearchResult
|
||||||
|
|
||||||
return { item, params, first, last, firstWithHeader, nextIsPinned in
|
return { item, params, first, last, firstWithHeader, nextIsPinned in
|
||||||
let account = item.context.account
|
let account = item.context.account
|
||||||
@ -632,11 +661,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
let inputActivities: [(Peer, PeerInputActivity)]?
|
let inputActivities: [(Peer, PeerInputActivity)]?
|
||||||
let isPeerGroup: Bool
|
let isPeerGroup: Bool
|
||||||
let isAd: Bool
|
let isAd: Bool
|
||||||
|
let displayAsMessage: Bool
|
||||||
|
|
||||||
var groupHiddenByDefault = false
|
var groupHiddenByDefault = false
|
||||||
|
|
||||||
switch item.content {
|
switch item.content {
|
||||||
case let .peer(messageValue, peerValue, combinedReadStateValue, notificationSettingsValue, peerPresenceValue, summaryInfoValue, embeddedStateValue, inputActivitiesValue, isAdValue, ignoreUnreadBadge):
|
case let .peer(messageValue, peerValue, combinedReadStateValue, notificationSettingsValue, peerPresenceValue, summaryInfoValue, embeddedStateValue, inputActivitiesValue, isAdValue, ignoreUnreadBadge, displayAsMessageValue):
|
||||||
message = messageValue
|
message = messageValue
|
||||||
contentPeer = .chat(peerValue)
|
contentPeer = .chat(peerValue)
|
||||||
combinedReadState = combinedReadStateValue
|
combinedReadState = combinedReadStateValue
|
||||||
@ -656,6 +686,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
inputActivities = inputActivitiesValue
|
inputActivities = inputActivitiesValue
|
||||||
isPeerGroup = false
|
isPeerGroup = false
|
||||||
isAd = isAdValue
|
isAd = isAdValue
|
||||||
|
displayAsMessage = displayAsMessageValue
|
||||||
case let .groupReference(_, peers, messageValue, unreadState, hiddenByDefault):
|
case let .groupReference(_, peers, messageValue, unreadState, hiddenByDefault):
|
||||||
if let _ = messageValue, !peers.isEmpty {
|
if let _ = messageValue, !peers.isEmpty {
|
||||||
contentPeer = .chat(peers[0].peer)
|
contentPeer = .chat(peers[0].peer)
|
||||||
@ -674,6 +705,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
unreadCount = (allCount, allCount != 0, true, nil)
|
unreadCount = (allCount, allCount != 0, true, nil)
|
||||||
peerPresence = nil
|
peerPresence = nil
|
||||||
isAd = false
|
isAd = false
|
||||||
|
displayAsMessage = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if let messageValue = message {
|
if let messageValue = message {
|
||||||
@ -766,6 +798,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var contentImageMedia: Media?
|
var contentImageMedia: Media?
|
||||||
|
var chatListSearchResult: CachedChatListSearchResult?
|
||||||
|
|
||||||
switch contentData {
|
switch contentData {
|
||||||
case let .chat(itemPeer, _, _, messageText):
|
case let .chat(itemPeer, _, _, messageText):
|
||||||
@ -777,15 +810,63 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
attributedText = NSAttributedString(string: embeddedState.text.string.replacingOccurrences(of: "\n\n", with: " "), font: textFont, textColor: theme.messageTextColor)
|
attributedText = NSAttributedString(string: embeddedState.text.string.replacingOccurrences(of: "\n\n", with: " "), font: textFont, textColor: theme.messageTextColor)
|
||||||
} else if let message = message {
|
} else if let message = message {
|
||||||
|
let composedString: NSMutableAttributedString
|
||||||
if let inlineAuthorPrefix = inlineAuthorPrefix {
|
if let inlineAuthorPrefix = inlineAuthorPrefix {
|
||||||
let composedString = NSMutableAttributedString()
|
composedString = NSMutableAttributedString()
|
||||||
composedString.append(NSAttributedString(string: "\(inlineAuthorPrefix): ", font: textFont, textColor: theme.titleColor))
|
composedString.append(NSAttributedString(string: "\(inlineAuthorPrefix): ", font: textFont, textColor: theme.titleColor))
|
||||||
composedString.append(NSAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor))
|
composedString.append(NSAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor))
|
||||||
attributedText = composedString
|
|
||||||
} else {
|
} else {
|
||||||
attributedText = NSAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor)
|
composedString = NSMutableAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let searchQuery = item.interaction.searchTextHighightState {
|
||||||
|
if let cached = currentChatListSearchResult, cached.matches(text: composedString.string, searchQuery: searchQuery) {
|
||||||
|
chatListSearchResult = cached
|
||||||
|
} else {
|
||||||
|
var ranges: [Range<String.Index>] = []
|
||||||
|
let queryWords = searchQuery.split { !$0.isLetter }.filter { !$0.isEmpty }.map { $0.lowercased() }
|
||||||
|
|
||||||
|
let searchRange = composedString.string.startIndex ..< composedString.string.endIndex
|
||||||
|
composedString.string.enumerateSubstrings(in: searchRange, options: .byWords) { (substring, range, _, _) in
|
||||||
|
guard let substring = substring?.lowercased() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for word in queryWords {
|
||||||
|
var count = 0
|
||||||
|
inner: for (c1, c2) in zip(word, substring) {
|
||||||
|
if c1 != c2 {
|
||||||
|
break inner
|
||||||
|
}
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
let length = Double(max(word.count, substring.count))
|
||||||
|
if length > 0 {
|
||||||
|
let difference = abs(length - Double(count))
|
||||||
|
let rating = difference / length
|
||||||
|
if rating < 0.33 {
|
||||||
|
ranges.append(range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chatListSearchResult = CachedChatListSearchResult(text: composedString.string, searchQuery: searchQuery, resultRanges: ranges)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chatListSearchResult = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let chatListSearchResult = chatListSearchResult {
|
||||||
|
for range in chatListSearchResult.resultRanges {
|
||||||
|
composedString.addAttribute(.foregroundColor, value: theme.messageHighlightedTextColor, range: NSRange(range, in: composedString.string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributedText = composedString
|
||||||
|
|
||||||
var peerText: String?
|
var peerText: String?
|
||||||
if case .groupReference = item.content {
|
if case .groupReference = item.content {
|
||||||
if let messagePeer = itemPeer.chatMainPeer {
|
if let messagePeer = itemPeer.chatMainPeer {
|
||||||
@ -793,7 +874,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
} else if let author = message.author as? TelegramUser, let peer = itemPeer.chatMainPeer, !(peer is TelegramUser) {
|
} else if let author = message.author as? TelegramUser, let peer = itemPeer.chatMainPeer, !(peer is TelegramUser) {
|
||||||
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||||
} else {
|
} else if !displayAsMessage {
|
||||||
peerText = author.id == account.peerId ? item.presentationData.strings.DialogList_You : author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
peerText = author.id == account.peerId ? item.presentationData.strings.DialogList_You : author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -864,7 +945,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
switch contentData {
|
switch contentData {
|
||||||
case let .chat(itemPeer, _, _, _):
|
case let .chat(itemPeer, _, _, _):
|
||||||
if isPeerGroup {
|
if let message = message, let author = message.author as? TelegramUser, displayAsMessage {
|
||||||
|
titleAttributedString = NSAttributedString(string: author.id == account.peerId ? item.presentationData.strings.DialogList_You : author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder), font: titleFont, textColor: theme.titleColor)
|
||||||
|
} else if isPeerGroup {
|
||||||
titleAttributedString = NSAttributedString(string: item.presentationData.strings.ChatList_ArchivedChatsTitle, font: titleFont, textColor: theme.titleColor)
|
titleAttributedString = NSAttributedString(string: item.presentationData.strings.ChatList_ArchivedChatsTitle, font: titleFont, textColor: theme.titleColor)
|
||||||
} else if itemPeer.chatMainPeer?.id == item.context.account.peerId {
|
} else if itemPeer.chatMainPeer?.id == item.context.account.peerId {
|
||||||
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: titleFont, textColor: theme.titleColor)
|
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: titleFont, textColor: theme.titleColor)
|
||||||
@ -1057,8 +1140,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
let peerRevealOptions: [ItemListRevealOption]
|
let peerRevealOptions: [ItemListRevealOption]
|
||||||
let peerLeftRevealOptions: [ItemListRevealOption]
|
let peerLeftRevealOptions: [ItemListRevealOption]
|
||||||
switch item.content {
|
switch item.content {
|
||||||
case let .peer(_, renderedPeer, _, _, presence, _ ,_ ,_, _, _):
|
case let .peer(_, renderedPeer, _, _, presence, _ ,_ ,_, _, _, displayAsMessage):
|
||||||
if let peer = renderedPeer.peer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId {
|
if !displayAsMessage, let peer = renderedPeer.peer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId {
|
||||||
let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: timestamp)
|
let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: timestamp)
|
||||||
if case .online = relativeStatus {
|
if case .online = relativeStatus {
|
||||||
online = true
|
online = true
|
||||||
@ -1124,6 +1207,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.layoutParams = (item, first, last, firstWithHeader, nextIsPinned, params, countersSize)
|
strongSelf.layoutParams = (item, first, last, firstWithHeader, nextIsPinned, params, countersSize)
|
||||||
strongSelf.contentImageMedia = contentImageMedia
|
strongSelf.contentImageMedia = contentImageMedia
|
||||||
|
strongSelf.cachedChatListSearchResult = chatListSearchResult
|
||||||
|
|
||||||
strongSelf.contextContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
strongSelf.contextContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||||
|
|
||||||
|
|||||||
@ -57,6 +57,7 @@ public final class ChatListNodeInteraction {
|
|||||||
let toggleArchivedFolderHiddenByDefault: () -> Void
|
let toggleArchivedFolderHiddenByDefault: () -> Void
|
||||||
let activateChatPreview: (ChatListItem, ASDisplayNode, ContextGesture?) -> Void
|
let activateChatPreview: (ChatListItem, ASDisplayNode, ContextGesture?) -> Void
|
||||||
|
|
||||||
|
var searchTextHighightState: String?
|
||||||
var highlightedChatLocation: ChatListHighlightedLocation?
|
var highlightedChatLocation: ChatListHighlightedLocation?
|
||||||
|
|
||||||
public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, messageSelected: @escaping (Peer, Message, Bool) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void) {
|
public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, messageSelected: @escaping (Peer, Message, Bool) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void) {
|
||||||
@ -141,7 +142,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
|||||||
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, isAd):
|
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, isAd):
|
||||||
switch mode {
|
switch mode {
|
||||||
case .chatList:
|
case .chatList:
|
||||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, hiddenOffset: false, interaction: nodeInteraction), directionHint: entry.directionHint)
|
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false, displayAsMessage: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, hiddenOffset: false, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||||
case let .peers(filter):
|
case let .peers(filter):
|
||||||
let itemPeer = peer.chatMainPeer
|
let itemPeer = peer.chatMainPeer
|
||||||
var chatPeer: Peer?
|
var chatPeer: Peer?
|
||||||
@ -222,7 +223,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
|||||||
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, isAd):
|
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, isAd):
|
||||||
switch mode {
|
switch mode {
|
||||||
case .chatList:
|
case .chatList:
|
||||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, hiddenOffset: false, interaction: nodeInteraction), directionHint: entry.directionHint)
|
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false, displayAsMessage: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, hiddenOffset: false, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||||
case let .peers(filter):
|
case let .peers(filter):
|
||||||
let itemPeer = peer.chatMainPeer
|
let itemPeer = peer.chatMainPeer
|
||||||
var chatPeer: Peer?
|
var chatPeer: Peer?
|
||||||
|
|||||||
@ -7,16 +7,20 @@ public final class TabBarControllerTheme {
|
|||||||
public let backgroundColor: UIColor
|
public let backgroundColor: UIColor
|
||||||
public let tabBarBackgroundColor: UIColor
|
public let tabBarBackgroundColor: UIColor
|
||||||
public let tabBarSeparatorColor: UIColor
|
public let tabBarSeparatorColor: UIColor
|
||||||
|
public let tabBarIconColor: UIColor
|
||||||
|
public let tabBarSelectedIconColor: UIColor
|
||||||
public let tabBarTextColor: UIColor
|
public let tabBarTextColor: UIColor
|
||||||
public let tabBarSelectedTextColor: UIColor
|
public let tabBarSelectedTextColor: UIColor
|
||||||
public let tabBarBadgeBackgroundColor: UIColor
|
public let tabBarBadgeBackgroundColor: UIColor
|
||||||
public let tabBarBadgeStrokeColor: UIColor
|
public let tabBarBadgeStrokeColor: UIColor
|
||||||
public let tabBarBadgeTextColor: UIColor
|
public let tabBarBadgeTextColor: UIColor
|
||||||
|
|
||||||
public init(backgroundColor: UIColor, tabBarBackgroundColor: UIColor, tabBarSeparatorColor: UIColor, tabBarTextColor: UIColor, tabBarSelectedTextColor: UIColor, tabBarBadgeBackgroundColor: UIColor, tabBarBadgeStrokeColor: UIColor, tabBarBadgeTextColor: UIColor) {
|
public init(backgroundColor: UIColor, tabBarBackgroundColor: UIColor, tabBarSeparatorColor: UIColor, tabBarIconColor: UIColor, tabBarSelectedIconColor: UIColor, tabBarTextColor: UIColor, tabBarSelectedTextColor: UIColor, tabBarBadgeBackgroundColor: UIColor, tabBarBadgeStrokeColor: UIColor, tabBarBadgeTextColor: UIColor) {
|
||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
self.tabBarBackgroundColor = tabBarBackgroundColor
|
self.tabBarBackgroundColor = tabBarBackgroundColor
|
||||||
self.tabBarSeparatorColor = tabBarSeparatorColor
|
self.tabBarSeparatorColor = tabBarSeparatorColor
|
||||||
|
self.tabBarIconColor = tabBarIconColor
|
||||||
|
self.tabBarSelectedIconColor = tabBarSelectedIconColor
|
||||||
self.tabBarTextColor = tabBarTextColor
|
self.tabBarTextColor = tabBarTextColor
|
||||||
self.tabBarSelectedTextColor = tabBarSelectedTextColor
|
self.tabBarSelectedTextColor = tabBarSelectedTextColor
|
||||||
self.tabBarBadgeBackgroundColor = tabBarBadgeBackgroundColor
|
self.tabBarBadgeBackgroundColor = tabBarBadgeBackgroundColor
|
||||||
|
|||||||
@ -452,20 +452,42 @@ public final class TextNodeLayout: NSObject {
|
|||||||
guard let attributedString = self.attributedString else {
|
guard let attributedString = self.attributedString else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
var ranges: [Range<String.Index>] = []
|
var ranges: [Range<String.Index>] = []
|
||||||
var searchRange = attributedString.string.startIndex ..< attributedString.string.endIndex
|
let queryWords = text.split { !$0.isLetter }.filter { !$0.isEmpty }.map { $0.lowercased() }
|
||||||
while searchRange.lowerBound != attributedString.string.endIndex {
|
|
||||||
if let range = attributedString.string.range(of: text, options: [.caseInsensitive, .diacriticInsensitive], range: searchRange, locale: nil) {
|
let text = attributedString.string.lowercased()
|
||||||
|
let searchRange = text.startIndex ..< text.endIndex
|
||||||
|
text.enumerateSubstrings(in: searchRange, options: .byWords) { (substring, range, _, _) in
|
||||||
|
guard let substring = substring else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for word in queryWords {
|
||||||
|
var count = 0
|
||||||
|
inner: for (c1, c2) in zip(word, substring) {
|
||||||
|
if c1 != c2 {
|
||||||
|
break inner
|
||||||
|
}
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
let length = Double(max(word.count, substring.count))
|
||||||
|
if length > 0 {
|
||||||
|
let difference = abs(length - Double(count))
|
||||||
|
let rating = difference / length
|
||||||
|
if rating < 0.33 {
|
||||||
ranges.append(range)
|
ranges.append(range)
|
||||||
searchRange = range.upperBound ..< attributedString.string.endIndex
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var result: [[CGRect]] = []
|
var result: [[CGRect]] = []
|
||||||
for stringRange in ranges {
|
for stringRange in ranges {
|
||||||
var rects: [CGRect] = []
|
var rects: [CGRect] = []
|
||||||
let range = NSRange(stringRange, in: attributedString.string)
|
let range = NSRange(stringRange, in: text)
|
||||||
for line in self.lines {
|
for line in self.lines {
|
||||||
let lineRange = NSIntersectionRange(range, line.range)
|
let lineRange = NSIntersectionRange(range, line.range)
|
||||||
if lineRange.length != 0 {
|
if lineRange.length != 0 {
|
||||||
|
|||||||
@ -516,8 +516,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
|
|
||||||
if isAnimated || disablePlayerControls {
|
if isAnimated || disablePlayerControls {
|
||||||
strongSelf.footerContentNode.content = .info
|
strongSelf.footerContentNode.content = .info
|
||||||
}
|
} else if isPaused {
|
||||||
else if isPaused {
|
|
||||||
if hasStarted || strongSelf.didPause {
|
if hasStarted || strongSelf.didPause {
|
||||||
strongSelf.footerContentNode.content = .playback(paused: true, seekable: seekable)
|
strongSelf.footerContentNode.content = .playback(paused: true, seekable: seekable)
|
||||||
} else if let fetchStatus = fetchStatus, !strongSelf.requiresDownload {
|
} else if let fetchStatus = fetchStatus, !strongSelf.requiresDownload {
|
||||||
|
|||||||
@ -2272,8 +2272,7 @@ final class SecureIdDocumentFormControllerNode: FormControllerNode<SecureIdDocum
|
|||||||
} else if useNext {
|
} else if useNext {
|
||||||
if case .deleteDocument = itemEntry {
|
if case .deleteDocument = itemEntry {
|
||||||
return false
|
return false
|
||||||
}
|
} else if let inputNode = itemNode as? FormControllerTextInputItemNode {
|
||||||
else if let inputNode = itemNode as? FormControllerTextInputItemNode {
|
|
||||||
inputNode.activate()
|
inputNode.activate()
|
||||||
return false
|
return false
|
||||||
} else if let actionNode = itemNode as? FormControllerDetailActionItemNode {
|
} else if let actionNode = itemNode as? FormControllerDetailActionItemNode {
|
||||||
|
|||||||
@ -277,10 +277,6 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
|
|||||||
width = max(minimumWidth, min(maximumWidth, calculatedWidth))
|
width = max(minimumWidth, min(maximumWidth, calculatedWidth))
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = CGSize(width: width, height: 32.0)
|
|
||||||
if !self.itemNodes.isEmpty {
|
|
||||||
let itemSize = CGSize(width: floorToScreenPixels(size.width / CGFloat(self.itemNodes.count)), height: size.height)
|
|
||||||
|
|
||||||
let selectedIndex: Int
|
let selectedIndex: Int
|
||||||
if let gestureSelectedIndex = self.gestureSelectedIndex {
|
if let gestureSelectedIndex = self.gestureSelectedIndex {
|
||||||
selectedIndex = gestureSelectedIndex
|
selectedIndex = gestureSelectedIndex
|
||||||
@ -288,6 +284,10 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
|
|||||||
selectedIndex = self.selectedIndex
|
selectedIndex = self.selectedIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let size = CGSize(width: width, height: 32.0)
|
||||||
|
if !self.itemNodes.isEmpty {
|
||||||
|
let itemSize = CGSize(width: floorToScreenPixels(size.width / CGFloat(self.itemNodes.count)), height: size.height)
|
||||||
|
|
||||||
transition.updateBounds(node: self.selectionNode, bounds: CGRect(origin: CGPoint(), size: itemSize))
|
transition.updateBounds(node: self.selectionNode, bounds: CGRect(origin: CGPoint(), size: itemSize))
|
||||||
transition.updatePosition(node: self.selectionNode, position: CGPoint(x: itemSize.width / 2.0 + itemSize.width * CGFloat(selectedIndex), y: size.height / 2.0))
|
transition.updatePosition(node: self.selectionNode, position: CGPoint(x: itemSize.width / 2.0 + itemSize.width * CGFloat(selectedIndex), y: size.height / 2.0))
|
||||||
|
|
||||||
@ -316,7 +316,7 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
|
|||||||
transition.updateFrame(node: dividerNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels(delta * CGFloat(i + 1) - dividerSize.width / 2.0), y: (size.height - dividerSize.height) / 2.0), size: dividerSize))
|
transition.updateFrame(node: dividerNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels(delta * CGFloat(i + 1) - dividerSize.width / 2.0), y: (size.height - dividerSize.height) / 2.0), size: dividerSize))
|
||||||
|
|
||||||
let dividerAlpha: CGFloat
|
let dividerAlpha: CGFloat
|
||||||
if (self.selectedIndex - 1 ... self.selectedIndex).contains(i) {
|
if (selectedIndex - 1 ... selectedIndex).contains(i) {
|
||||||
dividerAlpha = 0.0
|
dividerAlpha = 0.0
|
||||||
} else {
|
} else {
|
||||||
dividerAlpha = 1.0
|
dividerAlpha = 1.0
|
||||||
|
|||||||
@ -236,17 +236,17 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
|||||||
let timestamp = self.referenceTimestamp
|
let timestamp = self.referenceTimestamp
|
||||||
|
|
||||||
let timestamp1 = timestamp + 120
|
let timestamp1 = timestamp + 120
|
||||||
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
||||||
|
|
||||||
let presenceTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + 60 * 60)
|
let presenceTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + 60 * 60)
|
||||||
let timestamp2 = timestamp + 3660
|
let timestamp2 = timestamp + 3660
|
||||||
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), notificationSettings: nil, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), notificationSettings: nil, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
||||||
|
|
||||||
let timestamp3 = timestamp + 3200
|
let timestamp3 = timestamp + 3200
|
||||||
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer3), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer3), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
||||||
|
|
||||||
let timestamp4 = timestamp + 3000
|
let timestamp4 = timestamp + 3000
|
||||||
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer4), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer4), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
||||||
|
|
||||||
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
|
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
|
||||||
if let chatNodes = self.chatNodes {
|
if let chatNodes = self.chatNodes {
|
||||||
|
|||||||
@ -230,7 +230,6 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors):
|
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors):
|
||||||
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updatedTheme: { theme in
|
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updatedTheme: { theme in
|
||||||
arguments.updateTheme(theme)
|
arguments.updateTheme(theme)
|
||||||
}, longTapped: { _ in
|
|
||||||
}, contextAction: nil)
|
}, contextAction: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -344,24 +344,24 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
let timestamp = self.referenceTimestamp
|
let timestamp = self.referenceTimestamp
|
||||||
|
|
||||||
let timestamp1 = timestamp + 120
|
let timestamp1 = timestamp + 120
|
||||||
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
||||||
|
|
||||||
let presenceTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + 60 * 60)
|
let presenceTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + 60 * 60)
|
||||||
let timestamp2 = timestamp + 3660
|
let timestamp2 = timestamp + 3660
|
||||||
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: nil, notificationSettings: nil, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: [(peer2, .typingText)], isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: nil, notificationSettings: nil, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: [(peer2, .typingText)], isAd: false, ignoreUnreadBadge: false, displayAsMessage: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
||||||
|
|
||||||
let timestamp3 = timestamp + 3200
|
let timestamp3 = timestamp + 3200
|
||||||
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer3), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer3), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
||||||
|
|
||||||
let timestamp4 = timestamp + 3000
|
let timestamp4 = timestamp + 3000
|
||||||
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer4), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer4), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
||||||
|
|
||||||
let timestamp5 = timestamp + 1000
|
let timestamp5 = timestamp + 1000
|
||||||
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer5.id, namespace: 0, id: 0), timestamp: timestamp5)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp5, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer5, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer5), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer5.id, namespace: 0, id: 0), timestamp: timestamp5)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp5, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer5, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer5), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
||||||
|
|
||||||
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer6.id, namespace: 0, id: 0), timestamp: timestamp - 360)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer6.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 360, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer6), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer6.id, namespace: 0, id: 0), timestamp: timestamp - 360)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer6.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 360, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer6), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
||||||
|
|
||||||
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer7.id, namespace: 0, id: 0), timestamp: timestamp - 420)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer7.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 420, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer7), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer7.id, namespace: 0, id: 0), timestamp: timestamp - 420)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer7.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 420, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer7), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
|
||||||
|
|
||||||
let width: CGFloat
|
let width: CGFloat
|
||||||
if case .regular = layout.metrics.widthClass {
|
if case .regular = layout.metrics.widthClass {
|
||||||
|
|||||||
@ -67,11 +67,10 @@ private final class ThemeSettingsControllerArguments {
|
|||||||
let toggleLargeEmoji: (Bool) -> Void
|
let toggleLargeEmoji: (Bool) -> Void
|
||||||
let disableAnimations: (Bool) -> Void
|
let disableAnimations: (Bool) -> Void
|
||||||
let selectAppIcon: (String) -> Void
|
let selectAppIcon: (String) -> Void
|
||||||
let presentThemeMenu: (PresentationThemeReference, Bool) -> Void
|
|
||||||
let editTheme: (PresentationCloudTheme) -> Void
|
let editTheme: (PresentationCloudTheme) -> Void
|
||||||
let contextAction: (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void
|
let contextAction: (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void
|
||||||
|
|
||||||
init(context: AccountContext, updateTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, PresentationThemeAccentColor?) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, presentThemeMenu: @escaping (PresentationThemeReference, Bool) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, contextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void) {
|
init(context: AccountContext, updateTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, PresentationThemeAccentColor?) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, contextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.updateTheme = updateTheme
|
self.updateTheme = updateTheme
|
||||||
self.selectFontSize = selectFontSize
|
self.selectFontSize = selectFontSize
|
||||||
@ -82,7 +81,6 @@ private final class ThemeSettingsControllerArguments {
|
|||||||
self.toggleLargeEmoji = toggleLargeEmoji
|
self.toggleLargeEmoji = toggleLargeEmoji
|
||||||
self.disableAnimations = disableAnimations
|
self.disableAnimations = disableAnimations
|
||||||
self.selectAppIcon = selectAppIcon
|
self.selectAppIcon = selectAppIcon
|
||||||
self.presentThemeMenu = presentThemeMenu
|
|
||||||
self.editTheme = editTheme
|
self.editTheme = editTheme
|
||||||
self.contextAction = contextAction
|
self.contextAction = contextAction
|
||||||
}
|
}
|
||||||
@ -323,8 +321,6 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
arguments.updateTheme(theme)
|
arguments.updateTheme(theme)
|
||||||
}
|
}
|
||||||
}, longTapped: { theme in
|
|
||||||
//arguments.presentThemeMenu(theme, theme.index == currentTheme.index)
|
|
||||||
}, contextAction: { theme, node, gesture in
|
}, contextAction: { theme, node, gesture in
|
||||||
arguments.contextAction(theme.index == currentTheme.index, theme, node, gesture)
|
arguments.contextAction(theme.index == currentTheme.index, theme, node, gesture)
|
||||||
})
|
})
|
||||||
@ -460,66 +456,6 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
currentAppIconName.set(name)
|
currentAppIconName.set(name)
|
||||||
context.sharedContext.applicationBindings.requestSetAlternateIconName(name, { _ in
|
context.sharedContext.applicationBindings.requestSetAlternateIconName(name, { _ in
|
||||||
})
|
})
|
||||||
}, presentThemeMenu: { themeReference, isCurrent in
|
|
||||||
guard case let .cloud(theme) = themeReference else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
let actionSheet = ActionSheetController(presentationTheme: presentationData.theme)
|
|
||||||
var items: [ActionSheetItem] = []
|
|
||||||
items.append(ActionSheetTextItem(title: theme.theme.title))
|
|
||||||
if theme.theme.isCreator {
|
|
||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_EditTheme, color: .accent, action: { [weak actionSheet] in
|
|
||||||
actionSheet?.dismissAnimated()
|
|
||||||
let controller = editThemeController(context: context, mode: .edit(theme), navigateToChat: { peerId in
|
|
||||||
if let navigationController = getNavigationControllerImpl?() {
|
|
||||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_ShareTheme, color: .accent, action: { [weak actionSheet] in
|
|
||||||
actionSheet?.dismissAnimated()
|
|
||||||
let controller = ShareController(context: context, subject: .url("https://t.me/addtheme/\(theme.theme.slug)"), preferredAction: .default)
|
|
||||||
presentControllerImpl?(controller, nil)
|
|
||||||
}))
|
|
||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveTheme, color: .destructive, action: { [weak actionSheet] in
|
|
||||||
actionSheet?.dismissAnimated()
|
|
||||||
|
|
||||||
let actionSheet = ActionSheetController(presentationTheme: presentationData.theme)
|
|
||||||
var items: [ActionSheetItem] = []
|
|
||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeConfirmation, color: .destructive, action: { [weak actionSheet] in
|
|
||||||
actionSheet?.dismissAnimated()
|
|
||||||
let _ = (cloudThemes.get() |> delay(0.5, queue: Queue.mainQueue())
|
|
||||||
|> take(1)
|
|
||||||
|> deliverOnMainQueue).start(next: { themes in
|
|
||||||
if isCurrent, let themeIndex = themes.firstIndex(where: { $0.id == theme.theme.id }) {
|
|
||||||
let newTheme: PresentationThemeReference
|
|
||||||
if themeIndex > 0 {
|
|
||||||
newTheme = .cloud(PresentationCloudTheme(theme: themes[themeIndex - 1], resolvedWallpaper: nil))
|
|
||||||
} else {
|
|
||||||
newTheme = .builtin(.nightAccent)
|
|
||||||
}
|
|
||||||
updateThemeImpl?(newTheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = deleteThemeInteractively(account: context.account, accountManager: context.sharedContext.accountManager, theme: theme.theme).start()
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
|
||||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
|
|
||||||
actionSheet?.dismissAnimated()
|
|
||||||
})
|
|
||||||
])])
|
|
||||||
presentControllerImpl?(actionSheet, nil)
|
|
||||||
}))
|
|
||||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
|
||||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
|
|
||||||
actionSheet?.dismissAnimated()
|
|
||||||
})
|
|
||||||
])])
|
|
||||||
presentControllerImpl?(actionSheet, nil)
|
|
||||||
}, editTheme: { theme in
|
}, editTheme: { theme in
|
||||||
let controller = editThemeController(context: context, mode: .edit(theme), navigateToChat: { peerId in
|
let controller = editThemeController(context: context, mode: .edit(theme), navigateToChat: { peerId in
|
||||||
if let navigationController = getNavigationControllerImpl?() {
|
if let navigationController = getNavigationControllerImpl?() {
|
||||||
|
|||||||
@ -88,11 +88,10 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
|
|||||||
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||||
let currentTheme: PresentationThemeReference
|
let currentTheme: PresentationThemeReference
|
||||||
let updatedTheme: (PresentationThemeReference) -> Void
|
let updatedTheme: (PresentationThemeReference) -> Void
|
||||||
let longTapped: (PresentationThemeReference) -> Void
|
|
||||||
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
let tag: ItemListItemTag?
|
let tag: ItemListItemTag?
|
||||||
|
|
||||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, longTapped: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
|
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
@ -100,7 +99,6 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
|
|||||||
self.themeSpecificAccentColors = themeSpecificAccentColors
|
self.themeSpecificAccentColors = themeSpecificAccentColors
|
||||||
self.currentTheme = currentTheme
|
self.currentTheme = currentTheme
|
||||||
self.updatedTheme = updatedTheme
|
self.updatedTheme = updatedTheme
|
||||||
self.longTapped = longTapped
|
|
||||||
self.contextAction = contextAction
|
self.contextAction = contextAction
|
||||||
self.tag = tag
|
self.tag = tag
|
||||||
self.sectionId = sectionId
|
self.sectionId = sectionId
|
||||||
@ -146,7 +144,6 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
|
|||||||
private let overlayNode: ASImageNode
|
private let overlayNode: ASImageNode
|
||||||
private let textNode: ASTextNode
|
private let textNode: ASTextNode
|
||||||
private var action: (() -> Void)?
|
private var action: (() -> Void)?
|
||||||
private var longTapAction: (() -> Void)?
|
|
||||||
private var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
private var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
|
|
||||||
private var theme: PresentationThemeReference?
|
private var theme: PresentationThemeReference?
|
||||||
@ -186,7 +183,7 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?, currentTheme: PresentationTheme, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void, longTapAction: @escaping () -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) {
|
func setup(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?, currentTheme: PresentationTheme, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) {
|
||||||
let updatedTheme = self.currentTheme == nil || currentTheme !== self.currentTheme!
|
let updatedTheme = self.currentTheme == nil || currentTheme !== self.currentTheme!
|
||||||
if case let .cloud(theme) = theme, theme.theme.file == nil {
|
if case let .cloud(theme) = theme, theme.theme.file == nil {
|
||||||
if updatedTheme || accentColor != self.accentColor {
|
if updatedTheme || accentColor != self.accentColor {
|
||||||
@ -208,12 +205,7 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
|
|||||||
self.selected = selected
|
self.selected = selected
|
||||||
}
|
}
|
||||||
self.textNode.attributedText = title
|
self.textNode.attributedText = title
|
||||||
self.action = {
|
self.action = action
|
||||||
action()
|
|
||||||
}
|
|
||||||
self.longTapAction = {
|
|
||||||
longTapAction()
|
|
||||||
}
|
|
||||||
self.contextAction = contextAction
|
self.contextAction = contextAction
|
||||||
self.containerNode.isGestureEnabled = !selected
|
self.containerNode.isGestureEnabled = !selected
|
||||||
}
|
}
|
||||||
@ -236,8 +228,6 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
|
|||||||
switch gesture {
|
switch gesture {
|
||||||
case .tap:
|
case .tap:
|
||||||
self.action?()
|
self.action?()
|
||||||
case .longTap:
|
|
||||||
self.longTapAction?()
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -399,8 +389,6 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
if let imageNode = imageNode {
|
if let imageNode = imageNode {
|
||||||
self?.scrollToNode(imageNode, animated: true)
|
self?.scrollToNode(imageNode, animated: true)
|
||||||
}
|
}
|
||||||
}, longTapAction: {
|
|
||||||
item.longTapped(theme)
|
|
||||||
}, contextAction: item.contextAction.flatMap {
|
}, contextAction: item.contextAction.flatMap {
|
||||||
contextAction in
|
contextAction in
|
||||||
return { node, gesture in
|
return { node, gesture in
|
||||||
|
|||||||
@ -297,8 +297,7 @@ public final class ShareController: ViewController {
|
|||||||
self?.controllerNode.cancel?()
|
self?.controllerNode.cancel?()
|
||||||
showInChat(message)
|
showInChat(message)
|
||||||
})
|
})
|
||||||
}
|
} else if let chatPeer = message.peers[message.id.peerId] as? TelegramChannel, messages.count == 1 || sameGroupingKey {
|
||||||
else if let chatPeer = message.peers[message.id.peerId] as? TelegramChannel, messages.count == 1 || sameGroupingKey {
|
|
||||||
if message.id.namespace == Namespaces.Message.Cloud {
|
if message.id.namespace == Namespaces.Message.Cloud {
|
||||||
self.defaultAction = ShareControllerAction(title: self.presentationData.strings.ShareMenu_CopyShareLink, action: { [weak self] in
|
self.defaultAction = ShareControllerAction(title: self.presentationData.strings.ShareMenu_CopyShareLink, action: { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
|
|||||||
@ -131,6 +131,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
|||||||
case archiveIntroDismissed = 11
|
case archiveIntroDismissed = 11
|
||||||
case callsTabTip = 12
|
case callsTabTip = 12
|
||||||
case cellularDataPermissionWarning = 13
|
case cellularDataPermissionWarning = 13
|
||||||
|
case chatMessageSearchResultsTip = 14
|
||||||
|
|
||||||
var key: ValueBoxKey {
|
var key: ValueBoxKey {
|
||||||
let v = ValueBoxKey(length: 4)
|
let v = ValueBoxKey(length: 4)
|
||||||
@ -228,6 +229,10 @@ private struct ApplicationSpecificNoticeKeys {
|
|||||||
static func callsTabTip() -> NoticeEntryKey {
|
static func callsTabTip() -> NoticeEntryKey {
|
||||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.callsTabTip.key)
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.callsTabTip.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func chatMessageSearchResultsTip() -> NoticeEntryKey {
|
||||||
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatMessageSearchResultsTip.key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ApplicationSpecificNotice {
|
public struct ApplicationSpecificNotice {
|
||||||
@ -518,6 +523,29 @@ public struct ApplicationSpecificNotice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static func getChatMessageSearchResultsTip(accountManager: AccountManager) -> Signal<Int32, NoError> {
|
||||||
|
return accountManager.transaction { transaction -> Int32 in
|
||||||
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatMessageSearchResultsTip()) as? ApplicationSpecificCounterNotice {
|
||||||
|
return value.value
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func incrementChatMessageSearchResultsTip(accountManager: AccountManager, count: Int32 = 1) -> Signal<Void, NoError> {
|
||||||
|
return accountManager.transaction { transaction -> Void in
|
||||||
|
var currentValue: Int32 = 0
|
||||||
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatMessageSearchResultsTip()) as? ApplicationSpecificCounterNotice {
|
||||||
|
currentValue = value.value
|
||||||
|
}
|
||||||
|
currentValue += count
|
||||||
|
|
||||||
|
transaction.setNotice(ApplicationSpecificNoticeKeys.chatMessageSearchResultsTip(), ApplicationSpecificCounterNotice(value: currentValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static func reset(accountManager: AccountManager) -> Signal<Void, NoError> {
|
public static func reset(accountManager: AccountManager) -> Signal<Void, NoError> {
|
||||||
return accountManager.transaction { transaction -> Void in
|
return accountManager.transaction { transaction -> Void in
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import Display
|
|||||||
public extension TabBarControllerTheme {
|
public extension TabBarControllerTheme {
|
||||||
convenience init(rootControllerTheme: PresentationTheme) {
|
convenience init(rootControllerTheme: PresentationTheme) {
|
||||||
let theme = rootControllerTheme.rootController.tabBar
|
let theme = rootControllerTheme.rootController.tabBar
|
||||||
self.init(backgroundColor: rootControllerTheme.list.plainBackgroundColor, tabBarBackgroundColor: theme.backgroundColor, tabBarSeparatorColor: theme.separatorColor, tabBarTextColor: theme.textColor, tabBarSelectedTextColor: theme.selectedIconColor, tabBarBadgeBackgroundColor: theme.badgeBackgroundColor, tabBarBadgeStrokeColor: theme.badgeStrokeColor, tabBarBadgeTextColor: theme.badgeTextColor)
|
self.init(backgroundColor: rootControllerTheme.list.plainBackgroundColor, tabBarBackgroundColor: theme.backgroundColor, tabBarSeparatorColor: theme.separatorColor, tabBarIconColor: theme.iconColor, tabBarSelectedIconColor: theme.selectedIconColor, tabBarTextColor: theme.textColor, tabBarSelectedTextColor: theme.selectedTextColor, tabBarBadgeBackgroundColor: theme.badgeBackgroundColor, tabBarBadgeStrokeColor: theme.badgeStrokeColor, tabBarBadgeTextColor: theme.badgeTextColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -187,6 +187,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
|||||||
dateTextColor: UIColor(rgb: 0x8e8e92),
|
dateTextColor: UIColor(rgb: 0x8e8e92),
|
||||||
authorNameColor: UIColor(rgb: 0xffffff),
|
authorNameColor: UIColor(rgb: 0xffffff),
|
||||||
messageTextColor: UIColor(rgb: 0x8e8e92),
|
messageTextColor: UIColor(rgb: 0x8e8e92),
|
||||||
|
messageHighlightedTextColor: UIColor(rgb: 0xffffff),
|
||||||
messageDraftTextColor: UIColor(rgb: 0xdd4b39),
|
messageDraftTextColor: UIColor(rgb: 0xdd4b39),
|
||||||
checkmarkColor: accentColor,
|
checkmarkColor: accentColor,
|
||||||
pendingIndicatorColor: UIColor(rgb: 0xffffff),
|
pendingIndicatorColor: UIColor(rgb: 0xffffff),
|
||||||
|
|||||||
@ -163,6 +163,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
|||||||
dateTextColor: mainSecondaryTextColor.withAlphaComponent(0.5),
|
dateTextColor: mainSecondaryTextColor.withAlphaComponent(0.5),
|
||||||
authorNameColor: UIColor(rgb: 0xffffff),
|
authorNameColor: UIColor(rgb: 0xffffff),
|
||||||
messageTextColor: mainSecondaryTextColor.withAlphaComponent(0.5),
|
messageTextColor: mainSecondaryTextColor.withAlphaComponent(0.5),
|
||||||
|
messageHighlightedTextColor: UIColor(rgb: 0xffffff),
|
||||||
messageDraftTextColor: UIColor(rgb: 0xdd4b39),
|
messageDraftTextColor: UIColor(rgb: 0xdd4b39),
|
||||||
checkmarkColor: accentColor,
|
checkmarkColor: accentColor,
|
||||||
pendingIndicatorColor: mainSecondaryTextColor.withAlphaComponent(0.4),
|
pendingIndicatorColor: mainSecondaryTextColor.withAlphaComponent(0.4),
|
||||||
|
|||||||
@ -170,6 +170,7 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
|
|||||||
dateTextColor: UIColor(rgb: 0x8e8e93),
|
dateTextColor: UIColor(rgb: 0x8e8e93),
|
||||||
authorNameColor: .black,
|
authorNameColor: .black,
|
||||||
messageTextColor: UIColor(rgb: 0x8e8e93),
|
messageTextColor: UIColor(rgb: 0x8e8e93),
|
||||||
|
messageHighlightedTextColor: .black,
|
||||||
messageDraftTextColor: UIColor(rgb: 0xdd4b39),
|
messageDraftTextColor: UIColor(rgb: 0xdd4b39),
|
||||||
checkmarkColor: day ? accentColor : UIColor(rgb: 0x21c004),
|
checkmarkColor: day ? accentColor : UIColor(rgb: 0x21c004),
|
||||||
pendingIndicatorColor: UIColor(rgb: 0x8e8e93),
|
pendingIndicatorColor: UIColor(rgb: 0x8e8e93),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -386,6 +386,7 @@ public final class PresentationThemeChatList {
|
|||||||
public let dateTextColor: UIColor
|
public let dateTextColor: UIColor
|
||||||
public let authorNameColor: UIColor
|
public let authorNameColor: UIColor
|
||||||
public let messageTextColor: UIColor
|
public let messageTextColor: UIColor
|
||||||
|
public let messageHighlightedTextColor: UIColor
|
||||||
public let messageDraftTextColor: UIColor
|
public let messageDraftTextColor: UIColor
|
||||||
public let checkmarkColor: UIColor
|
public let checkmarkColor: UIColor
|
||||||
public let pendingIndicatorColor: UIColor
|
public let pendingIndicatorColor: UIColor
|
||||||
@ -408,7 +409,7 @@ public final class PresentationThemeChatList {
|
|||||||
public let unpinnedArchiveAvatarColor: PresentationThemeArchiveAvatarColors
|
public let unpinnedArchiveAvatarColor: PresentationThemeArchiveAvatarColors
|
||||||
public let onlineDotColor: UIColor
|
public let onlineDotColor: UIColor
|
||||||
|
|
||||||
init(backgroundColor: UIColor, itemSeparatorColor: UIColor, itemBackgroundColor: UIColor, pinnedItemBackgroundColor: UIColor, itemHighlightedBackgroundColor: UIColor, itemSelectedBackgroundColor: UIColor, titleColor: UIColor, secretTitleColor: UIColor, dateTextColor: UIColor, authorNameColor: UIColor, messageTextColor: UIColor, messageDraftTextColor: UIColor, checkmarkColor: UIColor, pendingIndicatorColor: UIColor, failedFillColor: UIColor, failedForegroundColor: UIColor, muteIconColor: UIColor, unreadBadgeActiveBackgroundColor: UIColor, unreadBadgeActiveTextColor: UIColor, unreadBadgeInactiveBackgroundColor: UIColor, unreadBadgeInactiveTextColor: UIColor, pinnedBadgeColor: UIColor, pinnedSearchBarColor: UIColor, regularSearchBarColor: UIColor, sectionHeaderFillColor: UIColor, sectionHeaderTextColor: UIColor, verifiedIconFillColor: UIColor, verifiedIconForegroundColor: UIColor, secretIconColor: UIColor, pinnedArchiveAvatarColor: PresentationThemeArchiveAvatarColors, unpinnedArchiveAvatarColor: PresentationThemeArchiveAvatarColors, onlineDotColor: UIColor) {
|
init(backgroundColor: UIColor, itemSeparatorColor: UIColor, itemBackgroundColor: UIColor, pinnedItemBackgroundColor: UIColor, itemHighlightedBackgroundColor: UIColor, itemSelectedBackgroundColor: UIColor, titleColor: UIColor, secretTitleColor: UIColor, dateTextColor: UIColor, authorNameColor: UIColor, messageTextColor: UIColor, messageHighlightedTextColor: UIColor, messageDraftTextColor: UIColor, checkmarkColor: UIColor, pendingIndicatorColor: UIColor, failedFillColor: UIColor, failedForegroundColor: UIColor, muteIconColor: UIColor, unreadBadgeActiveBackgroundColor: UIColor, unreadBadgeActiveTextColor: UIColor, unreadBadgeInactiveBackgroundColor: UIColor, unreadBadgeInactiveTextColor: UIColor, pinnedBadgeColor: UIColor, pinnedSearchBarColor: UIColor, regularSearchBarColor: UIColor, sectionHeaderFillColor: UIColor, sectionHeaderTextColor: UIColor, verifiedIconFillColor: UIColor, verifiedIconForegroundColor: UIColor, secretIconColor: UIColor, pinnedArchiveAvatarColor: PresentationThemeArchiveAvatarColors, unpinnedArchiveAvatarColor: PresentationThemeArchiveAvatarColors, onlineDotColor: UIColor) {
|
||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
self.itemSeparatorColor = itemSeparatorColor
|
self.itemSeparatorColor = itemSeparatorColor
|
||||||
self.itemBackgroundColor = itemBackgroundColor
|
self.itemBackgroundColor = itemBackgroundColor
|
||||||
@ -420,6 +421,7 @@ public final class PresentationThemeChatList {
|
|||||||
self.dateTextColor = dateTextColor
|
self.dateTextColor = dateTextColor
|
||||||
self.authorNameColor = authorNameColor
|
self.authorNameColor = authorNameColor
|
||||||
self.messageTextColor = messageTextColor
|
self.messageTextColor = messageTextColor
|
||||||
|
self.messageHighlightedTextColor = messageHighlightedTextColor
|
||||||
self.messageDraftTextColor = messageDraftTextColor
|
self.messageDraftTextColor = messageDraftTextColor
|
||||||
self.checkmarkColor = checkmarkColor
|
self.checkmarkColor = checkmarkColor
|
||||||
self.pendingIndicatorColor = pendingIndicatorColor
|
self.pendingIndicatorColor = pendingIndicatorColor
|
||||||
|
|||||||
@ -760,6 +760,7 @@ extension PresentationThemeChatList: Codable {
|
|||||||
case dateText
|
case dateText
|
||||||
case authorName
|
case authorName
|
||||||
case messageText
|
case messageText
|
||||||
|
case messageHighlightedText
|
||||||
case messageDraftText
|
case messageDraftText
|
||||||
case checkmark
|
case checkmark
|
||||||
case pendingIndicator
|
case pendingIndicator
|
||||||
@ -796,6 +797,7 @@ extension PresentationThemeChatList: Codable {
|
|||||||
dateTextColor: try decodeColor(values, .dateText),
|
dateTextColor: try decodeColor(values, .dateText),
|
||||||
authorNameColor: try decodeColor(values, .authorName),
|
authorNameColor: try decodeColor(values, .authorName),
|
||||||
messageTextColor: try decodeColor(values, .messageText),
|
messageTextColor: try decodeColor(values, .messageText),
|
||||||
|
messageHighlightedTextColor: try decodeColor(values, .messageHighlightedText),
|
||||||
messageDraftTextColor: try decodeColor(values, .messageDraftText),
|
messageDraftTextColor: try decodeColor(values, .messageDraftText),
|
||||||
checkmarkColor: try decodeColor(values, .checkmark),
|
checkmarkColor: try decodeColor(values, .checkmark),
|
||||||
pendingIndicatorColor: try decodeColor(values, .pendingIndicator),
|
pendingIndicatorColor: try decodeColor(values, .pendingIndicator),
|
||||||
@ -832,6 +834,7 @@ extension PresentationThemeChatList: Codable {
|
|||||||
try encodeColor(&values, self.dateTextColor, .dateText)
|
try encodeColor(&values, self.dateTextColor, .dateText)
|
||||||
try encodeColor(&values, self.authorNameColor, .authorName)
|
try encodeColor(&values, self.authorNameColor, .authorName)
|
||||||
try encodeColor(&values, self.messageTextColor, .messageText)
|
try encodeColor(&values, self.messageTextColor, .messageText)
|
||||||
|
try encodeColor(&values, self.messageHighlightedTextColor, .messageHighlightedText)
|
||||||
try encodeColor(&values, self.messageDraftTextColor, .messageDraftText)
|
try encodeColor(&values, self.messageDraftTextColor, .messageDraftText)
|
||||||
try encodeColor(&values, self.checkmarkColor, .checkmark)
|
try encodeColor(&values, self.checkmarkColor, .checkmark)
|
||||||
try encodeColor(&values, self.pendingIndicatorColor, .pendingIndicator)
|
try encodeColor(&values, self.pendingIndicatorColor, .pendingIndicator)
|
||||||
|
|||||||
@ -183,6 +183,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
private let startingBot = ValuePromise<Bool>(false, ignoreRepeated: true)
|
private let startingBot = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
private let unblockingPeer = ValuePromise<Bool>(false, ignoreRepeated: true)
|
private let unblockingPeer = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
private let searching = ValuePromise<Bool>(false, ignoreRepeated: true)
|
private let searching = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
|
private let searchResult = Promise<(SearchMessagesResult, SearchMessagesState)?>()
|
||||||
private let loadingMessage = ValuePromise<Bool>(false, ignoreRepeated: true)
|
private let loadingMessage = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
|
|
||||||
private var preloadHistoryPeerId: PeerId?
|
private var preloadHistoryPeerId: PeerId?
|
||||||
@ -3083,7 +3084,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
|
|
||||||
let editingMessage = strongSelf.editingMessage
|
let editingMessage = strongSelf.editingMessage
|
||||||
let text = trimChatInputText(editMessage.inputState.inputText)
|
let text = trimChatInputText(convertMarkdownToAttributes(editMessage.inputState.inputText))
|
||||||
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
|
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
|
||||||
var entitiesAttribute: TextEntitiesMessageAttribute?
|
var entitiesAttribute: TextEntitiesMessageAttribute?
|
||||||
if !entities.isEmpty {
|
if !entities.isEmpty {
|
||||||
@ -3173,6 +3174,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
})
|
})
|
||||||
strongSelf.updateItemNodesSearchTextHighlightStates()
|
strongSelf.updateItemNodesSearchTextHighlightStates()
|
||||||
}
|
}
|
||||||
|
}, openSearchResults: { [weak self] in
|
||||||
|
if let strongSelf = self, let searchData = strongSelf.presentationInterfaceState.search, let results = searchData.resultsState {
|
||||||
|
let _ = (strongSelf.searchResult.get()
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] searchResult in
|
||||||
|
if let strongSelf = self, let searchResult = searchResult?.0 {
|
||||||
|
let controller = ChatSearchResultsController(context: strongSelf.context, searchQuery: searchData.query, messages: searchResult.messages, navigateToMessageIndex: { index in
|
||||||
|
strongSelf.interfaceInteraction?.navigateMessageSearch(.index(index))
|
||||||
|
})
|
||||||
|
strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}, navigateMessageSearch: { [weak self] action in
|
}, navigateMessageSearch: { [weak self] action in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
var navigateIndex: MessageIndex?
|
var navigateIndex: MessageIndex?
|
||||||
@ -3189,6 +3203,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
if index != resultsState.messageIndices.count - 1 {
|
if index != resultsState.messageIndices.count - 1 {
|
||||||
updatedIndex = index + 1
|
updatedIndex = index + 1
|
||||||
}
|
}
|
||||||
|
case let .index(index):
|
||||||
|
if index >= 0 && index < resultsState.messageIndices.count {
|
||||||
|
updatedIndex = index
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let updatedIndex = updatedIndex {
|
if let updatedIndex = updatedIndex {
|
||||||
navigateIndex = resultsState.messageIndices[updatedIndex]
|
navigateIndex = resultsState.messageIndices[updatedIndex]
|
||||||
@ -4771,9 +4789,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateItemNodesSearchTextHighlightStates() {
|
private func updateItemNodesSearchTextHighlightStates() {
|
||||||
if true {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var searchString: String?
|
var searchString: String?
|
||||||
if let search = self.presentationInterfaceState.search, let resultsState = search.resultsState, !resultsState.messageIndices.isEmpty {
|
if let search = self.presentationInterfaceState.search, let resultsState = search.resultsState, !resultsState.messageIndices.isEmpty {
|
||||||
searchString = search.query
|
searchString = search.query
|
||||||
@ -5979,6 +5994,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
if queryIsEmpty {
|
if queryIsEmpty {
|
||||||
self.searching.set(false)
|
self.searching.set(false)
|
||||||
self.searchDisposable?.set(nil)
|
self.searchDisposable?.set(nil)
|
||||||
|
self.searchResult.set(.single(nil))
|
||||||
if let data = interfaceState.search {
|
if let data = interfaceState.search {
|
||||||
return interfaceState.updatedSearch(data.withUpdatedResultsState(nil))
|
return interfaceState.updatedSearch(data.withUpdatedResultsState(nil))
|
||||||
}
|
}
|
||||||
@ -5991,8 +6007,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
searchDisposable = MetaDisposable()
|
searchDisposable = MetaDisposable()
|
||||||
self.searchDisposable = searchDisposable
|
self.searchDisposable = searchDisposable
|
||||||
}
|
}
|
||||||
searchDisposable.set((searchMessages(account: self.context.account, location: searchState.location, query: searchState.query, state: nil, limit: limit)
|
|
||||||
|
let search = searchMessages(account: self.context.account, location: searchState.location, query: searchState.query, state: nil, limit: limit)
|
||||||
|> delay(0.2, queue: Queue.mainQueue())
|
|> delay(0.2, queue: Queue.mainQueue())
|
||||||
|
self.searchResult.set(search |> map(Optional.init))
|
||||||
|
|
||||||
|
searchDisposable.set((search
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] results, updatedState in
|
|> deliverOnMainQueue).start(next: { [weak self] results, updatedState in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -168,8 +168,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, webEmbedType(content: content).supportsSeeking {
|
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, webEmbedType(content: content).supportsSeeking {
|
||||||
isSeekableWebMedia = true
|
isSeekableWebMedia = true
|
||||||
}
|
} else if media is TelegramMediaUnsupported {
|
||||||
else if media is TelegramMediaUnsupported {
|
|
||||||
isUnsupportedMedia = true
|
isUnsupportedMedia = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,6 +32,7 @@ final class ChatPanelInterfaceInteractionStatuses {
|
|||||||
enum ChatPanelSearchNavigationAction {
|
enum ChatPanelSearchNavigationAction {
|
||||||
case earlier
|
case earlier
|
||||||
case later
|
case later
|
||||||
|
case index(Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ChatPanelRestrictionInfoSubject {
|
enum ChatPanelRestrictionInfoSubject {
|
||||||
@ -64,6 +65,7 @@ final class ChatPanelInterfaceInteraction {
|
|||||||
let dismissMessageSearch: () -> Void
|
let dismissMessageSearch: () -> Void
|
||||||
let updateMessageSearch: (String) -> Void
|
let updateMessageSearch: (String) -> Void
|
||||||
let navigateMessageSearch: (ChatPanelSearchNavigationAction) -> Void
|
let navigateMessageSearch: (ChatPanelSearchNavigationAction) -> Void
|
||||||
|
let openSearchResults: () -> Void
|
||||||
let openCalendarSearch: () -> Void
|
let openCalendarSearch: () -> Void
|
||||||
let toggleMembersSearch: (Bool) -> Void
|
let toggleMembersSearch: (Bool) -> Void
|
||||||
let navigateToMessage: (MessageId) -> Void
|
let navigateToMessage: (MessageId) -> Void
|
||||||
@ -112,7 +114,7 @@ final class ChatPanelInterfaceInteraction {
|
|||||||
let openScheduledMessages: () -> Void
|
let openScheduledMessages: () -> Void
|
||||||
let statuses: ChatPanelInterfaceInteractionStatuses?
|
let statuses: ChatPanelInterfaceInteractionStatuses?
|
||||||
|
|
||||||
init(setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping () -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping () -> Void, openScheduledMessages: @escaping () -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) {
|
init(setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, openSearchResults: @escaping () -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping () -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping () -> Void, openScheduledMessages: @escaping () -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) {
|
||||||
self.setupReplyMessage = setupReplyMessage
|
self.setupReplyMessage = setupReplyMessage
|
||||||
self.setupEditMessage = setupEditMessage
|
self.setupEditMessage = setupEditMessage
|
||||||
self.beginMessageSelection = beginMessageSelection
|
self.beginMessageSelection = beginMessageSelection
|
||||||
@ -131,6 +133,7 @@ final class ChatPanelInterfaceInteraction {
|
|||||||
self.beginMessageSearch = beginMessageSearch
|
self.beginMessageSearch = beginMessageSearch
|
||||||
self.dismissMessageSearch = dismissMessageSearch
|
self.dismissMessageSearch = dismissMessageSearch
|
||||||
self.updateMessageSearch = updateMessageSearch
|
self.updateMessageSearch = updateMessageSearch
|
||||||
|
self.openSearchResults = openSearchResults
|
||||||
self.navigateMessageSearch = navigateMessageSearch
|
self.navigateMessageSearch = navigateMessageSearch
|
||||||
self.openCalendarSearch = openCalendarSearch
|
self.openCalendarSearch = openCalendarSearch
|
||||||
self.toggleMembersSearch = toggleMembersSearch
|
self.toggleMembersSearch = toggleMembersSearch
|
||||||
|
|||||||
@ -61,6 +61,7 @@ final class ChatRecentActionsController: TelegramBaseController {
|
|||||||
}, beginMessageSearch: { _, _ in
|
}, beginMessageSearch: { _, _ in
|
||||||
}, dismissMessageSearch: {
|
}, dismissMessageSearch: {
|
||||||
}, updateMessageSearch: { _ in
|
}, updateMessageSearch: { _ in
|
||||||
|
}, openSearchResults: {
|
||||||
}, navigateMessageSearch: { _ in
|
}, navigateMessageSearch: { _ in
|
||||||
}, openCalendarSearch: {
|
}, openCalendarSearch: {
|
||||||
}, toggleMembersSearch: { _ in
|
}, toggleMembersSearch: { _ in
|
||||||
|
|||||||
@ -15,7 +15,8 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
|||||||
private let downButton: HighlightableButtonNode
|
private let downButton: HighlightableButtonNode
|
||||||
private let calendarButton: HighlightableButtonNode
|
private let calendarButton: HighlightableButtonNode
|
||||||
private let membersButton: HighlightableButtonNode
|
private let membersButton: HighlightableButtonNode
|
||||||
private let resultsLabel: TextNode
|
private let resultsButton: HighlightableButtonNode
|
||||||
|
private let measureResultsLabel: TextNode
|
||||||
private let activityIndicator: ActivityIndicator
|
private let activityIndicator: ActivityIndicator
|
||||||
|
|
||||||
private var presentationInterfaceState: ChatPresentationInterfaceState?
|
private var presentationInterfaceState: ChatPresentationInterfaceState?
|
||||||
@ -23,6 +24,8 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
|||||||
private let activityDisposable = MetaDisposable()
|
private let activityDisposable = MetaDisposable()
|
||||||
private var displayActivity = false
|
private var displayActivity = false
|
||||||
|
|
||||||
|
private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, LayoutMetrics)?
|
||||||
|
|
||||||
override var interfaceInteraction: ChatPanelInterfaceInteraction? {
|
override var interfaceInteraction: ChatPanelInterfaceInteraction? {
|
||||||
didSet {
|
didSet {
|
||||||
if let statuses = self.interfaceInteraction?.statuses {
|
if let statuses = self.interfaceInteraction?.statuses {
|
||||||
@ -31,8 +34,8 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
|||||||
if let strongSelf = self, strongSelf.displayActivity != value {
|
if let strongSelf = self, strongSelf.displayActivity != value {
|
||||||
strongSelf.displayActivity = value
|
strongSelf.displayActivity = value
|
||||||
strongSelf.activityIndicator.isHidden = !value
|
strongSelf.activityIndicator.isHidden = !value
|
||||||
if let interfaceState = strongSelf.presentationInterfaceState {
|
if let interfaceState = strongSelf.presentationInterfaceState, let validLayout = strongSelf.validLayout {
|
||||||
strongSelf.calendarButton.isHidden = !((interfaceState.search?.query.isEmpty ?? true)) || strongSelf.displayActivity
|
strongSelf.updateLayout(width: validLayout.0, leftInset: validLayout.1, rightInset: validLayout.2, maxHeight: validLayout.3, transition: .immediate, interfaceState: interfaceState, metrics: validLayout.4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@ -49,9 +52,8 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
|||||||
self.downButton.isEnabled = false
|
self.downButton.isEnabled = false
|
||||||
self.calendarButton = HighlightableButtonNode()
|
self.calendarButton = HighlightableButtonNode()
|
||||||
self.membersButton = HighlightableButtonNode()
|
self.membersButton = HighlightableButtonNode()
|
||||||
self.resultsLabel = TextNode()
|
self.measureResultsLabel = TextNode()
|
||||||
self.resultsLabel.isUserInteractionEnabled = false
|
self.resultsButton = HighlightableButtonNode()
|
||||||
self.resultsLabel.displaysAsynchronously = false
|
|
||||||
self.activityIndicator = ActivityIndicator(type: .navigationAccent(theme))
|
self.activityIndicator = ActivityIndicator(type: .navigationAccent(theme))
|
||||||
self.activityIndicator.isHidden = true
|
self.activityIndicator.isHidden = true
|
||||||
|
|
||||||
@ -61,13 +63,14 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
|||||||
self.addSubnode(self.downButton)
|
self.addSubnode(self.downButton)
|
||||||
self.addSubnode(self.calendarButton)
|
self.addSubnode(self.calendarButton)
|
||||||
self.addSubnode(self.membersButton)
|
self.addSubnode(self.membersButton)
|
||||||
self.addSubnode(self.resultsLabel)
|
self.addSubnode(self.resultsButton)
|
||||||
self.addSubnode(self.activityIndicator)
|
self.addSubnode(self.activityIndicator)
|
||||||
|
|
||||||
self.upButton.addTarget(self, action: #selector(self.upPressed), forControlEvents: [.touchUpInside])
|
self.upButton.addTarget(self, action: #selector(self.upPressed), forControlEvents: [.touchUpInside])
|
||||||
self.downButton.addTarget(self, action: #selector(self.downPressed), forControlEvents: [.touchUpInside])
|
self.downButton.addTarget(self, action: #selector(self.downPressed), forControlEvents: [.touchUpInside])
|
||||||
self.calendarButton.addTarget(self, action: #selector(self.calendarPressed), forControlEvents: [.touchUpInside])
|
self.calendarButton.addTarget(self, action: #selector(self.calendarPressed), forControlEvents: [.touchUpInside])
|
||||||
self.membersButton.addTarget(self, action: #selector(self.membersPressed), forControlEvents: [.touchUpInside])
|
self.membersButton.addTarget(self, action: #selector(self.membersPressed), forControlEvents: [.touchUpInside])
|
||||||
|
self.resultsButton.addTarget(self, action: #selector(self.resultsPressed), forControlEvents: [.touchUpInside])
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -90,7 +93,13 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
|||||||
self.interfaceInteraction?.toggleMembersSearch(true)
|
self.interfaceInteraction?.toggleMembersSearch(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func resultsPressed() {
|
||||||
|
self.interfaceInteraction?.openSearchResults()
|
||||||
|
}
|
||||||
|
|
||||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||||
|
self.validLayout = (width, leftInset, rightInset, maxHeight, metrics)
|
||||||
|
|
||||||
if self.presentationInterfaceState != interfaceState {
|
if self.presentationInterfaceState != interfaceState {
|
||||||
let themeUpdated = self.presentationInterfaceState?.theme !== interfaceState.theme
|
let themeUpdated = self.presentationInterfaceState?.theme !== interfaceState.theme
|
||||||
|
|
||||||
@ -121,16 +130,16 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
|||||||
|
|
||||||
var resultIndex: Int?
|
var resultIndex: Int?
|
||||||
var resultCount: Int?
|
var resultCount: Int?
|
||||||
var resultsText: NSAttributedString?
|
var resultsText: String?
|
||||||
if let results = interfaceState.search?.resultsState {
|
if let results = interfaceState.search?.resultsState {
|
||||||
resultCount = results.messageIndices.count
|
resultCount = results.messageIndices.count
|
||||||
let displayTotalCount = results.completed ? results.messageIndices.count : Int(results.totalCount)
|
let displayTotalCount = results.completed ? results.messageIndices.count : Int(results.totalCount)
|
||||||
if let currentId = results.currentId, let index = results.messageIndices.firstIndex(where: { $0.id == currentId }) {
|
if let currentId = results.currentId, let index = results.messageIndices.firstIndex(where: { $0.id == currentId }) {
|
||||||
let adjustedIndex = results.messageIndices.count - 1 - index
|
let adjustedIndex = results.messageIndices.count - 1 - index
|
||||||
resultIndex = index
|
resultIndex = index
|
||||||
resultsText = NSAttributedString(string: interfaceState.strings.Items_NOfM("\(adjustedIndex + 1)", "\(displayTotalCount)").0, font: labelFont, textColor: interfaceState.theme.chat.inputPanel.primaryTextColor)
|
resultsText = interfaceState.strings.Items_NOfM("\(adjustedIndex + 1)", "\(displayTotalCount)").0
|
||||||
} else {
|
} else {
|
||||||
resultsText = NSAttributedString(string: interfaceState.strings.Conversation_SearchNoResults, font: labelFont, textColor: interfaceState.theme.chat.inputPanel.primaryTextColor)
|
resultsText = interfaceState.strings.Conversation_SearchNoResults
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,15 +161,19 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
|||||||
}
|
}
|
||||||
self.membersButton.isHidden = (!(interfaceState.search?.query.isEmpty ?? true)) || self.displayActivity || !canSearchMembers
|
self.membersButton.isHidden = (!(interfaceState.search?.query.isEmpty ?? true)) || self.displayActivity || !canSearchMembers
|
||||||
|
|
||||||
let makeLabelLayout = TextNode.asyncLayout(self.resultsLabel)
|
let resultsEnabled = (resultCount ?? 0) > 5
|
||||||
let (labelSize, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: resultsText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - leftInset - rightInset - 50.0, height: 100.0), alignment: .left, cutout: nil, insets: UIEdgeInsets()))
|
self.resultsButton.setTitle(resultsText ?? "", with: labelFont, with: resultsEnabled ? interfaceState.theme.chat.inputPanel.panelControlAccentColor : interfaceState.theme.chat.inputPanel.primaryTextColor, for: .normal)
|
||||||
|
self.resultsButton.isUserInteractionEnabled = resultsEnabled
|
||||||
|
|
||||||
|
let makeLabelLayout = TextNode.asyncLayout(self.measureResultsLabel)
|
||||||
|
let (labelSize, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: resultsText ?? "", font: labelFont, textColor: .black, paragraphAlignment: .left), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - leftInset - rightInset - 50.0, height: 100.0), alignment: .left, cutout: nil, insets: UIEdgeInsets()))
|
||||||
let _ = labelApply()
|
let _ = labelApply()
|
||||||
|
|
||||||
var resultsOffset: CGFloat = 16.0
|
var resultsOffset: CGFloat = 16.0
|
||||||
if !self.calendarButton.isHidden {
|
if !self.calendarButton.isHidden {
|
||||||
resultsOffset += 48.0
|
resultsOffset += 48.0
|
||||||
}
|
}
|
||||||
self.resultsLabel.frame = CGRect(origin: CGPoint(x: leftInset + resultsOffset, y: floor((panelHeight - labelSize.size.height) / 2.0)), size: labelSize.size)
|
self.resultsButton.frame = CGRect(origin: CGPoint(x: leftInset + resultsOffset, y: floor((panelHeight - labelSize.size.height) / 2.0)), size: labelSize.size)
|
||||||
|
|
||||||
let indicatorSize = self.activityIndicator.measure(CGSize(width: 22.0, height: 22.0))
|
let indicatorSize = self.activityIndicator.measure(CGSize(width: 22.0, height: 22.0))
|
||||||
self.activityIndicator.frame = CGRect(origin: CGPoint(x: width - rightInset - 41.0, y: floor((panelHeight - indicatorSize.height) / 2.0)), size: indicatorSize)
|
self.activityIndicator.frame = CGRect(origin: CGPoint(x: width - rightInset - 41.0, y: floor((panelHeight - indicatorSize.height) / 2.0)), size: indicatorSize)
|
||||||
|
|||||||
@ -0,0 +1,274 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import TelegramPresentationData
|
||||||
|
import TelegramStringFormatting
|
||||||
|
import MergeLists
|
||||||
|
import ChatListUI
|
||||||
|
import AccountContext
|
||||||
|
|
||||||
|
private enum ChatListSearchEntryStableId: Hashable {
|
||||||
|
case messageId(MessageId)
|
||||||
|
|
||||||
|
public static func ==(lhs: ChatListSearchEntryStableId, rhs: ChatListSearchEntryStableId) -> Bool {
|
||||||
|
switch lhs {
|
||||||
|
case let .messageId(messageId):
|
||||||
|
if case .messageId(messageId) = rhs {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ChatListSearchEntry: Comparable, Identifiable {
|
||||||
|
case message(Message, RenderedPeer, CombinedPeerReadState?, ChatListPresentationData)
|
||||||
|
|
||||||
|
public var stableId: ChatListSearchEntryStableId {
|
||||||
|
switch self {
|
||||||
|
case let .message(message, _, _, _):
|
||||||
|
return .messageId(message.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: ChatListSearchEntry, rhs: ChatListSearchEntry) -> Bool {
|
||||||
|
switch lhs {
|
||||||
|
case let .message(lhsMessage, lhsPeer, lhsCombinedPeerReadState, lhsPresentationData):
|
||||||
|
if case let .message(rhsMessage, rhsPeer, rhsCombinedPeerReadState, rhsPresentationData) = rhs {
|
||||||
|
if lhsMessage.id != rhsMessage.id {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhsMessage.stableVersion != rhsMessage.stableVersion {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhsPeer != rhsPeer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhsPresentationData !== rhsPresentationData {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhsCombinedPeerReadState != rhsCombinedPeerReadState {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func <(lhs: ChatListSearchEntry, rhs: ChatListSearchEntry) -> Bool {
|
||||||
|
switch lhs {
|
||||||
|
case let .message(lhsMessage, _, _, _):
|
||||||
|
if case let .message(rhsMessage, _, _, _) = rhs {
|
||||||
|
return lhsMessage.index < rhsMessage.index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
public func item(context: AccountContext, interaction: ChatListNodeInteraction) -> ListViewItem {
|
||||||
|
switch self {
|
||||||
|
case let .message(message, peer, readState, presentationData):
|
||||||
|
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(message: message, peer: peer, combinedReadState: readState, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: true, displayAsMessage: true), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ChatListSearchContainerTransition {
|
||||||
|
public let deletions: [ListViewDeleteItem]
|
||||||
|
public let insertions: [ListViewInsertItem]
|
||||||
|
public let updates: [ListViewUpdateItem]
|
||||||
|
|
||||||
|
public init(deletions: [ListViewDeleteItem], insertions: [ListViewInsertItem], updates: [ListViewUpdateItem]) {
|
||||||
|
self.deletions = deletions
|
||||||
|
self.insertions = insertions
|
||||||
|
self.updates = updates
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func chatListSearchContainerPreparedTransition(from fromEntries: [ChatListSearchEntry], to toEntries: [ChatListSearchEntry], context: AccountContext, interaction: ChatListNodeInteraction) -> ChatListSearchContainerTransition {
|
||||||
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
|
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||||
|
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, interaction: interaction), directionHint: nil) }
|
||||||
|
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, interaction: interaction), directionHint: nil) }
|
||||||
|
|
||||||
|
return ChatListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
|
||||||
|
private let context: AccountContext
|
||||||
|
private var presentationData: PresentationData
|
||||||
|
private let messages: [Message]
|
||||||
|
|
||||||
|
private var interaction: ChatListNodeInteraction?
|
||||||
|
|
||||||
|
private let listNode: ListView
|
||||||
|
|
||||||
|
private var enqueuedTransitions: [(ChatListSearchContainerTransition, Bool)] = []
|
||||||
|
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||||
|
|
||||||
|
var resultSelected: ((Int) -> Void)?
|
||||||
|
var dismiss: (() -> Void)?
|
||||||
|
|
||||||
|
private let presentationDataPromise: Promise<ChatListPresentationData>
|
||||||
|
private let disposable = MetaDisposable()
|
||||||
|
|
||||||
|
init(context: AccountContext, messages: [Message]) {
|
||||||
|
self.context = context
|
||||||
|
self.messages = messages
|
||||||
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
self.presentationDataPromise = Promise(ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations))
|
||||||
|
|
||||||
|
self.listNode = ListView()
|
||||||
|
self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.backgroundColor = self.presentationData.theme.chatList.backgroundColor
|
||||||
|
self.isOpaque = false
|
||||||
|
self.addSubnode(self.listNode)
|
||||||
|
|
||||||
|
let signal = self.presentationDataPromise.get()
|
||||||
|
|> map { presentationData -> [ChatListSearchEntry] in
|
||||||
|
var entries: [ChatListSearchEntry] = []
|
||||||
|
|
||||||
|
for message in messages {
|
||||||
|
var peer = RenderedPeer(message: message)
|
||||||
|
if let group = message.peers[message.id.peerId] as? TelegramGroup, let migrationReference = group.migrationReference {
|
||||||
|
if let channelPeer = message.peers[migrationReference.peerId] {
|
||||||
|
peer = RenderedPeer(peer: channelPeer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entries.append(.message(message, peer, nil, presentationData))
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
let interaction = ChatListNodeInteraction(activateSearch: {
|
||||||
|
}, peerSelected: { _ in
|
||||||
|
}, togglePeerSelected: { _ in
|
||||||
|
}, messageSelected: { [weak self] peer, message, _ in
|
||||||
|
if let strongSelf = self {
|
||||||
|
if let index = strongSelf.messages.firstIndex(where: { $0.index == message.index }) {
|
||||||
|
strongSelf.resultSelected?(strongSelf.messages.count - index - 1)
|
||||||
|
}
|
||||||
|
strongSelf.listNode.clearHighlightAnimated(true)
|
||||||
|
}
|
||||||
|
}, groupSelected: { _ in
|
||||||
|
}, addContact: { [weak self] phoneNumber in
|
||||||
|
}, setPeerIdWithRevealedOptions: { _, _ in
|
||||||
|
}, setItemPinned: { _, _ in
|
||||||
|
}, setPeerMuted: { _, _ in
|
||||||
|
}, deletePeer: { _ in
|
||||||
|
}, updatePeerGrouping: { _, _ in
|
||||||
|
}, togglePeerMarkedUnread: { _, _ in
|
||||||
|
}, toggleArchivedFolderHiddenByDefault: {
|
||||||
|
}, activateChatPreview: { _, _, _ in
|
||||||
|
})
|
||||||
|
self.interaction = interaction
|
||||||
|
|
||||||
|
let previousEntries = Atomic<[ChatListSearchEntry]?>(value: nil)
|
||||||
|
self.disposable.set((signal
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] entries in
|
||||||
|
if let strongSelf = self {
|
||||||
|
let previousEntries = previousEntries.swap(entries)
|
||||||
|
|
||||||
|
let firstTime = previousEntries == nil
|
||||||
|
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, context: context, interaction: interaction)
|
||||||
|
strongSelf.enqueueTransition(transition, firstTime: firstTime)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePresentationData(_ presentationData: PresentationData) {
|
||||||
|
let previousTheme = self.presentationData.theme
|
||||||
|
self.presentationData = presentationData
|
||||||
|
self.presentationDataPromise.set(.single(ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateIn() {
|
||||||
|
self.layer.animatePosition(from: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), to: self.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateOut(completion: (() -> Void)? = nil) {
|
||||||
|
let internalCompletion: () -> Void = { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.dismiss?()
|
||||||
|
}
|
||||||
|
completion?()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.layer.animatePosition(from: self.layer.position, to: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { _ in
|
||||||
|
internalCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private func enqueueTransition(_ transition: ChatListSearchContainerTransition, firstTime: Bool) {
|
||||||
|
self.enqueuedTransitions.append((transition, firstTime))
|
||||||
|
|
||||||
|
if self.validLayout != nil {
|
||||||
|
while !self.enqueuedTransitions.isEmpty {
|
||||||
|
self.dequeueTransition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dequeueTransition() {
|
||||||
|
if let (transition, _) = self.enqueuedTransitions.first {
|
||||||
|
self.enqueuedTransitions.remove(at: 0)
|
||||||
|
|
||||||
|
var options = ListViewDeleteAndInsertOptions()
|
||||||
|
options.insert(.PreferSynchronousDrawing)
|
||||||
|
options.insert(.PreferSynchronousResourceLoading)
|
||||||
|
|
||||||
|
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
|
let hadValidLayout = self.validLayout != nil
|
||||||
|
self.validLayout = (layout, navigationBarHeight)
|
||||||
|
|
||||||
|
let topInset = navigationBarHeight
|
||||||
|
|
||||||
|
var duration: Double = 0.0
|
||||||
|
var curve: UInt = 0
|
||||||
|
switch transition {
|
||||||
|
case .immediate:
|
||||||
|
break
|
||||||
|
case let .animated(animationDuration, animationCurve):
|
||||||
|
duration = animationDuration
|
||||||
|
switch animationCurve {
|
||||||
|
case .easeInOut, .custom:
|
||||||
|
break
|
||||||
|
case .spring:
|
||||||
|
curve = 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let listViewCurve: ListViewAnimationCurve
|
||||||
|
if curve == 7 {
|
||||||
|
listViewCurve = .Spring(duration: duration)
|
||||||
|
} else {
|
||||||
|
listViewCurve = .Default(duration: duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.listNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
|
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: navigationBarHeight, left: layout.safeInsets.left, bottom: layout.insets(options: [.input]).bottom, right: layout.safeInsets.right), duration: duration, curve: listViewCurve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||||
|
|
||||||
|
if !hadValidLayout {
|
||||||
|
while !self.enqueuedTransitions.isEmpty {
|
||||||
|
self.dequeueTransition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,94 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import Postbox
|
||||||
|
import SwiftSignalKit
|
||||||
|
import TelegramCore
|
||||||
|
import TelegramPresentationData
|
||||||
|
import AccountContext
|
||||||
|
|
||||||
|
final class ChatSearchResultsController: ViewController {
|
||||||
|
private var controllerNode: ChatSearchResultsControllerNode {
|
||||||
|
return self.displayNode as! ChatSearchResultsControllerNode
|
||||||
|
}
|
||||||
|
|
||||||
|
private let context: AccountContext
|
||||||
|
private var presentationData: PresentationData
|
||||||
|
private let searchQuery: String
|
||||||
|
private let messages: [Message]
|
||||||
|
|
||||||
|
private var didPlayPresentationAnimation = false
|
||||||
|
|
||||||
|
private let navigateToMessageIndex: (Int) -> Void
|
||||||
|
|
||||||
|
private var presentationDataDisposable: Disposable?
|
||||||
|
|
||||||
|
init(context: AccountContext, searchQuery: String, messages: [Message], navigateToMessageIndex: @escaping (Int) -> Void) {
|
||||||
|
self.context = context
|
||||||
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
self.searchQuery = searchQuery
|
||||||
|
self.messages = messages
|
||||||
|
self.navigateToMessageIndex = navigateToMessageIndex
|
||||||
|
|
||||||
|
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings))
|
||||||
|
|
||||||
|
self.isModalWhenInOverlay = true
|
||||||
|
|
||||||
|
self.presentationDataDisposable = (context.sharedContext.presentationData
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.presentationData = presentationData
|
||||||
|
strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: presentationData.theme, presentationStrings: presentationData.strings))
|
||||||
|
strongSelf.controllerNode.updatePresentationData(presentationData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||||
|
|
||||||
|
self.title = searchQuery
|
||||||
|
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(donePressed))
|
||||||
|
}
|
||||||
|
|
||||||
|
required init(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.presentationDataDisposable?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func loadDisplayNode() {
|
||||||
|
self.displayNode = ChatSearchResultsControllerNode(context: self.context, messages: self.messages)
|
||||||
|
self.controllerNode.resultSelected = { [weak self] messageIndex in
|
||||||
|
self?.navigateToMessageIndex(messageIndex)
|
||||||
|
self?.dismiss()
|
||||||
|
}
|
||||||
|
self.controllerNode.dismiss = { [weak self] in
|
||||||
|
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func viewDidAppear(_ animated: Bool) {
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
|
if !self.didPlayPresentationAnimation {
|
||||||
|
self.didPlayPresentationAnimation = true
|
||||||
|
self.controllerNode.animateIn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||||
|
self.controllerNode.animateOut(completion: completion)
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
|
super.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
|
||||||
|
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func donePressed() {
|
||||||
|
self.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -224,7 +224,6 @@ final class EmojisChatInputContextPanelNode: ChatInputContextPanelNode {
|
|||||||
let hadValidLayout = self.validLayout != nil
|
let hadValidLayout = self.validLayout != nil
|
||||||
self.validLayout = (size, leftInset, rightInset)
|
self.validLayout = (size, leftInset, rightInset)
|
||||||
|
|
||||||
|
|
||||||
let sideInsets: CGFloat = 10.0 + leftInset
|
let sideInsets: CGFloat = 10.0 + leftInset
|
||||||
let contentWidth = min(size.width - sideInsets - sideInsets, max(24.0, CGFloat(self.currentEntries?.count ?? 0) * 45.0))
|
let contentWidth = min(size.width - sideInsets - sideInsets, max(24.0, CGFloat(self.currentEntries?.count ?? 0) * 45.0))
|
||||||
|
|
||||||
|
|||||||
@ -450,6 +450,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
|
|||||||
}, beginMessageSearch: { _, _ in
|
}, beginMessageSearch: { _, _ in
|
||||||
}, dismissMessageSearch: {
|
}, dismissMessageSearch: {
|
||||||
}, updateMessageSearch: { _ in
|
}, updateMessageSearch: { _ in
|
||||||
|
}, openSearchResults: {
|
||||||
}, navigateMessageSearch: { _ in
|
}, navigateMessageSearch: { _ in
|
||||||
}, openCalendarSearch: {
|
}, openCalendarSearch: {
|
||||||
}, toggleMembersSearch: { _ in
|
}, toggleMembersSearch: { _ in
|
||||||
|
|||||||
Binary file not shown.
@ -344,8 +344,6 @@ class WebSearchControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if themeUpdated {
|
if themeUpdated {
|
||||||
self.backgroundColor = self.theme.chatList.backgroundColor
|
|
||||||
|
|
||||||
self.segmentedBackgroundNode.backgroundColor = self.theme.rootController.navigationBar.backgroundColor
|
self.segmentedBackgroundNode.backgroundColor = self.theme.rootController.navigationBar.backgroundColor
|
||||||
self.segmentedSeparatorNode.backgroundColor = self.theme.rootController.navigationBar.separatorColor
|
self.segmentedSeparatorNode.backgroundColor = self.theme.rootController.navigationBar.separatorColor
|
||||||
self.segmentedControlNode.updateTheme(SegmentedControlTheme(theme: self.theme))
|
self.segmentedControlNode.updateTheme(SegmentedControlTheme(theme: self.theme))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user