Message search improvements

This commit is contained in:
Ilya Laktyushin 2019-09-14 01:26:20 +03:00
parent 3d4be40ddb
commit 91b9c28670
36 changed files with 3436 additions and 2965 deletions

View File

@ -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.";

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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?

View File

@ -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

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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?() {

View File

@ -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

View File

@ -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 {

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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
}
}

View File

@ -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

View File

@ -61,6 +61,7 @@ final class ChatRecentActionsController: TelegramBaseController {
}, beginMessageSearch: { _, _ in
}, dismissMessageSearch: {
}, updateMessageSearch: { _ in
}, openSearchResults: {
}, navigateMessageSearch: { _ in
}, openCalendarSearch: {
}, toggleMembersSearch: { _ in

View File

@ -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)

View File

@ -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()
}
}
}
}

View File

@ -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()
}
}

View File

@ -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()

View File

@ -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))

View File

@ -450,6 +450,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
}, beginMessageSearch: { _, _ in
}, dismissMessageSearch: {
}, updateMessageSearch: { _ in
}, openSearchResults: {
}, navigateMessageSearch: { _ in
}, openCalendarSearch: {
}, toggleMembersSearch: { _ in

View File

@ -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))