Search filters improvements

This commit is contained in:
Ilya Laktyushin 2020-09-08 16:07:35 +03:00
parent 9ef16b1185
commit 4aada91da5
30 changed files with 614 additions and 238 deletions

View File

@ -0,0 +1,22 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "ChatInterfaceState",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
"//submodules/Display:Display#shared",
"//submodules/Postbox:Postbox#shared",
"//submodules/TelegramCore:TelegramCore#shared",
"//submodules/SyncCore:SyncCore#shared",
"//submodules/TextFormat:TextFormat",
"//submodules/AccountContext:AccountContext",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
],
)

View File

@ -0,0 +1,22 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ChatInterfaceState",
module_name = "ChatInterfaceState",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
"//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore",
"//submodules/SyncCore:SyncCore",
"//submodules/TextFormat:TextFormat",
"//submodules/AccountContext:AccountContext",
],
visibility = [
"//visibility:public",
],
)

View File

@ -6,18 +6,23 @@ import SyncCore
import TextFormat
import AccountContext
struct ChatInterfaceSelectionState: PostboxCoding, Equatable {
let selectedIds: Set<MessageId>
public enum ChatTextInputMediaRecordingButtonMode: Int32 {
case audio = 0
case video = 1
}
public struct ChatInterfaceSelectionState: PostboxCoding, Equatable {
public let selectedIds: Set<MessageId>
static func ==(lhs: ChatInterfaceSelectionState, rhs: ChatInterfaceSelectionState) -> Bool {
public static func ==(lhs: ChatInterfaceSelectionState, rhs: ChatInterfaceSelectionState) -> Bool {
return lhs.selectedIds == rhs.selectedIds
}
init(selectedIds: Set<MessageId>) {
public init(selectedIds: Set<MessageId>) {
self.selectedIds = selectedIds
}
init(decoder: PostboxDecoder) {
public init(decoder: PostboxDecoder) {
if let data = decoder.decodeBytesForKeyNoCopy("i") {
self.selectedIds = Set(MessageId.decodeArrayFromBuffer(data))
} else {
@ -25,27 +30,27 @@ struct ChatInterfaceSelectionState: PostboxCoding, Equatable {
}
}
func encode(_ encoder: PostboxEncoder) {
public func encode(_ encoder: PostboxEncoder) {
let buffer = WriteBuffer()
MessageId.encodeArrayToBuffer(Array(selectedIds), buffer: buffer)
encoder.encodeBytes(buffer, forKey: "i")
}
}
struct ChatEditMessageState: PostboxCoding, Equatable {
let messageId: MessageId
let inputState: ChatTextInputState
let disableUrlPreview: String?
let inputTextMaxLength: Int32?
public struct ChatEditMessageState: PostboxCoding, Equatable {
public let messageId: MessageId
public let inputState: ChatTextInputState
public let disableUrlPreview: String?
public let inputTextMaxLength: Int32?
init(messageId: MessageId, inputState: ChatTextInputState, disableUrlPreview: String?, inputTextMaxLength: Int32?) {
public init(messageId: MessageId, inputState: ChatTextInputState, disableUrlPreview: String?, inputTextMaxLength: Int32?) {
self.messageId = messageId
self.inputState = inputState
self.disableUrlPreview = disableUrlPreview
self.inputTextMaxLength = inputTextMaxLength
}
init(decoder: PostboxDecoder) {
public init(decoder: PostboxDecoder) {
self.messageId = MessageId(peerId: PeerId(decoder.decodeInt64ForKey("mp", orElse: 0)), namespace: decoder.decodeInt32ForKey("mn", orElse: 0), id: decoder.decodeInt32ForKey("mi", orElse: 0))
if let inputState = decoder.decodeObjectForKey("is", decoder: { return ChatTextInputState(decoder: $0) }) as? ChatTextInputState {
self.inputState = inputState
@ -56,7 +61,7 @@ struct ChatEditMessageState: PostboxCoding, Equatable {
self.inputTextMaxLength = decoder.decodeOptionalInt32ForKey("tl")
}
func encode(_ encoder: PostboxEncoder) {
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt64(self.messageId.peerId.toInt64(), forKey: "mp")
encoder.encodeInt32(self.messageId.namespace, forKey: "mn")
encoder.encodeInt32(self.messageId.id, forKey: "mi")
@ -73,31 +78,31 @@ struct ChatEditMessageState: PostboxCoding, Equatable {
}
}
static func ==(lhs: ChatEditMessageState, rhs: ChatEditMessageState) -> Bool {
public static func ==(lhs: ChatEditMessageState, rhs: ChatEditMessageState) -> Bool {
return lhs.messageId == rhs.messageId && lhs.inputState == rhs.inputState && lhs.disableUrlPreview == rhs.disableUrlPreview && lhs.inputTextMaxLength == rhs.inputTextMaxLength
}
func withUpdatedInputState(_ inputState: ChatTextInputState) -> ChatEditMessageState {
public func withUpdatedInputState(_ inputState: ChatTextInputState) -> ChatEditMessageState {
return ChatEditMessageState(messageId: self.messageId, inputState: inputState, disableUrlPreview: self.disableUrlPreview, inputTextMaxLength: self.inputTextMaxLength)
}
func withUpdatedDisableUrlPreview(_ disableUrlPreview: String?) -> ChatEditMessageState {
public func withUpdatedDisableUrlPreview(_ disableUrlPreview: String?) -> ChatEditMessageState {
return ChatEditMessageState(messageId: self.messageId, inputState: self.inputState, disableUrlPreview: disableUrlPreview, inputTextMaxLength: self.inputTextMaxLength)
}
}
struct ChatInterfaceMessageActionsState: PostboxCoding, Equatable {
var closedButtonKeyboardMessageId: MessageId?
var processedSetupReplyMessageId: MessageId?
var closedPinnedMessageId: MessageId?
var closedPeerSpecificPackSetup: Bool = false
var dismissedAddContactPhoneNumber: String?
public struct ChatInterfaceMessageActionsState: PostboxCoding, Equatable {
public var closedButtonKeyboardMessageId: MessageId?
public var processedSetupReplyMessageId: MessageId?
public var closedPinnedMessageId: MessageId?
public var closedPeerSpecificPackSetup: Bool = false
public var dismissedAddContactPhoneNumber: String?
var isEmpty: Bool {
public var isEmpty: Bool {
return self.closedButtonKeyboardMessageId == nil && self.processedSetupReplyMessageId == nil && self.closedPinnedMessageId == nil && self.closedPeerSpecificPackSetup == false && self.dismissedAddContactPhoneNumber == nil
}
init() {
public init() {
self.closedButtonKeyboardMessageId = nil
self.processedSetupReplyMessageId = nil
self.closedPinnedMessageId = nil
@ -105,7 +110,7 @@ struct ChatInterfaceMessageActionsState: PostboxCoding, Equatable {
self.dismissedAddContactPhoneNumber = nil
}
init(closedButtonKeyboardMessageId: MessageId?, processedSetupReplyMessageId: MessageId?, closedPinnedMessageId: MessageId?, closedPeerSpecificPackSetup: Bool, dismissedAddContactPhoneNumber: String?) {
public init(closedButtonKeyboardMessageId: MessageId?, processedSetupReplyMessageId: MessageId?, closedPinnedMessageId: MessageId?, closedPeerSpecificPackSetup: Bool, dismissedAddContactPhoneNumber: String?) {
self.closedButtonKeyboardMessageId = closedButtonKeyboardMessageId
self.processedSetupReplyMessageId = processedSetupReplyMessageId
self.closedPinnedMessageId = closedPinnedMessageId
@ -113,7 +118,7 @@ struct ChatInterfaceMessageActionsState: PostboxCoding, Equatable {
self.dismissedAddContactPhoneNumber = dismissedAddContactPhoneNumber
}
init(decoder: PostboxDecoder) {
public init(decoder: PostboxDecoder) {
if let closedMessageIdPeerId = decoder.decodeOptionalInt64ForKey("cb.p"), let closedMessageIdNamespace = decoder.decodeOptionalInt32ForKey("cb.n"), let closedMessageIdId = decoder.decodeOptionalInt32ForKey("cb.i") {
self.closedButtonKeyboardMessageId = MessageId(peerId: PeerId(closedMessageIdPeerId), namespace: closedMessageIdNamespace, id: closedMessageIdId)
} else {
@ -135,7 +140,7 @@ struct ChatInterfaceMessageActionsState: PostboxCoding, Equatable {
self.closedPeerSpecificPackSetup = decoder.decodeInt32ForKey("cpss", orElse: 0) != 0
}
func encode(_ encoder: PostboxEncoder) {
public func encode(_ encoder: PostboxEncoder) {
if let closedButtonKeyboardMessageId = self.closedButtonKeyboardMessageId {
encoder.encodeInt64(closedButtonKeyboardMessageId.peerId.toInt64(), forKey: "cb.p")
encoder.encodeInt32(closedButtonKeyboardMessageId.namespace, forKey: "cb.n")
@ -176,21 +181,21 @@ struct ChatInterfaceMessageActionsState: PostboxCoding, Equatable {
}
}
struct ChatInterfaceHistoryScrollState: PostboxCoding, Equatable {
let messageIndex: MessageIndex
let relativeOffset: Double
public struct ChatInterfaceHistoryScrollState: PostboxCoding, Equatable {
public let messageIndex: MessageIndex
public let relativeOffset: Double
init(messageIndex: MessageIndex, relativeOffset: Double) {
public init(messageIndex: MessageIndex, relativeOffset: Double) {
self.messageIndex = messageIndex
self.relativeOffset = relativeOffset
}
init(decoder: PostboxDecoder) {
public init(decoder: PostboxDecoder) {
self.messageIndex = MessageIndex(id: MessageId(peerId: PeerId(decoder.decodeInt64ForKey("m.p", orElse: 0)), namespace: decoder.decodeInt32ForKey("m.n", orElse: 0), id: decoder.decodeInt32ForKey("m.i", orElse: 0)), timestamp: decoder.decodeInt32ForKey("m.t", orElse: 0))
self.relativeOffset = decoder.decodeDoubleForKey("ro", orElse: 0.0)
}
func encode(_ encoder: PostboxEncoder) {
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.messageIndex.timestamp, forKey: "m.t")
encoder.encodeInt64(self.messageIndex.id.peerId.toInt64(), forKey: "m.p")
encoder.encodeInt32(self.messageIndex.id.namespace, forKey: "m.n")
@ -198,7 +203,7 @@ struct ChatInterfaceHistoryScrollState: PostboxCoding, Equatable {
encoder.encodeDouble(self.relativeOffset, forKey: "ro")
}
static func ==(lhs: ChatInterfaceHistoryScrollState, rhs: ChatInterfaceHistoryScrollState) -> Bool {
public static func ==(lhs: ChatInterfaceHistoryScrollState, rhs: ChatInterfaceHistoryScrollState) -> Bool {
if lhs.messageIndex != rhs.messageIndex {
return false
}
@ -210,18 +215,18 @@ struct ChatInterfaceHistoryScrollState: PostboxCoding, Equatable {
}
public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equatable {
let timestamp: Int32
let composeInputState: ChatTextInputState
let composeDisableUrlPreview: String?
let replyMessageId: MessageId?
let forwardMessageIds: [MessageId]?
let editMessage: ChatEditMessageState?
let selectionState: ChatInterfaceSelectionState?
let messageActionsState: ChatInterfaceMessageActionsState
let historyScrollState: ChatInterfaceHistoryScrollState?
let mediaRecordingMode: ChatTextInputMediaRecordingButtonMode
let silentPosting: Bool
let inputLanguage: String?
public let timestamp: Int32
public let composeInputState: ChatTextInputState
public let composeDisableUrlPreview: String?
public let replyMessageId: MessageId?
public let forwardMessageIds: [MessageId]?
public let editMessage: ChatEditMessageState?
public let selectionState: ChatInterfaceSelectionState?
public let messageActionsState: ChatInterfaceMessageActionsState
public let historyScrollState: ChatInterfaceHistoryScrollState?
public let mediaRecordingMode: ChatTextInputMediaRecordingButtonMode
public let silentPosting: Bool
public let inputLanguage: String?
public var associatedMessageIds: [MessageId] {
var ids: [MessageId] = []
@ -259,7 +264,7 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata
return result
}
var effectiveInputState: ChatTextInputState {
public var effectiveInputState: ChatTextInputState {
if let editMessage = self.editMessage {
return editMessage.inputState
} else {
@ -282,7 +287,7 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata
self.inputLanguage = nil
}
init(timestamp: Int32, composeInputState: ChatTextInputState, composeDisableUrlPreview: String?, replyMessageId: MessageId?, forwardMessageIds: [MessageId]?, editMessage: ChatEditMessageState?, selectionState: ChatInterfaceSelectionState?, messageActionsState: ChatInterfaceMessageActionsState, historyScrollState: ChatInterfaceHistoryScrollState?, mediaRecordingMode: ChatTextInputMediaRecordingButtonMode, silentPosting: Bool, inputLanguage: String?) {
public init(timestamp: Int32, composeInputState: ChatTextInputState, composeDisableUrlPreview: String?, replyMessageId: MessageId?, forwardMessageIds: [MessageId]?, editMessage: ChatEditMessageState?, selectionState: ChatInterfaceSelectionState?, messageActionsState: ChatInterfaceMessageActionsState, historyScrollState: ChatInterfaceHistoryScrollState?, mediaRecordingMode: ChatTextInputMediaRecordingButtonMode, silentPosting: Bool, inputLanguage: String?) {
self.timestamp = timestamp
self.composeInputState = composeInputState
self.composeDisableUrlPreview = composeDisableUrlPreview
@ -437,17 +442,17 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata
return lhs.composeInputState == rhs.composeInputState && lhs.replyMessageId == rhs.replyMessageId && lhs.selectionState == rhs.selectionState && lhs.editMessage == rhs.editMessage
}
func withUpdatedComposeInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState {
public func withUpdatedComposeInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState {
let updatedComposeInputState = inputState
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
func withUpdatedComposeDisableUrlPreview(_ disableUrlPreview: String?) -> ChatInterfaceState {
public func withUpdatedComposeDisableUrlPreview(_ disableUrlPreview: String?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: disableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
func withUpdatedEffectiveInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState {
public func withUpdatedEffectiveInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState {
var updatedEditMessage = self.editMessage
var updatedComposeInputState = self.composeInputState
if let editMessage = self.editMessage {
@ -459,15 +464,15 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: updatedEditMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
func withUpdatedReplyMessageId(_ replyMessageId: MessageId?) -> ChatInterfaceState {
public func withUpdatedReplyMessageId(_ replyMessageId: MessageId?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
func withUpdatedForwardMessageIds(_ forwardMessageIds: [MessageId]?) -> ChatInterfaceState {
public func withUpdatedForwardMessageIds(_ forwardMessageIds: [MessageId]?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
func withUpdatedSelectedMessages(_ messageIds: [MessageId]) -> ChatInterfaceState {
public func withUpdatedSelectedMessages(_ messageIds: [MessageId]) -> ChatInterfaceState {
var selectedIds = Set<MessageId>()
if let selectionState = self.selectionState {
selectedIds.formUnion(selectionState.selectedIds)
@ -478,7 +483,7 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
func withToggledSelectedMessages(_ messageIds: [MessageId], value: Bool) -> ChatInterfaceState {
public func withToggledSelectedMessages(_ messageIds: [MessageId], value: Bool) -> ChatInterfaceState {
var selectedIds = Set<MessageId>()
if let selectionState = self.selectionState {
selectedIds.formUnion(selectionState.selectedIds)
@ -493,27 +498,27 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
func withoutSelectionState() -> ChatInterfaceState {
public func withoutSelectionState() -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: nil, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
func withUpdatedTimestamp(_ timestamp: Int32) -> ChatInterfaceState {
public func withUpdatedTimestamp(_ timestamp: Int32) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
func withUpdatedEditMessage(_ editMessage: ChatEditMessageState?) -> ChatInterfaceState {
public func withUpdatedEditMessage(_ editMessage: ChatEditMessageState?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
func withUpdatedMessageActionsState(_ f: (ChatInterfaceMessageActionsState) -> ChatInterfaceMessageActionsState) -> ChatInterfaceState {
public func withUpdatedMessageActionsState(_ f: (ChatInterfaceMessageActionsState) -> ChatInterfaceMessageActionsState) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: f(self.messageActionsState), historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
func withUpdatedHistoryScrollState(_ historyScrollState: ChatInterfaceHistoryScrollState?) -> ChatInterfaceState {
public func withUpdatedHistoryScrollState(_ historyScrollState: ChatInterfaceHistoryScrollState?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}
func withUpdatedMediaRecordingMode(_ mediaRecordingMode: ChatTextInputMediaRecordingButtonMode) -> ChatInterfaceState {
public func withUpdatedMediaRecordingMode(_ mediaRecordingMode: ChatTextInputMediaRecordingButtonMode) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage)
}

View File

@ -53,6 +53,7 @@ static_library(
"//submodules/GalleryData:GalleryData",
"//submodules/InstantPageUI:InstantPageUI",
"//submodules/ListSectionHeaderNode:ListSectionHeaderNode",
"//submodules/ChatInterfaceState:ChatInterfaceState",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -52,6 +52,8 @@ swift_library(
"//submodules/GalleryData:GalleryData",
"//submodules/InstantPageUI:InstantPageUI",
"//submodules/ListSectionHeaderNode:ListSectionHeaderNode",
"//submodules/ChatInterfaceState:ChatInterfaceState",
"//submodules/ShareController:ShareController",
],
visibility = [
"//visibility:public",

View File

@ -27,6 +27,8 @@ import AnimatedStickerNode
import AppBundle
import GalleryData
import InstantPageUI
import ChatInterfaceState
import ShareController
private final class PassthroughContainerNode: ASDisplayNode {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
@ -595,20 +597,26 @@ public func chatListSearchContainerPreparedTransition(from fromEntries: [ChatLis
private struct ChatListSearchContainerNodeState: Equatable {
let peerIdWithRevealedOptions: PeerId?
let selectedMessageIds: Set<MessageId>?
init(peerIdWithRevealedOptions: PeerId? = nil) {
init(peerIdWithRevealedOptions: PeerId? = nil, selectedMessageIds: Set<MessageId>? = nil) {
self.peerIdWithRevealedOptions = peerIdWithRevealedOptions
self.selectedMessageIds = selectedMessageIds
}
static func ==(lhs: ChatListSearchContainerNodeState, rhs: ChatListSearchContainerNodeState) -> Bool {
if lhs.peerIdWithRevealedOptions != rhs.peerIdWithRevealedOptions {
if lhs.peerIdWithRevealedOptions != rhs.peerIdWithRevealedOptions || lhs.selectedMessageIds != rhs.selectedMessageIds {
return false
}
return true
}
func withUpdatedPeerIdWithRevealedOptions(_ peerIdWithRevealedOptions: PeerId?) -> ChatListSearchContainerNodeState {
return ChatListSearchContainerNodeState(peerIdWithRevealedOptions: peerIdWithRevealedOptions)
return ChatListSearchContainerNodeState(peerIdWithRevealedOptions: peerIdWithRevealedOptions, selectedMessageIds: self.selectedMessageIds)
}
func withUpdatedSelectedMessageIds(_ selectedMessageIds: Set<MessageId>?) -> ChatListSearchContainerNodeState {
return ChatListSearchContainerNodeState(peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, selectedMessageIds: selectedMessageIds)
}
}
@ -681,12 +689,13 @@ public struct ChatListSearchOptions {
public final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
private let context: AccountContext
private let filter: ChatListNodePeersFilter
private let peersFilter: ChatListNodePeersFilter
private var interaction: ChatListNodeInteraction?
private let openMessage: (Peer, MessageId) -> Void
private let navigationController: NavigationController?
let filterContainerNode: ChatListSearchFiltersContainerNode
private var selectionPanelNode: ChatListSearchMessageSelectionPanelNode?
private let recentListNode: ListView
private let listNode: ListView
private let mediaNode: ChatListSearchMediaNode
@ -695,8 +704,11 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
private var enqueuedTransitions: [(ChatListSearchContainerTransition, Bool)] = []
private var validLayout: (ContainerViewLayout, CGFloat)?
private var present: ((ViewController, Any?) -> Void)?
private var presentInGlobalOverlay: ((ViewController, Any?) -> Void)?
private let activeActionDisposable = MetaDisposable()
private let recentDisposable = MetaDisposable()
private let updatedRecentPeersDisposable = MetaDisposable()
@ -738,11 +750,12 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, openPeer originalOpenPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?, updatedSearchOptions: ((ChatListSearchOptions?) -> Void)? = nil) {
self.context = context
self.filter = filter
self.peersFilter = filter
self.dimNode = ASDisplayNode()
self.navigationController = navigationController
self.updatedSearchOptions = updatedSearchOptions
self.present = present
self.presentInGlobalOverlay = presentInGlobalOverlay
self.openMessage = originalOpenMessage
@ -1475,7 +1488,11 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
}
}
strongSelf.mediaNode.updateHistory(entries: entriesAndFlags?.0 ?? [], totalCount: totalCount, updateType: .Initial)
var entries: [ChatListSearchEntry]? = entriesAndFlags?.0 ?? []
if isSearching && (entries?.isEmpty ?? true) {
entries = nil
}
strongSelf.mediaNode.updateHistory(entries: entries, totalCount: totalCount, updateType: .Initial)
}
let previousEntries = previousSearchItems.swap(entriesAndFlags?.0)
@ -1621,6 +1638,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
deinit {
self.activeActionDisposable.dispose()
self.updatedRecentPeersDisposable.dispose()
self.recentDisposable.dispose()
self.searchDisposable.dispose()
@ -1701,8 +1719,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
private func updateTheme(theme: PresentationTheme) {
self.backgroundColor = self.filter.contains(.excludeRecent) ? nil : theme.chatList.backgroundColor
self.dimNode.backgroundColor = self.filter.contains(.excludeRecent) ? UIColor.black.withAlphaComponent(0.5) : theme.chatList.backgroundColor
self.backgroundColor = self.peersFilter.contains(.excludeRecent) ? nil : theme.chatList.backgroundColor
self.dimNode.backgroundColor = self.peersFilter.contains(.excludeRecent) ? UIColor.black.withAlphaComponent(0.5) : theme.chatList.backgroundColor
self.recentListNode.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
self.listNode.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
@ -1811,9 +1829,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
strongSelf.emptyResultsTextNode.isHidden = !emptyResults
strongSelf.emptyResultsAnimationNode.visibility = emptyResults
strongSelf.recentListNode.isHidden = displayingResults || strongSelf.filter.contains(.excludeRecent)
strongSelf.recentListNode.isHidden = displayingResults || strongSelf.peersFilter.contains(.excludeRecent)
strongSelf.dimNode.isHidden = displayingResults
strongSelf.backgroundColor = !displayingResults && strongSelf.filter.contains(.excludeRecent) ? nil : strongSelf.presentationData.theme.chatList.backgroundColor
strongSelf.backgroundColor = !displayingResults && strongSelf.peersFilter.contains(.excludeRecent) ? nil : strongSelf.presentationData.theme.chatList.backgroundColor
if let (layout, navigationBarHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
@ -2036,6 +2054,66 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
transition.updateFrame(node: self.filterContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight + 6.0), size: CGSize(width: layout.size.width, height: 37.0)))
self.filterContainerNode.update(size: CGSize(width: layout.size.width, height: 37.0), sideInset: layout.safeInsets.left, filters: ChatListSearchFilter.allCases.map { .filter($0) }, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
if let selectedMessageIds = self.stateValue.selectedMessageIds {
var wasAdded = false
let selectionPanelNode: ChatListSearchMessageSelectionPanelNode
if let current = self.selectionPanelNode {
selectionPanelNode = current
} else {
wasAdded = true
selectionPanelNode = ChatListSearchMessageSelectionPanelNode(context: self.context, deleteMessages: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.deleteMessages(messageIds: nil)
}, shareMessages: { [weak self] in
guard let strongSelf = self, let messageIds = strongSelf.stateValue.selectedMessageIds, !messageIds.isEmpty else {
return
}
let _ = (strongSelf.context.account.postbox.transaction { transaction -> [Message] in
var messages: [Message] = []
for id in messageIds {
if let message = transaction.getMessage(id) {
messages.append(message)
}
}
return messages
}
|> deliverOnMainQueue).start(next: { messages in
if let strongSelf = self, !messages.isEmpty {
let shareController = ShareController(context: strongSelf.context, subject: .messages(messages.sorted(by: { lhs, rhs in
return lhs.index < rhs.index
})), externalShare: true, immediateExternalShare: true)
strongSelf.view.endEditing(true)
strongSelf.present?(shareController, nil)
}
})
}, forwardMessages: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.forwardMessages(messageIds: nil)
})
self.selectionPanelNode = selectionPanelNode
self.addSubnode(selectionPanelNode)
}
selectionPanelNode.selectedMessages = selectedMessageIds
let panelHeight = selectionPanelNode.update(layout: layout, presentationData: self.presentationData, transition: wasAdded ? .immediate : transition)
let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - panelHeight), size: CGSize(width: layout.size.width, height: panelHeight))
if wasAdded {
selectionPanelNode.frame = panelFrame
transition.animatePositionAdditive(node: selectionPanelNode, offset: CGPoint(x: 0.0, y: panelHeight))
} else {
transition.updateFrame(node: selectionPanelNode, frame: panelFrame)
}
} else if let selectionPanelNode = self.selectionPanelNode {
self.selectionPanelNode = nil
transition.updateFrame(node: selectionPanelNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: selectionPanelNode.bounds.size), completion: { [weak selectionPanelNode] _ in
selectionPanelNode?.removeFromSupernode()
})
}
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
self.recentListNode.frame = CGRect(origin: CGPoint(), size: layout.size)
@ -2180,15 +2258,18 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuMore, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/More"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
guard let strongSelf = self else {
return
if let strongSelf = self {
strongSelf.updateState { state in
return state.withUpdatedSelectedMessageIds([message.id])
}
if let (layout, navigationBarHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
}
// strongSelf.chatInterfaceInteraction.toggleMessagesSelection([message.id], true)
// strongSelf.expandTabs()
f(.default)
})))
// }
switch previewData {
case let .gallery(gallery):
@ -2245,12 +2326,17 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
})))
items.append(.separator)
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuMore, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/More"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuMore, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/More"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
c.dismiss(completion: {
// if let strongSelf = self {
// strongSelf.chatInterfaceInteraction.toggleMessagesSelection([message.id], true)
// strongSelf.expandTabs()
// }
if let strongSelf = self {
strongSelf.updateState { state in
return state.withUpdatedSelectedMessageIds([message.id])
}
if let (layout, navigationBarHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
}
})
})))
@ -2262,74 +2348,83 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
self.updateSearchOptions(nil)
self.setQuery?(nil, [], self.searchQueryValue ?? "")
}
func deleteMessages(messageIds: Set<MessageId>?) {
}
func forwardMessages(messageIds: Set<MessageId>?) {
// if let messageIds = messageIds, !messageIds.isEmpty {
// let peerSelectionController = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.onlyWriteable, .excludeDisabled]))
// peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peerId in
// if let strongSelf = self, let _ = peerSelectionController {
// if peerId == strongSelf.context.account.peerId {
//// strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
//
// let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messageIds.map { id -> EnqueueMessage in
// return .forward(source: id, grouping: .auto, attributes: [])
// })
// |> deliverOnMainQueue).start(next: { [weak self] messageIds in
// if let strongSelf = self {
// let signals: [Signal<Bool, NoError>] = messageIds.compactMap({ id -> Signal<Bool, NoError>? in
// guard let id = id else {
// return nil
// }
// return strongSelf.context.account.pendingMessageManager.pendingMessageStatus(id)
// |> mapToSignal { status, _ -> Signal<Bool, NoError> in
// if status != nil {
// return .never()
// } else {
// return .single(true)
// }
// }
// |> take(1)
// })
// strongSelf.activeActionDisposable.set((combineLatest(signals)
// |> deliverOnMainQueue).start(completed: {
// guard let strongSelf = self else {
// return
// }
// strongSelf.controller?.present(OverlayStatusController(theme: strongSelf.presentationData.theme, type: .success), in: .window(.root))
// }))
// }
// })
// if let peerSelectionController = peerSelectionController {
// peerSelectionController.dismiss()
// }
// } else {
// let _ = (strongSelf.context.account.postbox.transaction({ transaction -> Void in
// transaction.updatePeerChatInterfaceState(peerId, update: { currentState in
// if let currentState = currentState as? ChatInterfaceState {
// return currentState.withUpdatedForwardMessageIds(Array(messageIds))
// } else {
// return ChatInterfaceState().withUpdatedForwardMessageIds(Array(messageIds))
// }
// })
// }) |> deliverOnMainQueue).start(completed: {
// if let strongSelf = self {
if let messageIds = messageIds, !messageIds.isEmpty {
let peerSelectionController = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.onlyWriteable, .excludeDisabled]))
peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peerId in
if let strongSelf = self, let _ = peerSelectionController {
if peerId == strongSelf.context.account.peerId {
// strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messageIds.map { id -> EnqueueMessage in
return .forward(source: id, grouping: .auto, attributes: [])
})
|> deliverOnMainQueue).start(next: { [weak self] messageIds in
if let strongSelf = self {
let signals: [Signal<Bool, NoError>] = messageIds.compactMap({ id -> Signal<Bool, NoError>? in
guard let id = id else {
return nil
}
return strongSelf.context.account.pendingMessageManager.pendingMessageStatus(id)
|> mapToSignal { status, _ -> Signal<Bool, NoError> in
if status != nil {
return .never()
} else {
return .single(true)
}
}
|> take(1)
})
strongSelf.activeActionDisposable.set((combineLatest(signals)
|> deliverOnMainQueue).start(completed: {
guard let strongSelf = self else {
return
}
strongSelf.present?(OverlayStatusController(theme: strongSelf.presentationData.theme, type: .success), nil)
}))
}
})
if let peerSelectionController = peerSelectionController {
peerSelectionController.dismiss()
}
} else {
let _ = (strongSelf.context.account.postbox.transaction({ transaction -> Void in
transaction.updatePeerChatInterfaceState(peerId, update: { currentState in
if let currentState = currentState as? ChatInterfaceState {
return currentState.withUpdatedForwardMessageIds(Array(messageIds))
} else {
return ChatInterfaceState().withUpdatedForwardMessageIds(Array(messageIds))
}
})
}) |> deliverOnMainQueue).start(completed: {
if let strongSelf = self {
// strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
//
// let ready = Promise<Bool>()
// strongSelf.activeActionDisposable.set((ready.get() |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { _ in
// if let peerSelectionController = peerSelectionController {
// peerSelectionController.dismiss()
// }
// }))
//
// (strongSelf.controller?.navigationController as? NavigationController)?.replaceTopController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId)), animated: false, ready: ready)
// }
// })
// }
// }
// }
// self.controller?.push(peerSelectionController)
// }
let ready = Promise<Bool>()
let signal = ready.get()
|> filter({ $0 })
|> take(1)
|> deliverOnMainQueue
strongSelf.activeActionDisposable.set(signal.start(next: { _ in
if let peerSelectionController = peerSelectionController {
peerSelectionController.dismiss()
}
}))
let controller = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))
strongSelf.navigationController?.replaceTopController(controller, animated: false, ready: ready)
}
})
}
}
}
self.navigationController?.pushViewController(peerSelectionController)
}
}
}

View File

@ -14,6 +14,7 @@ import UniversalMediaPlayer
import ListMessageItem
import ListSectionHeaderNode
import ChatMessageInteractiveMediaBadge
import ShimmerEffect
private let mediaBadgeBackgroundColor = UIColor(white: 0.0, alpha: 0.6)
private let mediaBadgeTextColor = UIColor.white
@ -41,8 +42,6 @@ private final class VisualMediaItemNode: ASDisplayNode {
private let context: AccountContext
private let interaction: VisualMediaItemInteraction
// private var videoLayerFrameManager: SoftwareVideoLayerFrameManager?
// private var sampleBufferLayer: SampleBufferLayer?
private var displayLink: ConstantDisplayLinkAnimator?
private var displayLinkTimestamp: Double = 0.0
@ -50,6 +49,7 @@ private final class VisualMediaItemNode: ASDisplayNode {
private let imageNode: TransformImageNode
private var statusNode: RadialStatusNode
private let mediaBadgeNode: ChatMessageInteractiveMediaBadge
private var placeholderNode: ShimmerEffectNode?
private let fetchStatusDisposable = MetaDisposable()
private let fetchDisposable = MetaDisposable()
@ -84,7 +84,9 @@ private final class VisualMediaItemNode: ASDisplayNode {
guard let strongSelf = self, let item = strongSelf.item else {
return
}
strongSelf.interaction.openMessageContextActions(item.0.message, strongSelf.containerNode, strongSelf.containerNode.bounds, gesture)
if let message = item.0.message {
strongSelf.interaction.openMessageContextActions(message, strongSelf.containerNode, strongSelf.containerNode.bounds, gesture)
}
}
}
@ -108,12 +110,15 @@ private final class VisualMediaItemNode: ASDisplayNode {
}
@objc func tapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
guard let message = self.item?.0.message else {
return
}
if case .ended = recognizer.state {
if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation {
if case .tap = gesture {
if let (item, _, _, _) = self.item {
var media: Media?
for value in item.message.media {
for value in message.media {
if let image = value as? TelegramMediaImage {
media = image
break
@ -125,13 +130,13 @@ private final class VisualMediaItemNode: ASDisplayNode {
if let media = media {
if let file = media as? TelegramMediaFile {
if isMediaStreamable(message: item.message, media: file) {
self.interaction.openMessage(item.message)
if isMediaStreamable(message: message, media: file) {
self.interaction.openMessage(message)
} else {
self.progressPressed()
}
} else {
self.interaction.openMessage(item.message)
self.interaction.openMessage(message)
}
}
}
@ -172,50 +177,34 @@ private final class VisualMediaItemNode: ASDisplayNode {
self.containerNode.cancelGesture()
}
func updateAbsoluteRect(_ absoluteRect: CGRect, within containerSize: CGSize) {
self.placeholderNode?.updateAbsoluteRect(absoluteRect, within: containerSize)
}
func update(size: CGSize, item: VisualMediaItem, theme: PresentationTheme, synchronousLoad: Bool) {
if item === self.item?.0 && size == self.item?.2 {
return
}
self.theme = theme
var media: Media?
for value in item.message.media {
if let image = value as? TelegramMediaImage {
media = image
break
} else if let file = value as? TelegramMediaFile {
media = file
break
if let message = item.message {
for value in message.media {
if let image = value as? TelegramMediaImage {
media = image
break
} else if let file = value as? TelegramMediaFile {
media = file
break
}
}
}
// if let file = media as? TelegramMediaFile, file.isAnimated {
// if self.videoLayerFrameManager == nil {
// let sampleBufferLayer: SampleBufferLayer
// if let current = self.sampleBufferLayer {
// sampleBufferLayer = current
// } else {
// sampleBufferLayer = takeSampleBufferLayer()
// self.sampleBufferLayer = sampleBufferLayer
// self.imageNode.layer.addSublayer(sampleBufferLayer.layer)
// }
//
// self.videoLayerFrameManager = SoftwareVideoLayerFrameManager(account: self.context.account, fileReference: FileMediaReference.message(message: MessageReference(item.message), media: file), layerHolder: sampleBufferLayer)
// self.videoLayerFrameManager?.start()
// }
// } else {
// if let sampleBufferLayer = self.sampleBufferLayer {
// sampleBufferLayer.layer.removeFromSuperlayer()
// self.sampleBufferLayer = nil
// }
// self.videoLayerFrameManager = nil
// }
if let media = media, (self.item?.1 == nil || !media.isEqual(to: self.item!.1!)) {
if let media = media, (self.item?.1 == nil || !media.isEqual(to: self.item!.1!)), let message = item.message {
var mediaDimensions: CGSize?
if let image = media as? TelegramMediaImage, let largestSize = largestImageRepresentation(image.representations)?.dimensions {
mediaDimensions = largestSize.cgSize
self.imageNode.setSignal(mediaGridMessagePhoto(account: context.account, photoReference: .message(message: MessageReference(item.message), media: image), fullRepresentationSize: CGSize(width: 300.0, height: 300.0), synchronousLoad: synchronousLoad), attemptSynchronously: synchronousLoad, dispatchOnDisplayLink: true)
self.imageNode.setSignal(mediaGridMessagePhoto(account: context.account, photoReference: .message(message: MessageReference(message), media: image), fullRepresentationSize: CGSize(width: 300.0, height: 300.0), synchronousLoad: synchronousLoad), attemptSynchronously: synchronousLoad, dispatchOnDisplayLink: true)
self.fetchStatusDisposable.set(nil)
self.statusNode.transitionToState(.none, completion: { [weak self] in
@ -225,7 +214,7 @@ private final class VisualMediaItemNode: ASDisplayNode {
self.resourceStatus = nil
} else if let file = media as? TelegramMediaFile, file.isVideo {
mediaDimensions = file.dimensions?.cgSize
self.imageNode.setSignal(mediaGridMessageVideo(postbox: context.account.postbox, videoReference: .message(message: MessageReference(item.message), media: file), synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: true), attemptSynchronously: synchronousLoad)
self.imageNode.setSignal(mediaGridMessageVideo(postbox: context.account.postbox, videoReference: .message(message: MessageReference(message), media: file), synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: true), attemptSynchronously: synchronousLoad)
self.mediaBadgeNode.isHidden = file.isAnimated
@ -233,12 +222,12 @@ private final class VisualMediaItemNode: ASDisplayNode {
self.item = (item, media, size, mediaDimensions)
self.fetchStatusDisposable.set((messageMediaFileStatus(context: context, messageId: item.message.id, file: file)
self.fetchStatusDisposable.set((messageMediaFileStatus(context: context, messageId: message.id, file: file)
|> deliverOnMainQueue).start(next: { [weak self] status in
if let strongSelf = self, let (item, _, _, _) = strongSelf.item {
strongSelf.resourceStatus = status
let isStreamable = isMediaStreamable(message: item.message, media: file)
let isStreamable = isMediaStreamable(message: message, media: file)
var statusState: RadialStatusNodeState = .none
if isStreamable || file.isAnimated {
@ -310,18 +299,26 @@ private final class VisualMediaItemNode: ASDisplayNode {
self.mediaBadgeNode.frame = CGRect(origin: CGPoint(x: size.width - 3.0, y: size.height - 18.0 - 3.0), size: CGSize(width: 50.0, height: 50.0))
self.updateHiddenMedia()
if let placeholderNode = self.placeholderNode {
self.placeholderNode = nil
placeholderNode.removeFromSupernode()
}
} else if item.isEmpty, self.placeholderNode == nil {
let placeholderNode = ShimmerEffectNode()
placeholderNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: [.rect(rect: CGRect(origin: CGPoint(), size: size))], size: size)
self.addSubnode(placeholderNode)
self.placeholderNode = placeholderNode
}
let imageFrame = CGRect(origin: CGPoint(), size: size)
self.placeholderNode?.frame = imageFrame
if let (item, media, _, mediaDimensions) = self.item {
self.item = (item, media, size, mediaDimensions)
let imageFrame = CGRect(origin: CGPoint(), size: size)
self.containerNode.frame = imageFrame
self.imageNode.frame = imageFrame
// if let sampleBufferLayer = self.sampleBufferLayer {
// sampleBufferLayer.layer.frame = imageFrame
// }
if let mediaDimensions = mediaDimensions {
let imageSize = mediaDimensions.aspectFilled(imageFrame.size)
@ -379,8 +376,8 @@ private final class VisualMediaItemNode: ASDisplayNode {
}
func updateHiddenMedia() {
if let (item, _, _, _) = self.item {
if let _ = self.interaction.hiddenMedia[item.message.id] {
if let (item, _, _, _) = self.item, let message = item.message {
if let _ = self.interaction.hiddenMedia[message.id] {
self.isHidden = true
} else {
self.isHidden = false
@ -393,11 +390,22 @@ private final class VisualMediaItemNode: ASDisplayNode {
}
private final class VisualMediaItem {
let message: Message
let index: UInt32?
let message: Message?
let dimensions: CGSize
let aspectRatio: CGFloat
let isEmpty: Bool
init(index: UInt32) {
self.index = index
self.message = nil
self.dimensions = CGSize(width: 100.0, height: 100.0)
self.aspectRatio = 1.0
self.isEmpty = true
}
init(message: Message) {
self.index = nil
self.message = message
var aspectRatio: CGFloat = 1.0
@ -412,6 +420,17 @@ private final class VisualMediaItem {
}
self.aspectRatio = aspectRatio
self.dimensions = dimensions
self.isEmpty = false
}
var stableId: UInt32 {
if let message = self.message {
return message.stableId
} else if let index = self.index {
return index
} else {
return 0
}
}
}
@ -680,15 +699,24 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate {
}
func updateHistory(entries: [ChatListSearchEntry], totalCount: Int32, updateType: ViewUpdateType) {
func updateHistory(entries: [ChatListSearchEntry]?, totalCount: Int32, updateType: ViewUpdateType) {
switch updateType {
case .FillHole:
break
default:
self.mediaItems.removeAll()
for entry in entries {
if case let .message(message, _, _, _, _) = entry {
self.mediaItems.append(VisualMediaItem(message: message))
let loading: Bool
if let entries = entries {
loading = false
for entry in entries {
if case let .message(message, _, _, _, _) = entry {
self.mediaItems.append(VisualMediaItem(message: message))
}
}
} else {
loading = true
for i in 0 ..< 21 {
self.mediaItems.append(VisualMediaItem(index: UInt32(i)))
}
}
self.itemsLayout = nil
@ -697,12 +725,14 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate {
self.initialized = true
if let (size, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = self.currentParams {
if totalCount > 0 {
if loading {
self.headerNode.title = ""
} else if totalCount > 0 {
self.headerNode.title = presentationData.strings.ChatList_Search_Photos(totalCount).uppercased()
}
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: true, transition: .immediate)
self.headerNode.alpha = self.mediaItems.isEmpty ? 0.0 : 1.0
self.headerNode.alpha = self.mediaItems.isEmpty && !loading ? 0.0 : 1.0
if !self.didSetReady {
self.didSetReady = true
self.ready.set(.single(true))
@ -722,7 +752,7 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate {
func findLoadedMessage(id: MessageId) -> Message? {
for item in self.mediaItems {
if item.message.id == id {
if item.message?.id == id {
return item.message
}
}
@ -743,8 +773,8 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate {
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
for item in self.mediaItems {
if item.message.id == messageId {
if let itemNode = self.visibleMediaItems[item.message.stableId] {
if let message = item.message, message.id == messageId {
if let itemNode = self.visibleMediaItems[message.stableId] {
return itemNode.transitionNode()
}
break
@ -855,7 +885,7 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate {
var validIds = Set<UInt32>()
if minVisibleIndex <= maxVisibleIndex {
for i in minVisibleIndex ... maxVisibleIndex {
let stableId = self.mediaItems[i].message.stableId
let stableId = self.mediaItems[i].stableId
validIds.insert(stableId)
let itemFrame = itemsLayout.frame(forItemAt: i, sideInset: sideInset)
@ -869,6 +899,7 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate {
self.scrollNode.addSubnode(itemNode)
}
itemNode.frame = itemFrame
itemNode.updateAbsoluteRect(itemFrame, within: self.scrollNode.view.bounds.size)
if headerItem == nil && itemFrame.maxY > headerItemMinY {
headerItem = self.mediaItems[i].message
}

View File

@ -0,0 +1,181 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import Display
import Postbox
import TelegramCore
import SyncCore
import SwiftSignalKit
import TelegramPresentationData
import AccountContext
import AppBundle
final class ChatListSearchMessageSelectionPanelNode: ASDisplayNode {
private let context: AccountContext
private var theme: PresentationTheme
private let deleteMessages: () -> Void
private let shareMessages: () -> Void
private let forwardMessages: () -> Void
private let separatorNode: ASDisplayNode
private let backgroundNode: ASDisplayNode
private let deleteButton: HighlightableButtonNode
private let forwardButton: HighlightableButtonNode
private let shareButton: HighlightableButtonNode
private var actions: ChatAvailableMessageActions?
private let canDeleteMessagesDisposable = MetaDisposable()
private var validLayout: ContainerViewLayout?
var selectedMessages = Set<MessageId>() {
didSet {
if oldValue != self.selectedMessages {
self.forwardButton.isEnabled = self.selectedMessages.count != 0
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
if self.selectedMessages.isEmpty {
self.actions = nil
if let layout = self.validLayout {
let _ = self.update(layout: layout, presentationData: presentationData, transition: .immediate)
}
self.canDeleteMessagesDisposable.set(nil)
} else {
self.canDeleteMessagesDisposable.set((self.context.sharedContext.chatAvailableMessageActions(postbox: context.account.postbox, accountPeerId: context.account.peerId, messageIds: self.selectedMessages)
|> deliverOnMainQueue).start(next: { [weak self] actions in
if let strongSelf = self {
strongSelf.actions = actions
if let layout = strongSelf.validLayout {
let _ = strongSelf.update(layout: layout, presentationData: presentationData, transition: .immediate)
}
}
}))
}
}
}
}
init(context: AccountContext, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void) {
self.context = context
self.deleteMessages = deleteMessages
self.shareMessages = shareMessages
self.forwardMessages = forwardMessages
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.theme = presentationData.theme
self.separatorNode = ASDisplayNode()
self.separatorNode.backgroundColor = presentationData.theme.chat.inputPanel.panelSeparatorColor
self.backgroundNode = ASDisplayNode()
self.backgroundNode.backgroundColor = presentationData.theme.chat.inputPanel.panelBackgroundColor
self.deleteButton = HighlightableButtonNode(pointerStyle: .default)
self.deleteButton.isEnabled = false
self.deleteButton.isAccessibilityElement = true
self.deleteButton.accessibilityLabel = presentationData.strings.VoiceOver_MessageContextDelete
self.forwardButton = HighlightableButtonNode(pointerStyle: .default)
self.forwardButton.isAccessibilityElement = true
self.forwardButton.accessibilityLabel = presentationData.strings.VoiceOver_MessageContextForward
self.shareButton = HighlightableButtonNode(pointerStyle: .default)
self.shareButton.isEnabled = false
self.shareButton.isAccessibilityElement = true
self.shareButton.accessibilityLabel = presentationData.strings.VoiceOver_MessageContextShare
self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: presentationData.theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: presentationData.theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: presentationData.theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: presentationData.theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: presentationData.theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: presentationData.theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
super.init()
self.addSubnode(self.deleteButton)
self.addSubnode(self.forwardButton)
self.addSubnode(self.shareButton)
self.addSubnode(self.separatorNode)
self.forwardButton.isEnabled = false
self.deleteButton.addTarget(self, action: #selector(self.deleteButtonPressed), forControlEvents: .touchUpInside)
self.forwardButton.addTarget(self, action: #selector(self.forwardButtonPressed), forControlEvents: .touchUpInside)
self.shareButton.addTarget(self, action: #selector(self.shareButtonPressed), forControlEvents: .touchUpInside)
}
deinit {
self.canDeleteMessagesDisposable.dispose()
}
func update(layout: ContainerViewLayout, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat {
if presentationData.theme !== self.theme {
self.theme = presentationData.theme
self.backgroundNode.backgroundColor = presentationData.theme.rootController.navigationBar.backgroundColor
self.separatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: presentationData.theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: presentationData.theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: presentationData.theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: presentationData.theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: presentationData.theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: presentationData.theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
}
let width = layout.size.width
let insets = layout.insets(options: [])
let leftInset = insets.left
let rightInset = insets.left
let panelHeight: CGFloat
if case .regular = layout.metrics.widthClass, case .regular = layout.metrics.heightClass {
panelHeight = 49.0
} else {
panelHeight = 45.0
}
if let actions = self.actions {
self.deleteButton.isEnabled = false
self.forwardButton.isEnabled = actions.options.contains(.forward)
self.shareButton.isEnabled = false
self.deleteButton.isEnabled = !actions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty
self.shareButton.isEnabled = !actions.options.intersection([.forward]).isEmpty
self.deleteButton.isHidden = !self.deleteButton.isEnabled
} else {
self.deleteButton.isEnabled = false
self.deleteButton.isHidden = true
self.forwardButton.isEnabled = false
self.shareButton.isEnabled = false
}
self.deleteButton.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: 57.0, height: panelHeight))
self.forwardButton.frame = CGRect(origin: CGPoint(x: width - rightInset - 57.0, y: 0.0), size: CGSize(width: 57.0, height: panelHeight))
self.shareButton.frame = CGRect(origin: CGPoint(x: floor((width - rightInset - 57.0) / 2.0), y: 0.0), size: CGSize(width: 57.0, height: panelHeight))
let panelHeightWithInset = panelHeight + layout.intrinsicInsets.bottom
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: panelHeightWithInset)))
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: UIScreenPixel), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
return panelHeightWithInset
}
@objc func deleteButtonPressed() {
self.deleteMessages()
}
@objc func forwardButtonPressed() {
self.shareMessages()
}
@objc func shareButtonPressed() {
self.forwardMessages()
}
}

View File

@ -37,13 +37,14 @@ public final class ListMessageItem: ListViewItem {
let interaction: ListMessageItemInteraction
let message: Message
let selection: ChatHistoryMessageSelection
let hintIsLink: Bool
let isGlobalSearchResult: Bool
let header: ListViewItemHeader?
public let selectable: Bool = true
public init(presentationData: ChatPresentationData, context: AccountContext, chatLocation: ChatLocation, interaction: ListMessageItemInteraction, message: Message, selection: ChatHistoryMessageSelection, displayHeader: Bool, customHeader: ListViewItemHeader? = nil, isGlobalSearchResult: Bool = false) {
public init(presentationData: ChatPresentationData, context: AccountContext, chatLocation: ChatLocation, interaction: ListMessageItemInteraction, message: Message, selection: ChatHistoryMessageSelection, displayHeader: Bool, customHeader: ListViewItemHeader? = nil, hintIsLink: Bool = false, isGlobalSearchResult: Bool = false) {
self.presentationData = presentationData
self.context = context
self.chatLocation = chatLocation
@ -57,16 +58,19 @@ public final class ListMessageItem: ListViewItem {
self.header = nil
}
self.selection = selection
self.hintIsLink = hintIsLink
self.isGlobalSearchResult = isGlobalSearchResult
}
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
var viewClassName: AnyClass = ListMessageSnippetItemNode.self
for media in self.message.media {
if let _ = media as? TelegramMediaFile {
viewClassName = ListMessageFileItemNode.self
break
if !self.hintIsLink {
for media in self.message.media {
if let _ = media as? TelegramMediaFile {
viewClassName = ListMessageFileItemNode.self
break
}
}
}

View File

@ -128,6 +128,12 @@ private class SearchBarTextField: UITextField {
self.setNeedsLayout()
}
}
var selectedTokenIndex: Int? {
didSet {
self.layoutTokens(transition: .animated(duration: 0.2, curve: .easeInOut))
self.setNeedsLayout()
}
}
var theme: SearchBarNodeTheme

View File

@ -354,7 +354,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
controlColor: UIColor(rgb: 0x7e8791),
accentTextColor: UIColor(rgb: 0x007ee5),
backgroundColor: UIColor(rgb: 0xf7f7f7),
separatorColor: UIColor(rgb: 0xb1b1b1),
separatorColor: UIColor(rgb: 0xc8c7cc),
badgeBackgroundColor: UIColor(rgb: 0xff3b30),
badgeStrokeColor: UIColor(rgb: 0xff3b30),
badgeTextColor: UIColor(rgb: 0xffffff),
@ -374,7 +374,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
inputPlaceholderTextColor: UIColor(rgb: 0x8e8e93),
inputIconColor: UIColor(rgb: 0x8e8e93),
inputClearButtonColor: UIColor(rgb: 0x7b7b81),
separatorColor: UIColor(rgb: 0xb1b1b1)
separatorColor: UIColor(rgb: 0xc8c7cc)
)
let rootController = PresentationThemeRootController(
@ -685,7 +685,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
let historyNavigation = PresentationThemeChatHistoryNavigation(
fillColor: UIColor(rgb: 0xf7f7f7),
strokeColor: UIColor(rgb: 0xb1b1b1),
strokeColor: UIColor(rgb: 0xc8c7cc),
foregroundColor: UIColor(rgb: 0x88888d),
badgeBackgroundColor: UIColor(rgb: 0x007ee5),
badgeStrokeColor: UIColor(rgb: 0x007ee5),
@ -751,7 +751,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
backgroundColor: UIColor(rgb: 0xffffff),
primaryTextColor: UIColor(rgb: 0x000000),
controlColor: UIColor(rgb: 0x7e8791),
separatorColor: UIColor(rgb: 0xb1b1b1)
separatorColor: UIColor(rgb: 0xc8c7cc)
)
)
)

View File

@ -210,6 +210,7 @@ framework(
"//submodules/FileMediaResourceStatus:FileMediaResourceStatus",
"//submodules/ChatMessageInteractiveMediaBadge:ChatMessageInteractiveMediaBadge",
"//submodules/GalleryData:GalleryData",
"//submodules/ChatInterfaceState:ChatInterfaceState",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -205,6 +205,7 @@ swift_library(
"//submodules/FileMediaResourceStatus:FileMediaResourceStatus",
"//submodules/ChatMessageInteractiveMediaBadge:ChatMessageInteractiveMediaBadge",
"//submodules/GalleryData:GalleryData",
"//submodules/ChatInterfaceState:ChatInterfaceState",
],
visibility = [
"//visibility:public",

View File

@ -4,7 +4,6 @@ import Display
import AsyncDisplayKit
private final class AudioWaveformNodeParameters: NSObject {
let waveform: AudioWaveform?
let color: UIColor?
let gravity: AudioWaveformNode.Gravity?
@ -21,9 +20,7 @@ private final class AudioWaveformNodeParameters: NSObject {
}
final class AudioWaveformNode: ASDisplayNode {
enum Gravity {
case bottom
case center
}

View File

@ -62,6 +62,7 @@ import TooltipUI
import StatisticsUI
import MediaResources
import GalleryData
import ChatInterfaceState
extension ChatLocation {
var peerId: PeerId {

View File

@ -10,6 +10,7 @@ import AccountContext
import TextSelectionNode
import ReactionSelectionNode
import ContextUI
import ChatInterfaceState
struct ChatInterfaceHighlightedState: Equatable {
let messageStableId: UInt32

View File

@ -13,6 +13,7 @@ import AccountContext
import TelegramNotices
import ReactionSelectionNode
import TelegramUniversalVideoContent
import ChatInterfaceState
final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem {
let itemNode: OverlayMediaItemNode

View File

@ -16,6 +16,7 @@ import Emoji
import AppBundle
import ListMessageItem
import AccountContext
import ChatInterfaceState
private class ChatHistoryListSelectionRecognizer: UIPanGestureRecognizer {
private let selectionGestureActivationThreshold: CGFloat = 5.0
@ -76,7 +77,7 @@ public enum ChatHistoryListDisplayHeaders {
public enum ChatHistoryListMode: Equatable {
case bubbles
case list(search: Bool, reversed: Bool, displayHeaders: ChatHistoryListDisplayHeaders, isGlobalSearch: Bool)
case list(search: Bool, reversed: Bool, displayHeaders: ChatHistoryListDisplayHeaders, hintLinks: Bool, isGlobalSearch: Bool)
}
enum ChatHistoryViewScrollPosition {
@ -249,7 +250,7 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca
switch mode {
case .bubbles:
item = ChatMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .message(message: message, read: read, selection: selection, attributes: attributes))
case let .list(_, _, displayHeaders, isGlobalSearch):
case let .list(_, _, displayHeaders, hintLinks, isGlobalSearch):
let displayHeader: Bool
switch displayHeaders {
case .none:
@ -259,8 +260,7 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca
case .allButLast:
displayHeader = listMessageDateHeaderId(timestamp: message.timestamp) != lastHeaderId
}
item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: message, selection: selection, displayHeader: displayHeader, isGlobalSearchResult: isGlobalSearch)
item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: message, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch)
}
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
case let .MessageGroupEntry(_, messages, presentationData):
@ -268,7 +268,7 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca
switch mode {
case .bubbles:
item = ChatMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .group(messages: messages))
case let .list(_, _, _, _):
case let .list(_, _, _, _, _):
assertionFailure()
item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: messages[0].0, selection: .none, displayHeader: false)
}
@ -293,7 +293,7 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca
switch mode {
case .bubbles:
item = ChatMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .message(message: message, read: read, selection: selection, attributes: attributes))
case let .list(_, _, displayHeaders, isGlobalSearch):
case let .list(_, _, displayHeaders, hintLinks, isGlobalSearch):
let displayHeader: Bool
switch displayHeaders {
case .none:
@ -303,7 +303,7 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca
case .allButLast:
displayHeader = listMessageDateHeaderId(timestamp: message.timestamp) != lastHeaderId
}
item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: message, selection: selection, displayHeader: displayHeader, isGlobalSearchResult: isGlobalSearch)
item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: message, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch)
}
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
case let .MessageGroupEntry(_, messages, presentationData):
@ -311,7 +311,7 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca
switch mode {
case .bubbles:
item = ChatMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .group(messages: messages))
case let .list(_, _, _, _):
case let .list(_, _, _, _, _):
assertionFailure()
item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: messages[0].0, selection: .none, displayHeader: false)
}
@ -762,7 +762,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
var reverse = false
var includeSearchEntry = false
if case let .list(search, reverseValue, _, _) = mode {
if case let .list(search, reverseValue, _, _, _) = mode {
includeSearchEntry = search
reverse = reverseValue
}
@ -1738,7 +1738,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
switch self.mode {
case .bubbles:
item = ChatMessageItem(presentationData: presentationData, context: self.context, chatLocation: self.chatLocation, associatedData: associatedData, controllerInteraction: self.controllerInteraction, content: .message(message: message, read: read, selection: selection, attributes: attributes))
case let .list(_, _, displayHeaders, isGlobalSearch):
case let .list(_, _, displayHeaders, hintLinks, isGlobalSearch):
let displayHeader: Bool
switch displayHeaders {
case .none:
@ -1748,7 +1748,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
case .allButLast:
displayHeader = listMessageDateHeaderId(timestamp: message.timestamp) != historyView.lastHeaderId
}
item = ListMessageItem(presentationData: presentationData, context: self.context, chatLocation: self.chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: self.controllerInteraction), message: message, selection: selection, displayHeader: displayHeader, isGlobalSearchResult: isGlobalSearch)
item = ListMessageItem(presentationData: presentationData, context: self.context, chatLocation: self.chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: self.controllerInteraction), message: message, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch)
}
let updateItem = ListViewUpdateItem(index: index, previousIndex: index, item: item, directionHint: nil)
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [updateItem], options: [.AnimateInsertion], scrollToItem: nil, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })

View File

@ -6,6 +6,7 @@ import SyncCore
import SwiftSignalKit
import Display
import AccountContext
import ChatInterfaceState
func preloadedChatHistoryViewForLocation(_ location: ChatHistoryLocationInput, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, fixedCombinedReadStates: MessageHistoryViewReadState?, tagMask: MessageTags?, additionalData: [AdditionalMessageHistoryViewData], orderStatistics: MessageHistoryViewOrderStatistics = []) -> Signal<ChatHistoryViewUpdate, NoError> {
return chatHistoryViewForLocation(location, context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, scheduled: false, fixedCombinedReadStates: fixedCombinedReadStates, tagMask: tagMask, additionalData: additionalData, orderStatistics: orderStatistics)

View File

@ -6,6 +6,7 @@ import Postbox
import Display
import AccountContext
import Emoji
import ChatInterfaceState
struct PossibleContextQueryTypes: OptionSet {
var rawValue: Int32

View File

@ -17,6 +17,7 @@ import ContextUI
import GalleryUI
import OverlayStatusController
import PresentationDataUtils
import ChatInterfaceState
struct PeerSpecificPackData {
let peer: Peer

View File

@ -6,6 +6,7 @@ import SyncCore
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
import ChatInterfaceState
enum ChatPresentationInputQueryKind: Int32 {
case emoji

View File

@ -8,15 +8,11 @@ import SwiftSignalKit
import TelegramPresentationData
import LegacyComponents
import AccountContext
import ChatInterfaceState
private let offsetThreshold: CGFloat = 10.0
private let dismissOffsetThreshold: CGFloat = 70.0
enum ChatTextInputMediaRecordingButtonMode: Int32 {
case audio = 0
case video = 1
}
private func findTargetView(_ view: UIView, point: CGPoint) -> UIView? {
if view.bounds.contains(point) && view.tag == 0x01f2bca {
return view

View File

@ -11,6 +11,7 @@ import SettingsUI
import WallpaperResources
import MediaResources
import LocationUI
import ChatInterfaceState
private var telegramUIDeclaredEncodables: Void = {
declareEncodable(InAppNotificationSettings.self, f: { InAppNotificationSettings(decoder: $0) })

View File

@ -20,6 +20,7 @@ import LanguageLinkPreviewUI
import SettingsUI
import UrlHandling
import ShareController
import ChatInterfaceState
private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer) -> ChatControllerInteractionNavigateToPeer {
if case .default = navigation {

View File

@ -164,7 +164,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
self.historyNode = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: .message(initialMessageId), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: currentIsReversed, displayHeaders: .none, isGlobalSearch: isGlobalSearch))
self.historyNode = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: .message(initialMessageId), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: isGlobalSearch))
super.init()
@ -495,7 +495,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
let historyNode = ChatHistoryListNode(context: self.context, chatLocation: .peer(self.peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: .message(messageId), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, isGlobalSearch: self.isGlobalSearch))
let historyNode = ChatHistoryListNode(context: self.context, chatLocation: .peer(self.peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: .message(messageId), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch))
historyNode.preloadPages = true
historyNode.stackFromBottom = true
historyNode.updateFloatingHeaderOffset = { [weak self] offset, _ in

View File

@ -72,7 +72,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
self.selectedMessagesPromise.set(.single(self.selectedMessages))
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
self.listNode = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false, displayHeaders: .allButLast, isGlobalSearch: false))
self.listNode = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false, displayHeaders: .allButLast, hintLinks: tagMask == .webPage, isGlobalSearch: false))
self.listNode.defaultToSynchronousTransactionWhileScrolling = true
if tagMask == .music {

View File

@ -51,6 +51,7 @@ import SaveToCameraRoll
import PeerInfoUI
import ListMessageItem
import GalleryData
import ChatInterfaceState
protocol PeerInfoScreenItem: class {
var id: AnyHashable { get }

View File

@ -1,6 +1,7 @@
import Foundation
import Postbox
import TelegramPresentationData
import ChatInterfaceState
enum PeerMediaCollectionMode: Int32 {
case photoOrVideo

View File

@ -8,6 +8,7 @@ import OpenInExternalAppUI
import MusicAlbumArtResources
import LocalMediaResources
import LocationResources
import ChatInterfaceState
public let telegramAccountAuxiliaryMethods = AccountAuxiliaryMethods(updatePeerChatInputState: { interfaceState, inputState -> PeerChatInterfaceState? in
if interfaceState == nil {