mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Pinned messages
This commit is contained in:
parent
4ed47db191
commit
de039d7303
@ -3,7 +3,7 @@
|
||||
@implementation Serialization
|
||||
|
||||
- (NSUInteger)currentLayer {
|
||||
return 119;
|
||||
return 120;
|
||||
}
|
||||
|
||||
- (id _Nullable)parseMessage:(NSData * _Nullable)data {
|
||||
|
@ -1921,6 +1921,7 @@
|
||||
"Conversation.Unpin" = "Unpin";
|
||||
"Conversation.Report" = "Report Spam";
|
||||
"Conversation.PinnedMessage" = "Pinned Message";
|
||||
"Conversation.PinnedPreviousMessage" = "Previous Message";
|
||||
|
||||
"Conversation.Moderate.Delete" = "Delete Message";
|
||||
"Conversation.Moderate.Ban" = "Ban User";
|
||||
|
@ -14,7 +14,7 @@ public func legacySuggestionContext(context: AccountContext, peerId: PeerId, cha
|
||||
suggestionContext.userListSignal = { query in
|
||||
return SSignal { subscriber in
|
||||
if let query = query {
|
||||
let disposable = searchPeerMembers(context: context, peerId: peerId, chatLocation: chatLocation, query: query).start(next: { peers in
|
||||
let disposable = searchPeerMembers(context: context, peerId: peerId, chatLocation: chatLocation, query: query, scope: .mention).start(next: { peers in
|
||||
let users = NSMutableArray()
|
||||
for peer in peers {
|
||||
let user = TGUser()
|
||||
|
@ -5,20 +5,25 @@ import SyncCore
|
||||
import SwiftSignalKit
|
||||
import AccountContext
|
||||
|
||||
public func searchPeerMembers(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, query: String) -> Signal<[Peer], NoError> {
|
||||
public enum SearchPeerMembersScope {
|
||||
case memberSuggestion
|
||||
case mention
|
||||
}
|
||||
|
||||
public func searchPeerMembers(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, query: String, scope: SearchPeerMembersScope) -> Signal<[Peer], NoError> {
|
||||
if case .replyThread = chatLocation {
|
||||
return .single([])
|
||||
} else if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
return context.account.postbox.transaction { transaction -> CachedChannelData? in
|
||||
return transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData
|
||||
}
|
||||
|> mapToSignal { cachedData -> Signal<[Peer], NoError> in
|
||||
|> mapToSignal { cachedData -> Signal<([Peer], Bool), NoError> in
|
||||
if let cachedData = cachedData, let memberCount = cachedData.participantsSummary.memberCount, memberCount <= 64 {
|
||||
return Signal { subscriber in
|
||||
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.recent(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, requestUpdate: false, updated: { state in
|
||||
if case .ready = state.loadingState {
|
||||
let normalizedQuery = query.lowercased()
|
||||
subscriber.putNext(state.list.compactMap { participant -> Peer? in
|
||||
subscriber.putNext((state.list.compactMap { participant -> Peer? in
|
||||
if participant.peer.isDeleted {
|
||||
return nil
|
||||
}
|
||||
@ -37,7 +42,7 @@ public func searchPeerMembers(context: AccountContext, peerId: PeerId, chatLocat
|
||||
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}, true))
|
||||
}
|
||||
})
|
||||
|
||||
@ -51,12 +56,12 @@ public func searchPeerMembers(context: AccountContext, peerId: PeerId, chatLocat
|
||||
return Signal { subscriber in
|
||||
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.recent(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: query.isEmpty ? nil : query, updated: { state in
|
||||
if case .ready = state.loadingState {
|
||||
subscriber.putNext(state.list.compactMap { participant in
|
||||
subscriber.putNext((state.list.compactMap { participant in
|
||||
if participant.peer.isDeleted {
|
||||
return nil
|
||||
}
|
||||
return participant.peer
|
||||
})
|
||||
}, true))
|
||||
}
|
||||
})
|
||||
|
||||
@ -65,6 +70,36 @@ public func searchPeerMembers(context: AccountContext, peerId: PeerId, chatLocat
|
||||
}
|
||||
} |> runOn(Queue.mainQueue())
|
||||
}
|
||||
|> mapToSignal { result, isReady -> Signal<[Peer], NoError> in
|
||||
switch scope {
|
||||
case .mention:
|
||||
return .single(result)
|
||||
case .memberSuggestion:
|
||||
return context.account.postbox.transaction { transaction -> [Peer] in
|
||||
var result = result
|
||||
let normalizedQuery = query.lowercased()
|
||||
if isReady {
|
||||
if let channel = transaction.getPeer(peerId) as? TelegramChannel, case .group = channel.info {
|
||||
var matches = false
|
||||
if normalizedQuery.isEmpty {
|
||||
matches = true
|
||||
} else {
|
||||
if channel.indexName.matchesByTokens(normalizedQuery) {
|
||||
matches = true
|
||||
}
|
||||
if let addressName = channel.addressName, addressName.lowercased().hasPrefix(normalizedQuery) {
|
||||
matches = true
|
||||
}
|
||||
}
|
||||
if matches {
|
||||
result.insert(channel, at: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return searchGroupMembers(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, query: query)
|
||||
}
|
||||
|
@ -322,6 +322,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[106343499] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsSearch($0) }
|
||||
dict[-1548400251] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsKicked($0) }
|
||||
dict[-1150621555] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsContacts($0) }
|
||||
dict[915357814] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsMentions($0) }
|
||||
dict[-350980120] = { return Api.WebPage.parse_webPageEmpty($0) }
|
||||
dict[-981018084] = { return Api.WebPage.parse_webPagePending($0) }
|
||||
dict[-392411726] = { return Api.WebPage.parse_webPage($0) }
|
||||
|
@ -9991,6 +9991,7 @@ public extension Api {
|
||||
case channelParticipantsSearch(q: String)
|
||||
case channelParticipantsKicked(q: String)
|
||||
case channelParticipantsContacts(q: String)
|
||||
case channelParticipantsMentions(q: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -10036,6 +10037,12 @@ public extension Api {
|
||||
}
|
||||
serializeString(q, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .channelParticipantsMentions(let q):
|
||||
if boxed {
|
||||
buffer.appendInt32(915357814)
|
||||
}
|
||||
serializeString(q, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -10055,6 +10062,8 @@ public extension Api {
|
||||
return ("channelParticipantsKicked", [("q", q)])
|
||||
case .channelParticipantsContacts(let q):
|
||||
return ("channelParticipantsContacts", [("q", q)])
|
||||
case .channelParticipantsMentions(let q):
|
||||
return ("channelParticipantsMentions", [("q", q)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -10111,6 +10120,17 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_channelParticipantsMentions(_ reader: BufferReader) -> ChannelParticipantsFilter? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.ChannelParticipantsFilter.channelParticipantsMentions(q: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum WebPage: TypeConstructorDescription {
|
||||
|
@ -3737,9 +3737,26 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func search(flags: Int32, peer: Api.InputPeer, q: String, fromId: Api.InputUser?, topMsgId: Int32?, filter: Api.MessagesFilter, minDate: Int32, maxDate: Int32, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Messages>) {
|
||||
public static func setTyping(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, action: Api.SendMessageAction) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1310163211)
|
||||
buffer.appendInt32(1486110434)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
|
||||
action.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "messages.setTyping", parameters: [("flags", flags), ("peer", peer), ("topMsgId", topMsgId), ("action", action)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func search(flags: Int32, peer: Api.InputPeer, q: String, fromId: Api.InputPeer?, topMsgId: Int32?, filter: Api.MessagesFilter, minDate: Int32, maxDate: Int32, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Messages>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(204812012)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeString(q, buffer: buffer, boxed: false)
|
||||
@ -3763,23 +3780,6 @@ public extension Api {
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func setTyping(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, action: Api.SendMessageAction) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1486110434)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
|
||||
action.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "messages.setTyping", parameters: [("flags", flags), ("peer", peer), ("topMsgId", topMsgId), ("action", action)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public struct channels {
|
||||
public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
|
@ -221,11 +221,11 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
|
||||
guard let inputPeer = apiInputPeer(peer) else {
|
||||
return .single((nil, nil))
|
||||
}
|
||||
var fromInputUser: Api.InputUser? = nil
|
||||
var fromInputPeer: Api.InputPeer? = nil
|
||||
var flags: Int32 = 0
|
||||
if let from = values.from {
|
||||
fromInputUser = apiInputUser(from)
|
||||
if let _ = fromInputUser {
|
||||
fromInputPeer = apiInputPeer(from)
|
||||
if let _ = fromInputPeer {
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
}
|
||||
@ -241,7 +241,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
|
||||
if peer.id.namespace == Namespaces.Peer.CloudChannel && query.isEmpty && fromId == nil && tags == nil && minDate == nil && maxDate == nil {
|
||||
signal = account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: lowerBound?.id.id ?? 0, offsetDate: 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0))
|
||||
} else {
|
||||
signal = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputUser, topMsgId: topMsgId?.id, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0))
|
||||
signal = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputPeer, topMsgId: topMsgId?.id, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0))
|
||||
}
|
||||
peerMessages = signal
|
||||
|> map(Optional.init)
|
||||
@ -257,7 +257,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
|
||||
additionalPeerMessages = .single(nil)
|
||||
} else if mainCompleted || !hasAdditional {
|
||||
let lowerBound = state?.additional?.messages.last.flatMap({ $0.index })
|
||||
additionalPeerMessages = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputUser, topMsgId: topMsgId?.id, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0))
|
||||
additionalPeerMessages = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputPeer, topMsgId: topMsgId?.id, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.Messages?, NoError> in
|
||||
return .single(nil)
|
||||
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
||||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 119
|
||||
return 120
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -3268,13 +3268,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
if case let .replyThread(replyThreadMessageId) = strongSelf.chatLocation {
|
||||
pinnedMessageId = replyThreadMessageId.messageId
|
||||
pinnedMessageId = replyThreadMessageId.effectiveTopId
|
||||
}
|
||||
|
||||
var pinnedMessage: Message?
|
||||
var pinnedMessage: ChatPinnedMessage?
|
||||
if let pinnedMessageId = pinnedMessageId {
|
||||
if let cachedDataMessages = combinedInitialData.cachedDataMessages {
|
||||
pinnedMessage = cachedDataMessages[pinnedMessageId]
|
||||
if let message = cachedDataMessages[pinnedMessageId] {
|
||||
pinnedMessage = ChatPinnedMessage(message: message, isLatest: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3385,7 +3387,62 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let isTopReplyThreadMessageShown: Signal<Bool, NoError> = self.chatDisplayNode.historyNode.isTopReplyThreadMessageShown.get()
|
||||
|> distinctUntilChanged
|
||||
|
||||
self.cachedDataDisposable = combineLatest(queue: .mainQueue(), self.chatDisplayNode.historyNode.cachedPeerDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown).start(next: { [weak self] cachedDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown in
|
||||
let topPinnedMessage: Signal<ChatPinnedMessage?, NoError>
|
||||
switch self.chatLocation {
|
||||
case let .peer(peerId):
|
||||
let replyHistory: Signal<ChatHistoryViewUpdate, NoError> = (chatHistoryViewForLocation(ChatHistoryLocationInput(content: .Initial(count: 100), id: 0), context: self.context, chatLocation: .peer(peerId), chatLocationContextHolder: Atomic<ChatLocationContextHolder?>(value: nil), scheduled: false, fixedCombinedReadStates: nil, tagMask: MessageTags.photoOrVideo, additionalData: [])
|
||||
|> castError(Bool.self)
|
||||
|> mapToSignal { update -> Signal<ChatHistoryViewUpdate, Bool> in
|
||||
switch update {
|
||||
case let .Loading(_, type):
|
||||
if case .Generic(.FillHole) = type {
|
||||
return .fail(true)
|
||||
}
|
||||
case let .HistoryView(_, type, _, _, _, _, _):
|
||||
if case .Generic(.FillHole) = type {
|
||||
return .fail(true)
|
||||
}
|
||||
}
|
||||
return .single(update)
|
||||
})
|
||||
|> restartIfError
|
||||
|
||||
topPinnedMessage = combineLatest(
|
||||
replyHistory,
|
||||
self.chatDisplayNode.historyNode.topVisibleMessage.get()
|
||||
)
|
||||
|> map { update, topVisibleMessage -> ChatPinnedMessage? in
|
||||
var message: ChatPinnedMessage?
|
||||
switch update {
|
||||
case .Loading:
|
||||
break
|
||||
case let .HistoryView(view, _, _, _, _, _, _):
|
||||
for i in 0 ..< view.entries.count {
|
||||
let entry = view.entries[i]
|
||||
var matches = false
|
||||
if message == nil {
|
||||
matches = true
|
||||
} else if let topVisibleMessage = topVisibleMessage {
|
||||
if entry.message.id < topVisibleMessage.id {
|
||||
matches = true
|
||||
}
|
||||
} else {
|
||||
matches = true
|
||||
}
|
||||
if matches {
|
||||
message = ChatPinnedMessage(message: entry.message, isLatest: i == view.entries.count - 1)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
return message
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
case .replyThread:
|
||||
topPinnedMessage = .single(nil)
|
||||
}
|
||||
|
||||
self.cachedDataDisposable = combineLatest(queue: .mainQueue(), self.chatDisplayNode.historyNode.cachedPeerDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage).start(next: { [weak self] cachedDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage in
|
||||
if let strongSelf = self {
|
||||
let (cachedData, messages) = cachedDataAndMessages
|
||||
|
||||
@ -3413,22 +3470,26 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
} else if let _ = cachedData as? CachedSecretChatData {
|
||||
}
|
||||
|
||||
var pinnedMessage: ChatPinnedMessage?
|
||||
if case let .replyThread(replyThreadMessage) = strongSelf.chatLocation {
|
||||
if isTopReplyThreadMessageShown {
|
||||
pinnedMessageId = nil
|
||||
} else {
|
||||
pinnedMessageId = replyThreadMessage.messageId
|
||||
pinnedMessageId = replyThreadMessage.effectiveTopId
|
||||
}
|
||||
}
|
||||
|
||||
var pinnedMessage: Message?
|
||||
if let pinnedMessageId = pinnedMessageId {
|
||||
pinnedMessage = messages?[pinnedMessageId]
|
||||
if let pinnedMessageId = pinnedMessageId {
|
||||
if let message = messages?[pinnedMessageId] {
|
||||
pinnedMessage = ChatPinnedMessage(message: message, isLatest: true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pinnedMessageId = topPinnedMessage?.message.id
|
||||
pinnedMessage = topPinnedMessage
|
||||
}
|
||||
|
||||
var pinnedMessageUpdated = false
|
||||
if let current = strongSelf.presentationInterfaceState.pinnedMessage, let updated = pinnedMessage {
|
||||
if current.id != updated.id || current.stableVersion != updated.stableVersion {
|
||||
if current != updated {
|
||||
pinnedMessageUpdated = true
|
||||
}
|
||||
} else if (strongSelf.presentationInterfaceState.pinnedMessage != nil) != (pinnedMessage != nil) {
|
||||
@ -3437,7 +3498,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
let callsDataUpdated = strongSelf.presentationInterfaceState.callsAvailable != callsAvailable || strongSelf.presentationInterfaceState.callsPrivate != callsPrivate
|
||||
|
||||
if strongSelf.presentationInterfaceState.pinnedMessageId != pinnedMessageId || strongSelf.presentationInterfaceState.pinnedMessage?.stableVersion != pinnedMessage?.stableVersion || strongSelf.presentationInterfaceState.peerIsBlocked != peerIsBlocked || pinnedMessageUpdated || callsDataUpdated || strongSelf.presentationInterfaceState.slowmodeState != slowmodeState {
|
||||
if strongSelf.presentationInterfaceState.pinnedMessageId != pinnedMessageId || strongSelf.presentationInterfaceState.pinnedMessage != pinnedMessage || strongSelf.presentationInterfaceState.peerIsBlocked != peerIsBlocked || pinnedMessageUpdated || callsDataUpdated || strongSelf.presentationInterfaceState.slowmodeState != slowmodeState {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: strongSelf.willAppear, interactive: strongSelf.willAppear, { state in
|
||||
return state
|
||||
.updatedPinnedMessageId(pinnedMessageId)
|
||||
@ -4734,7 +4795,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})]), in: .window(.root))
|
||||
}
|
||||
} else {
|
||||
if let pinnedMessageId = strongSelf.presentationInterfaceState.pinnedMessage?.id {
|
||||
if let pinnedMessageId = strongSelf.presentationInterfaceState.pinnedMessage?.message.id {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
return $0.updatedInterfaceState({ $0.withUpdatedMessageActionsState({ value in
|
||||
var value = value
|
||||
@ -4786,7 +4847,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
return $0.updatedInterfaceState({ $0.withUpdatedMessageActionsState({ value in
|
||||
var value = value
|
||||
value.closedPinnedMessageId = pinnedMessage.id
|
||||
value.closedPinnedMessageId = pinnedMessage.message.id
|
||||
return value
|
||||
}) })
|
||||
})
|
||||
|
@ -997,7 +997,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
if let pinnedMessage = self.chatPresentationInterfaceState.pinnedMessage, self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding, self.context.sharedContext.immediateExperimentalUISettings.playlistPlayback, self.embeddedTitleContentNode == nil, let url = extractExperimentalPlaylistUrl(pinnedMessage.text), self.didProcessExperimentalEmbedUrl != url {
|
||||
if let pinnedMessage = self.chatPresentationInterfaceState.pinnedMessage, self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding, self.context.sharedContext.immediateExperimentalUISettings.playlistPlayback, self.embeddedTitleContentNode == nil, let url = extractExperimentalPlaylistUrl(pinnedMessage.message.text), self.didProcessExperimentalEmbedUrl != url {
|
||||
self.didProcessExperimentalEmbedUrl = url
|
||||
let context = self.context
|
||||
let baseNavigationController = self.controller?.navigationController as? NavigationController
|
||||
|
@ -24,6 +24,11 @@ extension ChatReplyThreadMessage {
|
||||
}
|
||||
}
|
||||
|
||||
struct ChatTopVisibleMessage: Equatable {
|
||||
var id: MessageId
|
||||
var isLast: Bool
|
||||
}
|
||||
|
||||
private class ChatHistoryListSelectionRecognizer: UIPanGestureRecognizer {
|
||||
private let selectionGestureActivationThreshold: CGFloat = 5.0
|
||||
|
||||
@ -557,6 +562,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
private var loadedMessagesFromCachedDataDisposable: Disposable?
|
||||
|
||||
let isTopReplyThreadMessageShown = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
let topVisibleMessage = ValuePromise<ChatTopVisibleMessage?>(nil, ignoreRepeated: true)
|
||||
|
||||
private let clientId: Atomic<Int32>
|
||||
|
||||
@ -1151,6 +1157,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
private func processDisplayedItemRangeChanged(displayedRange: ListViewDisplayedItemRange, transactionState: ChatHistoryTransactionOpaqueState) {
|
||||
let historyView = transactionState.historyView
|
||||
var isTopReplyThreadMessageShownValue = false
|
||||
var topVisibleMessage: ChatTopVisibleMessage?
|
||||
if let visible = displayedRange.visibleRange {
|
||||
let indexRange = (historyView.filteredEntries.count - 1 - visible.lastIndex, historyView.filteredEntries.count - 1 - visible.firstIndex)
|
||||
if indexRange.0 > indexRange.1 {
|
||||
@ -1225,6 +1232,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.effectiveTopId == message.id {
|
||||
isTopReplyThreadMessageShownValue = true
|
||||
}
|
||||
topVisibleMessage = ChatTopVisibleMessage(id: message.id, isLast: i == historyView.filteredEntries.count - 1)
|
||||
case let .MessageGroupEntry(_, messages, _):
|
||||
for (message, _, _, _) in messages {
|
||||
var hasUnconsumedMention = false
|
||||
@ -1255,6 +1263,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.effectiveTopId == message.id {
|
||||
isTopReplyThreadMessageShownValue = true
|
||||
}
|
||||
topVisibleMessage = ChatTopVisibleMessage(id: message.id, isLast: i == historyView.filteredEntries.count - 1)
|
||||
}
|
||||
default:
|
||||
break
|
||||
@ -1371,6 +1380,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
}
|
||||
}
|
||||
self.isTopReplyThreadMessageShown.set(isTopReplyThreadMessageShownValue)
|
||||
self.topVisibleMessage.set(topVisibleMessage)
|
||||
|
||||
if let loaded = displayedRange.loadedRange, let firstEntry = historyView.filteredEntries.first, let lastEntry = historyView.filteredEntries.last {
|
||||
if loaded.firstIndex < 5 && historyView.originalView.laterId != nil {
|
||||
|
@ -643,7 +643,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
}
|
||||
|
||||
if data.canPin, case .peer = chatPresentationInterfaceState.chatLocation {
|
||||
if chatPresentationInterfaceState.pinnedMessage?.id != messages[0].id {
|
||||
if chatPresentationInterfaceState.pinnedMessage?.message.id != messages[0].id {
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Pin, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Pin"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
|
@ -145,7 +145,7 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
|
||||
}
|
||||
|
||||
let inlineBots: Signal<[(Peer, Double)], NoError> = types.contains(.contextBots) ? recentlyUsedInlineBots(postbox: context.account.postbox) : .single([])
|
||||
let participants = combineLatest(inlineBots, searchPeerMembers(context: context, peerId: peer.id, chatLocation: chatLocation, query: query))
|
||||
let participants = combineLatest(inlineBots, searchPeerMembers(context: context, peerId: peer.id, chatLocation: chatLocation, query: query, scope: .mention))
|
||||
|> map { inlineBots, peers -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
||||
let filteredInlineBots = inlineBots.sorted(by: { $0.1 > $1.1 }).filter { peer, rating in
|
||||
if rating < 0.14 {
|
||||
@ -347,7 +347,7 @@ func searchQuerySuggestionResultStateForChatInterfacePresentationState(_ chatPre
|
||||
}
|
||||
}
|
||||
|
||||
let participants = searchPeerMembers(context: context, peerId: peer.id, chatLocation: chatPresentationInterfaceState.chatLocation, query: query)
|
||||
let participants = searchPeerMembers(context: context, peerId: peer.id, chatLocation: chatPresentationInterfaceState.chatLocation, query: query, scope: .memberSuggestion)
|
||||
|> map { peers -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
||||
let filteredPeers = peers
|
||||
var sortedPeers: [Peer] = []
|
||||
|
@ -19,7 +19,7 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat
|
||||
loop: for context in chatPresentationInterfaceState.titlePanelContexts.reversed() {
|
||||
switch context {
|
||||
case .pinnedMessage:
|
||||
if let pinnedMessage = chatPresentationInterfaceState.pinnedMessage, pinnedMessage.id != chatPresentationInterfaceState.interfaceState.messageActionsState.closedPinnedMessageId {
|
||||
if let pinnedMessage = chatPresentationInterfaceState.pinnedMessage, pinnedMessage.message.id != chatPresentationInterfaceState.interfaceState.messageActionsState.closedPinnedMessageId {
|
||||
selectedContext = context
|
||||
break loop
|
||||
}
|
||||
|
@ -13,10 +13,18 @@ import StickerResources
|
||||
import PhotoResources
|
||||
import TelegramStringFormatting
|
||||
|
||||
private enum PinnedMessageAnimation {
|
||||
case slideToTop
|
||||
case slideToBottom
|
||||
}
|
||||
|
||||
final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
private let context: AccountContext
|
||||
private let tapButton: HighlightTrackingButtonNode
|
||||
private let closeButton: HighlightableButtonNode
|
||||
|
||||
private let clippingContainer: ASDisplayNode
|
||||
private let contentContainer: ASDisplayNode
|
||||
private let lineNode: ASImageNode
|
||||
private let titleNode: TextNode
|
||||
private let textNode: TextNode
|
||||
@ -25,7 +33,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
private let separatorNode: ASDisplayNode
|
||||
|
||||
private var currentLayout: (CGFloat, CGFloat, CGFloat)?
|
||||
private var currentMessage: Message?
|
||||
private var currentMessage: ChatPinnedMessage?
|
||||
private var previousMediaReference: AnyMediaReference?
|
||||
|
||||
private var isReplyThread: Bool = false
|
||||
@ -46,6 +54,11 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
self.clippingContainer = ASDisplayNode()
|
||||
self.clippingContainer.clipsToBounds = true
|
||||
|
||||
self.contentContainer = ASDisplayNode()
|
||||
|
||||
self.lineNode = ASImageNode()
|
||||
self.lineNode.displayWithoutProcessing = true
|
||||
self.lineNode.displaysAsynchronously = false
|
||||
@ -87,10 +100,12 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside])
|
||||
self.addSubnode(self.closeButton)
|
||||
|
||||
self.addSubnode(self.clippingContainer)
|
||||
self.clippingContainer.addSubnode(self.contentContainer)
|
||||
self.addSubnode(self.lineNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.imageNode)
|
||||
self.contentContainer.addSubnode(self.titleNode)
|
||||
self.contentContainer.addSubnode(self.textNode)
|
||||
self.contentContainer.addSubnode(self.imageNode)
|
||||
|
||||
self.tapButton.addTarget(self, action: #selector(self.tapped), forControlEvents: [.touchUpInside])
|
||||
self.addSubnode(self.tapButton)
|
||||
@ -128,10 +143,18 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.closeButton.isHidden = isReplyThread
|
||||
|
||||
var messageUpdated = false
|
||||
var messageUpdatedAnimation: PinnedMessageAnimation?
|
||||
if let currentMessage = self.currentMessage, let pinnedMessage = interfaceState.pinnedMessage {
|
||||
if currentMessage.id != pinnedMessage.id || currentMessage.stableVersion != pinnedMessage.stableVersion {
|
||||
if currentMessage != pinnedMessage {
|
||||
messageUpdated = true
|
||||
}
|
||||
if currentMessage.message.id != pinnedMessage.message.id {
|
||||
if currentMessage.message.id < pinnedMessage.message.id {
|
||||
messageUpdatedAnimation = .slideToTop
|
||||
} else {
|
||||
messageUpdatedAnimation = .slideToBottom
|
||||
}
|
||||
}
|
||||
} else if (self.currentMessage != nil) != (interfaceState.pinnedMessage != nil) {
|
||||
messageUpdated = true
|
||||
}
|
||||
@ -141,7 +164,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.currentMessage = interfaceState.pinnedMessage
|
||||
|
||||
if let currentMessage = currentMessage, let currentLayout = self.currentLayout {
|
||||
self.enqueueTransition(width: currentLayout.0, leftInset: currentLayout.1, rightInset: currentLayout.2, transition: .immediate, message: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, accountPeerId: self.context.account.peerId, firstTime: previousMessageWasNil, isReplyThread: isReplyThread)
|
||||
self.enqueueTransition(width: currentLayout.0, leftInset: currentLayout.1, rightInset: currentLayout.2, transition: .immediate, animation: messageUpdatedAnimation, pinnedMessage: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, accountPeerId: self.context.account.peerId, firstTime: previousMessageWasNil, isReplyThread: isReplyThread)
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,18 +179,43 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))
|
||||
self.tapButton.frame = CGRect(origin: CGPoint(), size: CGSize(width: width - rightInset - closeButtonSize.width - 4.0, height: panelHeight))
|
||||
|
||||
self.clippingContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight))
|
||||
self.contentContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight))
|
||||
|
||||
if self.currentLayout?.0 != width || self.currentLayout?.1 != leftInset || self.currentLayout?.2 != rightInset {
|
||||
self.currentLayout = (width, leftInset, rightInset)
|
||||
|
||||
if let currentMessage = self.currentMessage {
|
||||
self.enqueueTransition(width: width, leftInset: leftInset, rightInset: rightInset, transition: .immediate, message: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, accountPeerId: interfaceState.accountPeerId, firstTime: true, isReplyThread: isReplyThread)
|
||||
self.enqueueTransition(width: width, leftInset: leftInset, rightInset: rightInset, transition: .immediate, animation: .none, pinnedMessage: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, accountPeerId: interfaceState.accountPeerId, firstTime: true, isReplyThread: isReplyThread)
|
||||
}
|
||||
}
|
||||
|
||||
return panelHeight
|
||||
}
|
||||
|
||||
private func enqueueTransition(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, message: Message, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, accountPeerId: PeerId, firstTime: Bool, isReplyThread: Bool) {
|
||||
private func enqueueTransition(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, animation: PinnedMessageAnimation?, pinnedMessage: ChatPinnedMessage, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, accountPeerId: PeerId, firstTime: Bool, isReplyThread: Bool) {
|
||||
let message = pinnedMessage.message
|
||||
|
||||
if let animation = animation {
|
||||
let offset: CGFloat
|
||||
switch animation {
|
||||
case .slideToTop:
|
||||
offset = -40.0
|
||||
case .slideToBottom:
|
||||
offset = 40.0
|
||||
}
|
||||
if let copyView = self.contentContainer.view.snapshotView(afterScreenUpdates: false) {
|
||||
copyView.frame = self.contentContainer.frame
|
||||
self.clippingContainer.view.addSubview(copyView)
|
||||
copyView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: offset), duration: 0.2, removeOnCompletion: false, additive: true)
|
||||
copyView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak copyView] _ in
|
||||
copyView?.removeFromSuperview()
|
||||
})
|
||||
self.contentContainer.layer.animatePosition(from: CGPoint(x: 0.0, y: -offset), to: CGPoint(), duration: 0.2, additive: true)
|
||||
self.contentContainer.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeTextLayout = TextNode.asyncLayout(self.textNode)
|
||||
let imageNodeLayout = self.imageNode.asyncLayout()
|
||||
@ -191,7 +239,12 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
var updatedMediaReference: AnyMediaReference?
|
||||
var imageDimensions: CGSize?
|
||||
|
||||
var titleString = strings.Conversation_PinnedMessage
|
||||
var titleString: String
|
||||
if pinnedMessage.isLatest {
|
||||
titleString = strings.Conversation_PinnedMessage
|
||||
} else {
|
||||
titleString = strings.Conversation_PinnedPreviousMessage
|
||||
}
|
||||
|
||||
for media in message.media {
|
||||
if let image = media as? TelegramMediaImage {
|
||||
@ -300,13 +353,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
if let interfaceInteraction = self.interfaceInteraction, let message = self.currentMessage {
|
||||
if self.isReplyThread {
|
||||
interfaceInteraction.scrollToTop()
|
||||
/*if let sourceReference = message.sourceReference {
|
||||
interfaceInteraction.navigateToMessage(sourceReference.messageId, true)
|
||||
} else {
|
||||
interfaceInteraction.navigateToMessage(message.id, false)
|
||||
}*/
|
||||
} else {
|
||||
interfaceInteraction.navigateToMessage(message.id, false)
|
||||
interfaceInteraction.navigateToMessage(message.message.id, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -257,6 +257,32 @@ struct ChatSlowmodeState: Equatable {
|
||||
var variant: ChatSlowmodeVariant
|
||||
}
|
||||
|
||||
final class ChatPinnedMessage: Equatable {
|
||||
let message: Message
|
||||
let isLatest: Bool
|
||||
|
||||
init(message: Message, isLatest: Bool) {
|
||||
self.message = message
|
||||
self.isLatest = isLatest
|
||||
}
|
||||
|
||||
static func ==(lhs: ChatPinnedMessage, rhs: ChatPinnedMessage) -> Bool {
|
||||
if lhs === rhs {
|
||||
return true
|
||||
}
|
||||
if lhs.message.id != rhs.message.id {
|
||||
return false
|
||||
}
|
||||
if lhs.message.stableVersion != rhs.message.stableVersion {
|
||||
return false
|
||||
}
|
||||
if lhs.isLatest != rhs.isLatest {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
final class ChatPresentationInterfaceState: Equatable {
|
||||
let interfaceState: ChatInterfaceState
|
||||
let chatLocation: ChatLocation
|
||||
@ -274,7 +300,7 @@ final class ChatPresentationInterfaceState: Equatable {
|
||||
let titlePanelContexts: [ChatTitlePanelContext]
|
||||
let keyboardButtonsMessage: Message?
|
||||
let pinnedMessageId: MessageId?
|
||||
let pinnedMessage: Message?
|
||||
let pinnedMessage: ChatPinnedMessage?
|
||||
let peerIsBlocked: Bool
|
||||
let peerIsMuted: Bool
|
||||
let peerDiscussionId: PeerId?
|
||||
@ -348,7 +374,7 @@ final class ChatPresentationInterfaceState: Equatable {
|
||||
self.peerNearbyData = peerNearbyData
|
||||
}
|
||||
|
||||
init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: Message?, peerIsBlocked: Bool, peerIsMuted: Bool, peerDiscussionId: PeerId?, peerGeoLocation: PeerGeoLocation?, callsAvailable: Bool, callsPrivate: Bool, slowmodeState: ChatSlowmodeState?, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, hasScheduledMessages: Bool, isScheduledMessages: Bool, peerNearbyData: ChatPeerNearbyData?) {
|
||||
init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: ChatPinnedMessage?, peerIsBlocked: Bool, peerIsMuted: Bool, peerDiscussionId: PeerId?, peerGeoLocation: PeerGeoLocation?, callsAvailable: Bool, callsPrivate: Bool, slowmodeState: ChatSlowmodeState?, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, hasScheduledMessages: Bool, isScheduledMessages: Bool, peerNearbyData: ChatPeerNearbyData?) {
|
||||
self.interfaceState = interfaceState
|
||||
self.chatLocation = chatLocation
|
||||
self.renderedPeer = renderedPeer
|
||||
@ -447,14 +473,7 @@ final class ChatPresentationInterfaceState: Equatable {
|
||||
if lhs.pinnedMessageId != rhs.pinnedMessageId {
|
||||
return false
|
||||
}
|
||||
if let lhsMessage = lhs.pinnedMessage, let rhsMessage = rhs.pinnedMessage {
|
||||
if lhsMessage.id != rhsMessage.id {
|
||||
return false
|
||||
}
|
||||
if lhsMessage.stableVersion != rhsMessage.stableVersion {
|
||||
return false
|
||||
}
|
||||
} else if (lhs.pinnedMessage != nil) != (rhs.pinnedMessage != nil) {
|
||||
if lhs.pinnedMessage != rhs.pinnedMessage {
|
||||
return false
|
||||
}
|
||||
if lhs.callsAvailable != rhs.callsAvailable {
|
||||
@ -613,7 +632,7 @@ final class ChatPresentationInterfaceState: Equatable {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages, peerNearbyData: self.peerNearbyData)
|
||||
}
|
||||
|
||||
func updatedPinnedMessage(_ pinnedMessage: Message?) -> ChatPresentationInterfaceState {
|
||||
func updatedPinnedMessage(_ pinnedMessage: ChatPinnedMessage?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages, peerNearbyData: self.peerNearbyData)
|
||||
}
|
||||
|
||||
|
Binary file not shown.
@ -449,12 +449,12 @@ public final class WalletStrings: Equatable {
|
||||
public var Wallet_Send_ConfirmationConfirm: String { return self._s[218]! }
|
||||
public var Wallet_Created_ExportErrorTitle: String { return self._s[219]! }
|
||||
public var Wallet_Info_TransactionPendingHeader: String { return self._s[220]! }
|
||||
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
||||
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
||||
let form = getPluralizationForm(self.lc, value)
|
||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
|
||||
}
|
||||
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
||||
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
||||
let form = getPluralizationForm(self.lc, value)
|
||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
|
||||
|
Loading…
x
Reference in New Issue
Block a user