mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-09-17 09:10:30 +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.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() {
|
||||
let controller = self.context.sharedContext.makeComposeController(context: self.context)
|
||||
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? {
|
||||
@ -1087,7 +1086,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
var sourceRect = selectedNode.view.superview!.convert(selectedNode.frame, to: sourceView)
|
||||
sourceRect.size.height -= UIScreenPixel
|
||||
switch item.content {
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, _):
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, _, _):
|
||||
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))
|
||||
chatController.canReadHistory.set(false)
|
||||
|
@ -437,7 +437,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
}
|
||||
})
|
||||
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):
|
||||
return ContactsAddItem(theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: {
|
||||
interaction.addContact(phoneNumber)
|
||||
@ -544,6 +544,7 @@ public enum ChatListSearchContextActionSource {
|
||||
|
||||
public final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
private let context: AccountContext
|
||||
private var interaction: ChatListNodeInteraction?
|
||||
|
||||
private let recentListNode: ListView
|
||||
private let listNode: ListView
|
||||
@ -886,6 +887,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
gesture?.cancel()
|
||||
}
|
||||
})
|
||||
self.interaction = interaction
|
||||
|
||||
let previousRecentItems = Atomic<[ChatListRecentEntry]?>(value: nil)
|
||||
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() {
|
||||
super.didLoad()
|
||||
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
||||
@ -1030,13 +1039,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
self.cancel?()
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.updatedRecentPeersDisposable.dispose()
|
||||
self.recentDisposable.dispose()
|
||||
self.searchDisposable.dispose()
|
||||
self.presentationDataDisposable?.dispose()
|
||||
}
|
||||
|
||||
|
||||
private func updateTheme(theme: PresentationTheme) {
|
||||
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) {
|
||||
if text.isEmpty {
|
||||
self.searchQuery.set(.single(nil))
|
||||
} else {
|
||||
self.searchQuery.set(.single(text))
|
||||
}
|
||||
let searchQuery: String? = !text.isEmpty ? text : nil
|
||||
self.interaction?.searchTextHighightState = searchQuery
|
||||
self.searchQuery.set(.single(searchQuery))
|
||||
}
|
||||
|
||||
private func enqueueRecentTransition(_ transition: ChatListSearchContainerRecentTransition, firstTime: Bool) {
|
||||
@ -1124,7 +1125,6 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
let hadValidLayout = self.validLayout != nil
|
||||
self.validLayout = layout
|
||||
|
||||
|
||||
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)))
|
||||
|
||||
@ -1202,7 +1202,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
bounds = selectedItemNode.bounds
|
||||
}
|
||||
switch item.content {
|
||||
case let .peer(message, peer, _, _, _, _, _, _, _, _):
|
||||
case let .peer(message, peer, _, _, _, _, _, _, _, _, _):
|
||||
return (selectedItemNode.view, bounds, message?.id ?? peer.peerId)
|
||||
case let .groupReference(groupId, _, _, _, _):
|
||||
return (selectedItemNode.view, bounds, groupId)
|
||||
|
@ -18,12 +18,12 @@ import ChatListSearchItemNode
|
||||
import ContextUI
|
||||
|
||||
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)
|
||||
|
||||
public var chatLocation: ChatLocation? {
|
||||
switch self {
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, _):
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, _, _):
|
||||
return .peer(peer.peerId)
|
||||
case .groupReference:
|
||||
return nil
|
||||
@ -120,7 +120,7 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
|
||||
|
||||
public func selected(listView: ListView) {
|
||||
switch self.content {
|
||||
case let .peer(message, peer, _, _, _, _, _, _, isAd, _):
|
||||
case let .peer(message, peer, _, _, _, _, _, _, isAd, _, _):
|
||||
if let message = message, let peer = peer.peer {
|
||||
self.interaction.messageSelected(peer, message, isAd)
|
||||
} 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 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 {
|
||||
var item: ChatListItem?
|
||||
|
||||
@ -319,6 +341,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
private var peerPresenceManager: PeerPresenceStatusManager?
|
||||
|
||||
private var cachedChatListSearchResult: CachedChatListSearchResult?
|
||||
|
||||
var layoutParams: (ChatListItem, first: Bool, last: Bool, firstWithHeader: Bool, nextIsPinned: Bool, ListViewItemLayoutParams, countersSize: CGFloat)?
|
||||
private var contentImageMedia: Media?
|
||||
|
||||
@ -500,8 +524,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
var peer: Peer?
|
||||
switch item.content {
|
||||
case let .peer(_, peerValue, _, _, _, _, _, _, _, _):
|
||||
peer = peerValue.chatMainPeer
|
||||
case let .peer(message, peerValue, _, _, _, _, _, _, _, _, displayAsMessage):
|
||||
if displayAsMessage, let author = message?.author as? TelegramUser {
|
||||
peer = author
|
||||
} else {
|
||||
peer = peerValue.chatMainPeer
|
||||
}
|
||||
case let .groupReference(groupReference):
|
||||
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: {
|
||||
@ -614,6 +642,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let currentItem = self.layoutParams?.0
|
||||
let currentContentImageMedia = self.contentImageMedia
|
||||
let currentChatListSearchResult = self.cachedChatListSearchResult
|
||||
|
||||
return { item, params, first, last, firstWithHeader, nextIsPinned in
|
||||
let account = item.context.account
|
||||
@ -632,11 +661,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let inputActivities: [(Peer, PeerInputActivity)]?
|
||||
let isPeerGroup: Bool
|
||||
let isAd: Bool
|
||||
let displayAsMessage: Bool
|
||||
|
||||
var groupHiddenByDefault = false
|
||||
|
||||
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
|
||||
contentPeer = .chat(peerValue)
|
||||
combinedReadState = combinedReadStateValue
|
||||
@ -656,6 +686,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
inputActivities = inputActivitiesValue
|
||||
isPeerGroup = false
|
||||
isAd = isAdValue
|
||||
displayAsMessage = displayAsMessageValue
|
||||
case let .groupReference(_, peers, messageValue, unreadState, hiddenByDefault):
|
||||
if let _ = messageValue, !peers.isEmpty {
|
||||
contentPeer = .chat(peers[0].peer)
|
||||
@ -674,6 +705,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
unreadCount = (allCount, allCount != 0, true, nil)
|
||||
peerPresence = nil
|
||||
isAd = false
|
||||
displayAsMessage = false
|
||||
}
|
||||
|
||||
if let messageValue = message {
|
||||
@ -766,6 +798,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
var contentImageMedia: Media?
|
||||
var chatListSearchResult: CachedChatListSearchResult?
|
||||
|
||||
switch contentData {
|
||||
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)
|
||||
} else if let message = message {
|
||||
let composedString: NSMutableAttributedString
|
||||
if let inlineAuthorPrefix = inlineAuthorPrefix {
|
||||
let composedString = NSMutableAttributedString()
|
||||
composedString = NSMutableAttributedString()
|
||||
composedString.append(NSAttributedString(string: "\(inlineAuthorPrefix): ", font: textFont, textColor: theme.titleColor))
|
||||
composedString.append(NSAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor))
|
||||
attributedText = composedString
|
||||
} 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?
|
||||
if case .groupReference = item.content {
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -864,7 +945,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
switch contentData {
|
||||
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)
|
||||
} else if itemPeer.chatMainPeer?.id == item.context.account.peerId {
|
||||
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: titleFont, textColor: theme.titleColor)
|
||||
@ -1057,8 +1140,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let peerRevealOptions: [ItemListRevealOption]
|
||||
let peerLeftRevealOptions: [ItemListRevealOption]
|
||||
switch item.content {
|
||||
case let .peer(_, renderedPeer, _, _, presence, _ ,_ ,_, _, _):
|
||||
if let peer = renderedPeer.peer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId {
|
||||
case let .peer(_, renderedPeer, _, _, presence, _ ,_ ,_, _, _, displayAsMessage):
|
||||
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)
|
||||
if case .online = relativeStatus {
|
||||
online = true
|
||||
@ -1124,6 +1207,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
if let strongSelf = self {
|
||||
strongSelf.layoutParams = (item, first, last, firstWithHeader, nextIsPinned, params, countersSize)
|
||||
strongSelf.contentImageMedia = contentImageMedia
|
||||
strongSelf.cachedChatListSearchResult = chatListSearchResult
|
||||
|
||||
strongSelf.contextContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
|
||||
|
@ -57,6 +57,7 @@ public final class ChatListNodeInteraction {
|
||||
let toggleArchivedFolderHiddenByDefault: () -> Void
|
||||
let activateChatPreview: (ChatListItem, ASDisplayNode, ContextGesture?) -> Void
|
||||
|
||||
var searchTextHighightState: String?
|
||||
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) {
|
||||
@ -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):
|
||||
switch mode {
|
||||
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):
|
||||
let itemPeer = peer.chatMainPeer
|
||||
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):
|
||||
switch mode {
|
||||
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):
|
||||
let itemPeer = peer.chatMainPeer
|
||||
var chatPeer: Peer?
|
||||
|
@ -7,16 +7,20 @@ public final class TabBarControllerTheme {
|
||||
public let backgroundColor: UIColor
|
||||
public let tabBarBackgroundColor: UIColor
|
||||
public let tabBarSeparatorColor: UIColor
|
||||
public let tabBarIconColor: UIColor
|
||||
public let tabBarSelectedIconColor: UIColor
|
||||
public let tabBarTextColor: UIColor
|
||||
public let tabBarSelectedTextColor: UIColor
|
||||
public let tabBarBadgeBackgroundColor: UIColor
|
||||
public let tabBarBadgeStrokeColor: 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.tabBarBackgroundColor = tabBarBackgroundColor
|
||||
self.tabBarSeparatorColor = tabBarSeparatorColor
|
||||
self.tabBarIconColor = tabBarIconColor
|
||||
self.tabBarSelectedIconColor = tabBarSelectedIconColor
|
||||
self.tabBarTextColor = tabBarTextColor
|
||||
self.tabBarSelectedTextColor = tabBarSelectedTextColor
|
||||
self.tabBarBadgeBackgroundColor = tabBarBadgeBackgroundColor
|
||||
|
@ -452,20 +452,42 @@ public final class TextNodeLayout: NSObject {
|
||||
guard let attributedString = self.attributedString else {
|
||||
return []
|
||||
}
|
||||
|
||||
var ranges: [Range<String.Index>] = []
|
||||
var searchRange = attributedString.string.startIndex ..< attributedString.string.endIndex
|
||||
while searchRange.lowerBound != attributedString.string.endIndex {
|
||||
if let range = attributedString.string.range(of: text, options: [.caseInsensitive, .diacriticInsensitive], range: searchRange, locale: nil) {
|
||||
ranges.append(range)
|
||||
searchRange = range.upperBound ..< attributedString.string.endIndex
|
||||
} else {
|
||||
break
|
||||
let queryWords = text.split { !$0.isLetter }.filter { !$0.isEmpty }.map { $0.lowercased() }
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result: [[CGRect]] = []
|
||||
for stringRange in ranges {
|
||||
var rects: [CGRect] = []
|
||||
let range = NSRange(stringRange, in: attributedString.string)
|
||||
let range = NSRange(stringRange, in: text)
|
||||
for line in self.lines {
|
||||
let lineRange = NSIntersectionRange(range, line.range)
|
||||
if lineRange.length != 0 {
|
||||
|
@ -516,8 +516,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
|
||||
if isAnimated || disablePlayerControls {
|
||||
strongSelf.footerContentNode.content = .info
|
||||
}
|
||||
else if isPaused {
|
||||
} else if isPaused {
|
||||
if hasStarted || strongSelf.didPause {
|
||||
strongSelf.footerContentNode.content = .playback(paused: true, seekable: seekable)
|
||||
} else if let fetchStatus = fetchStatus, !strongSelf.requiresDownload {
|
||||
|
@ -2272,8 +2272,7 @@ final class SecureIdDocumentFormControllerNode: FormControllerNode<SecureIdDocum
|
||||
} else if useNext {
|
||||
if case .deleteDocument = itemEntry {
|
||||
return false
|
||||
}
|
||||
else if let inputNode = itemNode as? FormControllerTextInputItemNode {
|
||||
} else if let inputNode = itemNode as? FormControllerTextInputItemNode {
|
||||
inputNode.activate()
|
||||
return false
|
||||
} else if let actionNode = itemNode as? FormControllerDetailActionItemNode {
|
||||
|
@ -276,18 +276,18 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
|
||||
case let .sizeToFit(maximumWidth, minimumWidth):
|
||||
width = max(minimumWidth, min(maximumWidth, calculatedWidth))
|
||||
}
|
||||
|
||||
let selectedIndex: Int
|
||||
if let gestureSelectedIndex = self.gestureSelectedIndex {
|
||||
selectedIndex = gestureSelectedIndex
|
||||
} else {
|
||||
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)
|
||||
|
||||
let selectedIndex: Int
|
||||
if let gestureSelectedIndex = self.gestureSelectedIndex {
|
||||
selectedIndex = gestureSelectedIndex
|
||||
} else {
|
||||
selectedIndex = self.selectedIndex
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
@ -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))
|
||||
|
||||
let dividerAlpha: CGFloat
|
||||
if (self.selectedIndex - 1 ... self.selectedIndex).contains(i) {
|
||||
if (selectedIndex - 1 ... selectedIndex).contains(i) {
|
||||
dividerAlpha = 0.0
|
||||
} else {
|
||||
dividerAlpha = 1.0
|
||||
|
@ -236,17 +236,17 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
let timestamp = self.referenceTimestamp
|
||||
|
||||
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 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
|
||||
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
|
||||
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)
|
||||
if let chatNodes = self.chatNodes {
|
||||
|
@ -230,7 +230,6 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
|
||||
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
|
||||
arguments.updateTheme(theme)
|
||||
}, longTapped: { _ in
|
||||
}, contextAction: nil)
|
||||
}
|
||||
}
|
||||
|
@ -344,24 +344,24 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
let timestamp = self.referenceTimestamp
|
||||
|
||||
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 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
|
||||
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
|
||||
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
|
||||
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
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
|
@ -67,11 +67,10 @@ private final class ThemeSettingsControllerArguments {
|
||||
let toggleLargeEmoji: (Bool) -> Void
|
||||
let disableAnimations: (Bool) -> Void
|
||||
let selectAppIcon: (String) -> Void
|
||||
let presentThemeMenu: (PresentationThemeReference, Bool) -> Void
|
||||
let editTheme: (PresentationCloudTheme) -> 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.updateTheme = updateTheme
|
||||
self.selectFontSize = selectFontSize
|
||||
@ -82,7 +81,6 @@ private final class ThemeSettingsControllerArguments {
|
||||
self.toggleLargeEmoji = toggleLargeEmoji
|
||||
self.disableAnimations = disableAnimations
|
||||
self.selectAppIcon = selectAppIcon
|
||||
self.presentThemeMenu = presentThemeMenu
|
||||
self.editTheme = editTheme
|
||||
self.contextAction = contextAction
|
||||
}
|
||||
@ -323,8 +321,6 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
arguments.updateTheme(theme)
|
||||
}
|
||||
}, longTapped: { theme in
|
||||
//arguments.presentThemeMenu(theme, theme.index == currentTheme.index)
|
||||
}, contextAction: { theme, node, gesture in
|
||||
arguments.contextAction(theme.index == currentTheme.index, theme, node, gesture)
|
||||
})
|
||||
@ -460,66 +456,6 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
currentAppIconName.set(name)
|
||||
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
|
||||
let controller = editThemeController(context: context, mode: .edit(theme), navigateToChat: { peerId in
|
||||
if let navigationController = getNavigationControllerImpl?() {
|
||||
|
@ -88,11 +88,10 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
|
||||
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||
let currentTheme: PresentationThemeReference
|
||||
let updatedTheme: (PresentationThemeReference) -> Void
|
||||
let longTapped: (PresentationThemeReference) -> Void
|
||||
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
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.theme = theme
|
||||
self.strings = strings
|
||||
@ -100,7 +99,6 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
|
||||
self.themeSpecificAccentColors = themeSpecificAccentColors
|
||||
self.currentTheme = currentTheme
|
||||
self.updatedTheme = updatedTheme
|
||||
self.longTapped = longTapped
|
||||
self.contextAction = contextAction
|
||||
self.tag = tag
|
||||
self.sectionId = sectionId
|
||||
@ -146,7 +144,6 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
|
||||
private let overlayNode: ASImageNode
|
||||
private let textNode: ASTextNode
|
||||
private var action: (() -> Void)?
|
||||
private var longTapAction: (() -> Void)?
|
||||
private var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||
|
||||
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!
|
||||
if case let .cloud(theme) = theme, theme.theme.file == nil {
|
||||
if updatedTheme || accentColor != self.accentColor {
|
||||
@ -208,12 +205,7 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
|
||||
self.selected = selected
|
||||
}
|
||||
self.textNode.attributedText = title
|
||||
self.action = {
|
||||
action()
|
||||
}
|
||||
self.longTapAction = {
|
||||
longTapAction()
|
||||
}
|
||||
self.action = action
|
||||
self.contextAction = contextAction
|
||||
self.containerNode.isGestureEnabled = !selected
|
||||
}
|
||||
@ -236,8 +228,6 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
|
||||
switch gesture {
|
||||
case .tap:
|
||||
self.action?()
|
||||
case .longTap:
|
||||
self.longTapAction?()
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -399,8 +389,6 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
if let imageNode = imageNode {
|
||||
self?.scrollToNode(imageNode, animated: true)
|
||||
}
|
||||
}, longTapAction: {
|
||||
item.longTapped(theme)
|
||||
}, contextAction: item.contextAction.flatMap {
|
||||
contextAction in
|
||||
return { node, gesture in
|
||||
|
@ -297,8 +297,7 @@ public final class ShareController: ViewController {
|
||||
self?.controllerNode.cancel?()
|
||||
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 {
|
||||
self.defaultAction = ShareControllerAction(title: self.presentationData.strings.ShareMenu_CopyShareLink, action: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
|
@ -131,6 +131,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
||||
case archiveIntroDismissed = 11
|
||||
case callsTabTip = 12
|
||||
case cellularDataPermissionWarning = 13
|
||||
case chatMessageSearchResultsTip = 14
|
||||
|
||||
var key: ValueBoxKey {
|
||||
let v = ValueBoxKey(length: 4)
|
||||
@ -228,6 +229,10 @@ private struct ApplicationSpecificNoticeKeys {
|
||||
static func callsTabTip() -> NoticeEntryKey {
|
||||
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 {
|
||||
@ -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> {
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import Display
|
||||
public extension TabBarControllerTheme {
|
||||
convenience init(rootControllerTheme: PresentationTheme) {
|
||||
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),
|
||||
authorNameColor: UIColor(rgb: 0xffffff),
|
||||
messageTextColor: UIColor(rgb: 0x8e8e92),
|
||||
messageHighlightedTextColor: UIColor(rgb: 0xffffff),
|
||||
messageDraftTextColor: UIColor(rgb: 0xdd4b39),
|
||||
checkmarkColor: accentColor,
|
||||
pendingIndicatorColor: UIColor(rgb: 0xffffff),
|
||||
|
@ -163,6 +163,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
||||
dateTextColor: mainSecondaryTextColor.withAlphaComponent(0.5),
|
||||
authorNameColor: UIColor(rgb: 0xffffff),
|
||||
messageTextColor: mainSecondaryTextColor.withAlphaComponent(0.5),
|
||||
messageHighlightedTextColor: UIColor(rgb: 0xffffff),
|
||||
messageDraftTextColor: UIColor(rgb: 0xdd4b39),
|
||||
checkmarkColor: accentColor,
|
||||
pendingIndicatorColor: mainSecondaryTextColor.withAlphaComponent(0.4),
|
||||
|
@ -170,6 +170,7 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
|
||||
dateTextColor: UIColor(rgb: 0x8e8e93),
|
||||
authorNameColor: .black,
|
||||
messageTextColor: UIColor(rgb: 0x8e8e93),
|
||||
messageHighlightedTextColor: .black,
|
||||
messageDraftTextColor: UIColor(rgb: 0xdd4b39),
|
||||
checkmarkColor: day ? accentColor : UIColor(rgb: 0x21c004),
|
||||
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 authorNameColor: UIColor
|
||||
public let messageTextColor: UIColor
|
||||
public let messageHighlightedTextColor: UIColor
|
||||
public let messageDraftTextColor: UIColor
|
||||
public let checkmarkColor: UIColor
|
||||
public let pendingIndicatorColor: UIColor
|
||||
@ -408,7 +409,7 @@ public final class PresentationThemeChatList {
|
||||
public let unpinnedArchiveAvatarColor: PresentationThemeArchiveAvatarColors
|
||||
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.itemSeparatorColor = itemSeparatorColor
|
||||
self.itemBackgroundColor = itemBackgroundColor
|
||||
@ -420,6 +421,7 @@ public final class PresentationThemeChatList {
|
||||
self.dateTextColor = dateTextColor
|
||||
self.authorNameColor = authorNameColor
|
||||
self.messageTextColor = messageTextColor
|
||||
self.messageHighlightedTextColor = messageHighlightedTextColor
|
||||
self.messageDraftTextColor = messageDraftTextColor
|
||||
self.checkmarkColor = checkmarkColor
|
||||
self.pendingIndicatorColor = pendingIndicatorColor
|
||||
|
@ -760,6 +760,7 @@ extension PresentationThemeChatList: Codable {
|
||||
case dateText
|
||||
case authorName
|
||||
case messageText
|
||||
case messageHighlightedText
|
||||
case messageDraftText
|
||||
case checkmark
|
||||
case pendingIndicator
|
||||
@ -796,6 +797,7 @@ extension PresentationThemeChatList: Codable {
|
||||
dateTextColor: try decodeColor(values, .dateText),
|
||||
authorNameColor: try decodeColor(values, .authorName),
|
||||
messageTextColor: try decodeColor(values, .messageText),
|
||||
messageHighlightedTextColor: try decodeColor(values, .messageHighlightedText),
|
||||
messageDraftTextColor: try decodeColor(values, .messageDraftText),
|
||||
checkmarkColor: try decodeColor(values, .checkmark),
|
||||
pendingIndicatorColor: try decodeColor(values, .pendingIndicator),
|
||||
@ -832,6 +834,7 @@ extension PresentationThemeChatList: Codable {
|
||||
try encodeColor(&values, self.dateTextColor, .dateText)
|
||||
try encodeColor(&values, self.authorNameColor, .authorName)
|
||||
try encodeColor(&values, self.messageTextColor, .messageText)
|
||||
try encodeColor(&values, self.messageHighlightedTextColor, .messageHighlightedText)
|
||||
try encodeColor(&values, self.messageDraftTextColor, .messageDraftText)
|
||||
try encodeColor(&values, self.checkmarkColor, .checkmark)
|
||||
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 unblockingPeer = 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 var preloadHistoryPeerId: PeerId?
|
||||
@ -3083,7 +3084,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
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))
|
||||
var entitiesAttribute: TextEntitiesMessageAttribute?
|
||||
if !entities.isEmpty {
|
||||
@ -3173,6 +3174,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
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
|
||||
if let strongSelf = self {
|
||||
var navigateIndex: MessageIndex?
|
||||
@ -3189,6 +3203,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if index != resultsState.messageIndices.count - 1 {
|
||||
updatedIndex = index + 1
|
||||
}
|
||||
case let .index(index):
|
||||
if index >= 0 && index < resultsState.messageIndices.count {
|
||||
updatedIndex = index
|
||||
}
|
||||
}
|
||||
if let updatedIndex = updatedIndex {
|
||||
navigateIndex = resultsState.messageIndices[updatedIndex]
|
||||
@ -4771,9 +4789,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
private func updateItemNodesSearchTextHighlightStates() {
|
||||
if true {
|
||||
return
|
||||
}
|
||||
var searchString: String?
|
||||
if let search = self.presentationInterfaceState.search, let resultsState = search.resultsState, !resultsState.messageIndices.isEmpty {
|
||||
searchString = search.query
|
||||
@ -5979,6 +5994,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if queryIsEmpty {
|
||||
self.searching.set(false)
|
||||
self.searchDisposable?.set(nil)
|
||||
self.searchResult.set(.single(nil))
|
||||
if let data = interfaceState.search {
|
||||
return interfaceState.updatedSearch(data.withUpdatedResultsState(nil))
|
||||
}
|
||||
@ -5991,8 +6007,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
searchDisposable = MetaDisposable()
|
||||
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())
|
||||
self.searchResult.set(search |> map(Optional.init))
|
||||
|
||||
searchDisposable.set((search
|
||||
|> deliverOnMainQueue).start(next: { [weak self] results, updatedState in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
@ -168,8 +168,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, webEmbedType(content: content).supportsSeeking {
|
||||
isSeekableWebMedia = true
|
||||
}
|
||||
else if media is TelegramMediaUnsupported {
|
||||
} else if media is TelegramMediaUnsupported {
|
||||
isUnsupportedMedia = true
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ final class ChatPanelInterfaceInteractionStatuses {
|
||||
enum ChatPanelSearchNavigationAction {
|
||||
case earlier
|
||||
case later
|
||||
case index(Int)
|
||||
}
|
||||
|
||||
enum ChatPanelRestrictionInfoSubject {
|
||||
@ -64,6 +65,7 @@ final class ChatPanelInterfaceInteraction {
|
||||
let dismissMessageSearch: () -> Void
|
||||
let updateMessageSearch: (String) -> Void
|
||||
let navigateMessageSearch: (ChatPanelSearchNavigationAction) -> Void
|
||||
let openSearchResults: () -> Void
|
||||
let openCalendarSearch: () -> Void
|
||||
let toggleMembersSearch: (Bool) -> Void
|
||||
let navigateToMessage: (MessageId) -> Void
|
||||
@ -112,7 +114,7 @@ final class ChatPanelInterfaceInteraction {
|
||||
let openScheduledMessages: () -> Void
|
||||
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.setupEditMessage = setupEditMessage
|
||||
self.beginMessageSelection = beginMessageSelection
|
||||
@ -131,6 +133,7 @@ final class ChatPanelInterfaceInteraction {
|
||||
self.beginMessageSearch = beginMessageSearch
|
||||
self.dismissMessageSearch = dismissMessageSearch
|
||||
self.updateMessageSearch = updateMessageSearch
|
||||
self.openSearchResults = openSearchResults
|
||||
self.navigateMessageSearch = navigateMessageSearch
|
||||
self.openCalendarSearch = openCalendarSearch
|
||||
self.toggleMembersSearch = toggleMembersSearch
|
||||
|
@ -61,6 +61,7 @@ final class ChatRecentActionsController: TelegramBaseController {
|
||||
}, beginMessageSearch: { _, _ in
|
||||
}, dismissMessageSearch: {
|
||||
}, updateMessageSearch: { _ in
|
||||
}, openSearchResults: {
|
||||
}, navigateMessageSearch: { _ in
|
||||
}, openCalendarSearch: {
|
||||
}, toggleMembersSearch: { _ in
|
||||
|
@ -15,7 +15,8 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
||||
private let downButton: HighlightableButtonNode
|
||||
private let calendarButton: HighlightableButtonNode
|
||||
private let membersButton: HighlightableButtonNode
|
||||
private let resultsLabel: TextNode
|
||||
private let resultsButton: HighlightableButtonNode
|
||||
private let measureResultsLabel: TextNode
|
||||
private let activityIndicator: ActivityIndicator
|
||||
|
||||
private var presentationInterfaceState: ChatPresentationInterfaceState?
|
||||
@ -23,6 +24,8 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
||||
private let activityDisposable = MetaDisposable()
|
||||
private var displayActivity = false
|
||||
|
||||
private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, LayoutMetrics)?
|
||||
|
||||
override var interfaceInteraction: ChatPanelInterfaceInteraction? {
|
||||
didSet {
|
||||
if let statuses = self.interfaceInteraction?.statuses {
|
||||
@ -31,8 +34,8 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
||||
if let strongSelf = self, strongSelf.displayActivity != value {
|
||||
strongSelf.displayActivity = value
|
||||
strongSelf.activityIndicator.isHidden = !value
|
||||
if let interfaceState = strongSelf.presentationInterfaceState {
|
||||
strongSelf.calendarButton.isHidden = !((interfaceState.search?.query.isEmpty ?? true)) || strongSelf.displayActivity
|
||||
if let interfaceState = strongSelf.presentationInterfaceState, let validLayout = strongSelf.validLayout {
|
||||
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.calendarButton = HighlightableButtonNode()
|
||||
self.membersButton = HighlightableButtonNode()
|
||||
self.resultsLabel = TextNode()
|
||||
self.resultsLabel.isUserInteractionEnabled = false
|
||||
self.resultsLabel.displaysAsynchronously = false
|
||||
self.measureResultsLabel = TextNode()
|
||||
self.resultsButton = HighlightableButtonNode()
|
||||
self.activityIndicator = ActivityIndicator(type: .navigationAccent(theme))
|
||||
self.activityIndicator.isHidden = true
|
||||
|
||||
@ -61,13 +63,14 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
||||
self.addSubnode(self.downButton)
|
||||
self.addSubnode(self.calendarButton)
|
||||
self.addSubnode(self.membersButton)
|
||||
self.addSubnode(self.resultsLabel)
|
||||
self.addSubnode(self.resultsButton)
|
||||
self.addSubnode(self.activityIndicator)
|
||||
|
||||
self.upButton.addTarget(self, action: #selector(self.upPressed), forControlEvents: [.touchUpInside])
|
||||
self.downButton.addTarget(self, action: #selector(self.downPressed), forControlEvents: [.touchUpInside])
|
||||
self.calendarButton.addTarget(self, action: #selector(self.calendarPressed), forControlEvents: [.touchUpInside])
|
||||
self.membersButton.addTarget(self, action: #selector(self.membersPressed), forControlEvents: [.touchUpInside])
|
||||
self.resultsButton.addTarget(self, action: #selector(self.resultsPressed), forControlEvents: [.touchUpInside])
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -90,7 +93,13 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
||||
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 {
|
||||
self.validLayout = (width, leftInset, rightInset, maxHeight, metrics)
|
||||
|
||||
if self.presentationInterfaceState != interfaceState {
|
||||
let themeUpdated = self.presentationInterfaceState?.theme !== interfaceState.theme
|
||||
|
||||
@ -121,16 +130,16 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
||||
|
||||
var resultIndex: Int?
|
||||
var resultCount: Int?
|
||||
var resultsText: NSAttributedString?
|
||||
var resultsText: String?
|
||||
if let results = interfaceState.search?.resultsState {
|
||||
resultCount = results.messageIndices.count
|
||||
let displayTotalCount = results.completed ? results.messageIndices.count : Int(results.totalCount)
|
||||
if let currentId = results.currentId, let index = results.messageIndices.firstIndex(where: { $0.id == currentId }) {
|
||||
let adjustedIndex = results.messageIndices.count - 1 - 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 {
|
||||
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
|
||||
|
||||
let makeLabelLayout = TextNode.asyncLayout(self.resultsLabel)
|
||||
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()))
|
||||
let resultsEnabled = (resultCount ?? 0) > 5
|
||||
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()
|
||||
|
||||
var resultsOffset: CGFloat = 16.0
|
||||
if !self.calendarButton.isHidden {
|
||||
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))
|
||||
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()
|
||||
}
|
||||
}
|
@ -60,18 +60,18 @@ public class ComposeController: ViewController {
|
||||
}
|
||||
|
||||
self.presentationDataDisposable = (context.sharedContext.presentationData
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
if let strongSelf = self {
|
||||
let previousTheme = strongSelf.presentationData.theme
|
||||
let previousStrings = strongSelf.presentationData.strings
|
||||
|
||||
strongSelf.presentationData = presentationData
|
||||
|
||||
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
|
||||
strongSelf.updateThemeAndStrings()
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
if let strongSelf = self {
|
||||
let previousTheme = strongSelf.presentationData.theme
|
||||
let previousStrings = strongSelf.presentationData.strings
|
||||
|
||||
strongSelf.presentationData = presentationData
|
||||
|
||||
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
|
||||
strongSelf.updateThemeAndStrings()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
self.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search, activate: { [weak self] in
|
||||
self?.activateSearch()
|
||||
|
@ -224,7 +224,6 @@ final class EmojisChatInputContextPanelNode: ChatInputContextPanelNode {
|
||||
let hadValidLayout = self.validLayout != nil
|
||||
self.validLayout = (size, leftInset, rightInset)
|
||||
|
||||
|
||||
let sideInsets: CGFloat = 10.0 + leftInset
|
||||
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
|
||||
}, dismissMessageSearch: {
|
||||
}, updateMessageSearch: { _ in
|
||||
}, openSearchResults: {
|
||||
}, navigateMessageSearch: { _ in
|
||||
}, openCalendarSearch: {
|
||||
}, toggleMembersSearch: { _ in
|
||||
|
Binary file not shown.
@ -344,8 +344,6 @@ class WebSearchControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if themeUpdated {
|
||||
self.backgroundColor = self.theme.chatList.backgroundColor
|
||||
|
||||
self.segmentedBackgroundNode.backgroundColor = self.theme.rootController.navigationBar.backgroundColor
|
||||
self.segmentedSeparatorNode.backgroundColor = self.theme.rootController.navigationBar.separatorColor
|
||||
self.segmentedControlNode.updateTheme(SegmentedControlTheme(theme: self.theme))
|
||||
|
Loading…
x
Reference in New Issue
Block a user