Merge commit '838c352971dd8a75be5940a351eab0ff6a97c4c4' into macos-9.5-release

This commit is contained in:
Mike Renoir 2023-03-09 11:49:56 +04:00
commit 58bde45d5e
11 changed files with 408 additions and 121 deletions

View File

@ -9052,3 +9052,8 @@ Sorry for the inconvenience.";
"Login.Email.PremiumRequiredText" = "Due to high cost of SMS in your country, you need to have a **Telegram Premium** account to reset this email via an SMS code. You can ask a friend to a gift a Premium subscription for your account %@";
"ChatList.StartMessaging" = "Select a chat to start messaging";
"Conversation.ForwardOptions.RecipientsMessageForwardVisible" = "Recipients will see that it was forwarded";
"Conversation.ForwardOptions.RecipientsMessageForwardHidden" = "Recipients won't see that it was forwarded";
"Conversation.ForwardOptions.RecipientsMessagesForwardVisible" = "Recipients will see they were forwarded";
"Conversation.ForwardOptions.RecipientsMessagesForwardHidden" = "Recipients won't see they were forwarded";

View File

@ -489,7 +489,7 @@ public enum ChatControllerSubject: Equatable {
case message(id: MessageSubject, highlight: Bool, timecode: Double?)
case scheduledMessages
case pinnedMessages(id: EngineMessage.Id?)
case forwardedMessages(ids: [EngineMessage.Id], options: Signal<ForwardOptions, NoError>)
case forwardedMessages(peerIds: [EnginePeer.Id], ids: [EngineMessage.Id], options: Signal<ForwardOptions, NoError>)
public static func ==(lhs: ChatControllerSubject, rhs: ChatControllerSubject) -> Bool {
switch lhs {
@ -511,8 +511,8 @@ public enum ChatControllerSubject: Equatable {
} else {
return false
}
case let .forwardedMessages(lhsIds, _):
if case let .forwardedMessages(rhsIds, _) = rhs, lhsIds == rhsIds {
case let .forwardedMessages(lhsPeerIds, lhsIds, _):
if case let .forwardedMessages(rhsPeerIds, rhsIds, _) = rhs, lhsPeerIds == rhsPeerIds, lhsIds == rhsIds {
return true
} else {
return false

View File

@ -4606,7 +4606,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return message?.totalCount
}
|> distinctUntilChanged
} else if case let .forwardedMessages(messageIds, options) = subject {
} else if case let .forwardedMessages(peerIds, messageIds, options) = subject {
displayedCountSignal = self.presentationInterfaceStatePromise.get()
|> map { state -> Int? in
if let selectionState = state.interfaceState.selectionState {
@ -4617,50 +4617,71 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
|> distinctUntilChanged
subtitleTextSignal = combineLatest(self.presentationInterfaceStatePromise.get(), options, displayedCountSignal)
|> map { state, options, count in
if let peer = state.renderedPeer?.chatMainPeer {
if let peer = peer as? TelegramUser {
let displayName = EnginePeer(peer).compactDisplayTitle
if count == 1 {
if options.hideNames {
return state.strings.Conversation_ForwardOptions_UserMessageForwardHidden(displayName).string
let peers = self.context.account.postbox.multiplePeersView(peerIds)
|> take(1)
let presentationData = self.presentationData
subtitleTextSignal = combineLatest(peers, options, displayedCountSignal)
|> map { peersView, options, count in
let peers = peersView.peers.values
if !peers.isEmpty {
if peers.count == 1, let peer = peers.first {
if let peer = peer as? TelegramUser {
let displayName = EnginePeer(peer).compactDisplayTitle
if count == 1 {
if options.hideNames {
return presentationData.strings.Conversation_ForwardOptions_UserMessageForwardHidden(displayName).string
} else {
return presentationData.strings.Conversation_ForwardOptions_UserMessageForwardVisible(displayName).string
}
} else {
return state.strings.Conversation_ForwardOptions_UserMessageForwardVisible(displayName).string
if options.hideNames {
return presentationData.strings.Conversation_ForwardOptions_UserMessagesForwardHidden(displayName).string
} else {
return presentationData.strings.Conversation_ForwardOptions_UserMessagesForwardVisible(displayName).string
}
}
} else if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
if count == 1 {
if options.hideNames {
return presentationData.strings.Conversation_ForwardOptions_ChannelMessageForwardHidden
} else {
return presentationData.strings.Conversation_ForwardOptions_ChannelMessageForwardVisible
}
} else {
if options.hideNames {
return presentationData.strings.Conversation_ForwardOptions_ChannelMessagesForwardHidden
} else {
return presentationData.strings.Conversation_ForwardOptions_ChannelMessagesForwardVisible
}
}
} else {
if options.hideNames {
return state.strings.Conversation_ForwardOptions_UserMessagesForwardHidden(displayName).string
if count == 1 {
if options.hideNames {
return presentationData.strings.Conversation_ForwardOptions_GroupMessageForwardHidden
} else {
return presentationData.strings.Conversation_ForwardOptions_GroupMessageForwardVisible
}
} else {
return state.strings.Conversation_ForwardOptions_UserMessagesForwardVisible(displayName).string
}
}
} else if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
if count == 1 {
if options.hideNames {
return state.strings.Conversation_ForwardOptions_ChannelMessageForwardHidden
} else {
return state.strings.Conversation_ForwardOptions_ChannelMessageForwardVisible
}
} else {
if options.hideNames {
return state.strings.Conversation_ForwardOptions_ChannelMessagesForwardHidden
} else {
return state.strings.Conversation_ForwardOptions_ChannelMessagesForwardVisible
if options.hideNames {
return presentationData.strings.Conversation_ForwardOptions_GroupMessagesForwardHidden
} else {
return presentationData.strings.Conversation_ForwardOptions_GroupMessagesForwardVisible
}
}
}
} else {
if count == 1 {
if options.hideNames {
return state.strings.Conversation_ForwardOptions_GroupMessageForwardHidden
return presentationData.strings.Conversation_ForwardOptions_RecipientsMessageForwardHidden
} else {
return state.strings.Conversation_ForwardOptions_GroupMessageForwardVisible
return presentationData.strings.Conversation_ForwardOptions_RecipientsMessageForwardVisible
}
} else {
if options.hideNames {
return state.strings.Conversation_ForwardOptions_GroupMessagesForwardHidden
return presentationData.strings.Conversation_ForwardOptions_RecipientsMessagesForwardHidden
} else {
return state.strings.Conversation_ForwardOptions_GroupMessagesForwardVisible
return presentationData.strings.Conversation_ForwardOptions_RecipientsMessagesForwardVisible
}
}
}
@ -5948,6 +5969,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.chatTitleView?.networkState = state
}
})
if case let .forwardedMessages(_, messageIds, _) = self.subject, messageIds.count > 1 {
self.updateChatPresentationInterfaceState(interactive: false, { state in
return state.updatedInterfaceState({ $0.withUpdatedSelectedMessages(messageIds) })
})
}
}
required public init(coder aDecoder: NSCoder) {
@ -8100,7 +8127,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|> distinctUntilChanged
}
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .forwardedMessages(ids: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], options: forwardOptions), botStart: nil, mode: .standard(previewing: true))
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .forwardedMessages(peerIds: [peerId], ids: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], options: forwardOptions), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let messageIds = strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? []
@ -8132,7 +8159,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if !uniquePeerIds.contains(author.id) {
uniquePeerIds.insert(author.id)
}
if message.id.peerId == accountPeerId && message.forwardInfo == nil {
} else {
hasNotOwnMessages = true
@ -10272,7 +10298,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
strongSelf.presentAttachmentPremiumGift()
let _ = ApplicationSpecificNotice.incrementDismissedPremiumGiftSuggestion(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId).start()
Queue.mainQueue().after(0.5) {
let _ = ApplicationSpecificNotice.incrementDismissedPremiumGiftSuggestion(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId).start()
}
}, requestLayout: { [weak self] transition in
if let strongSelf = self, let layout = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, transition: transition)
@ -10633,12 +10661,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
if case let .forwardedMessages(messageIds, _) = self.subject, messageIds.count > 1 {
self.updateChatPresentationInterfaceState(interactive: false, { state in
return state.updatedInterfaceState({ $0.withUpdatedSelectedMessages(messageIds) })
})
}
self.displayNodeDidLoad()
}
@ -18509,7 +18531,7 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent
let sourceRect: CGRect?
let navigationController: NavigationController? = nil
let passthroughTouches: Bool
init(controller: ViewController, sourceNode: ASDisplayNode?, sourceRect: CGRect? = nil, passthroughTouches: Bool) {

View File

@ -336,7 +336,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.inputContextOverTextPanelContainer = ChatControllerTitlePanelNodeContainer()
var source: ChatHistoryListSource
if case let .forwardedMessages(messageIds, options) = subject {
if case let .forwardedMessages(_, messageIds, options) = subject {
let messages = combineLatest(context.account.postbox.messagesAtIds(messageIds), context.account.postbox.loadedPeerWithId(context.account.peerId), options)
|> map { messages, accountPeer, options -> ([Message], Int32, Bool) in
var messages = messages

View File

@ -15,6 +15,7 @@ func chatHistoryEntriesForView(
includeSearchEntry: Bool,
reverse: Bool,
groupMessages: Bool,
reverseGroupedMessages: Bool,
selectedMessages: Set<MessageId>?,
presentationData: ChatPresentationData,
historyAppearsCleared: Bool,
@ -154,9 +155,18 @@ func chatHistoryEntriesForView(
}
}
if groupMessages {
if groupMessages || reverseGroupedMessages {
if !groupBucket.isEmpty && message.groupInfo != groupBucket[0].0.groupInfo {
entries.append(.MessageGroupEntry(groupBucket[0].0.groupInfo!, groupBucket, presentationData))
if reverseGroupedMessages {
groupBucket.reverse()
}
if groupMessages {
entries.append(.MessageGroupEntry(groupBucket[0].0.groupInfo!, groupBucket, presentationData))
} else {
for (message, isRead, selection, attributes, location) in groupBucket {
entries.append(.MessageEntry(message, presentationData, isRead, location, selection, attributes))
}
}
groupBucket.removeAll()
}
if let _ = message.groupInfo {
@ -188,8 +198,17 @@ func chatHistoryEntriesForView(
}
if !groupBucket.isEmpty {
assert(groupMessages)
entries.append(.MessageGroupEntry(groupBucket[0].0.groupInfo!, groupBucket, presentationData))
assert(groupMessages || reverseGroupedMessages)
if reverseGroupedMessages {
groupBucket.reverse()
}
if groupMessages {
entries.append(.MessageGroupEntry(groupBucket[0].0.groupInfo!, groupBucket, presentationData))
} else {
for (message, isRead, selection, attributes, location) in groupBucket {
entries.append(.MessageEntry(message, presentationData, isRead, location, selection, attributes))
}
}
}
if let maybeJoinMessage = joinMessage, !view.holeLater {

View File

@ -47,7 +47,7 @@ public enum ChatHistoryListDisplayHeaders {
public enum ChatHistoryListMode: Equatable {
case bubbles
case list(search: Bool, reversed: Bool, displayHeaders: ChatHistoryListDisplayHeaders, hintLinks: Bool, isGlobalSearch: Bool)
case list(search: Bool, reversed: Bool, reverseGroups: Bool, displayHeaders: ChatHistoryListDisplayHeaders, hintLinks: Bool, isGlobalSearch: Bool)
}
enum ChatHistoryViewScrollPosition {
@ -224,7 +224,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, location: location))
case let .list(_, _, displayHeaders, hintLinks, isGlobalSearch):
case let .list(_, _, _, displayHeaders, hintLinks, isGlobalSearch):
let displayHeader: Bool
switch displayHeaders {
case .none:
@ -269,7 +269,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, location: location))
case let .list(_, _, displayHeaders, hintLinks, isGlobalSearch):
case let .list(_, _, _, displayHeaders, hintLinks, isGlobalSearch):
let displayHeader: Bool
switch displayHeaders {
case .none:
@ -1216,10 +1216,12 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
var updatedScrollPosition = scrollPosition
var reverse = false
var reverseGroups = false
var includeSearchEntry = false
if case let .list(search, reverseValue, _, _, _) = mode {
if case let .list(search, reverseValue, reverseGroupsValue, _, _, _) = mode {
includeSearchEntry = search
reverse = reverseValue
reverseGroups = reverseGroupsValue
}
var isCopyProtectionEnabled: Bool = data.initialData?.peer?.isCopyProtectionEnabled ?? false
@ -1265,6 +1267,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
includeSearchEntry: includeSearchEntry && tagMask != nil,
reverse: reverse,
groupMessages: mode == .bubbles,
reverseGroupedMessages: reverseGroups,
selectedMessages: selectedMessages,
presentationData: chatPresentationData,
historyAppearsCleared: historyAppearsCleared,
@ -3465,7 +3468,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, location: location))
case let .list(_, _, displayHeaders, hintLinks, isGlobalSearch):
case let .list(_, _, _, displayHeaders, hintLinks, isGlobalSearch):
let displayHeader: Bool
switch displayHeaders {
case .none:
@ -3521,7 +3524,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, location: location))
case let .list(_, _, displayHeaders, hintLinks, isGlobalSearch):
case let .list(_, _, _, displayHeaders, hintLinks, isGlobalSearch):
let displayHeader: Bool
switch displayHeaders {
case .none:

View File

@ -640,7 +640,14 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} else if self.emojiFile?.id != emojiFile?.id {
if self.emojiFile != nil {
self.didSetUpAnimationNode = false
item.controllerInteraction.seenOneTimeAnimatedMedia.remove(item.message.id)
item.controllerInteraction.seenOneTimeAnimatedMedia.remove(item.message.id)
self.animationNode?.removeFromSupernode()
self.animationNode = nil
self.contextSourceNode.contentNode.insertSubnode(self.placeholderNode, aboveSubnode: self.imageNode)
self.setupNode(item: item)
}
self.emojiFile = emojiFile
if let emojiFile = emojiFile {

View File

@ -208,7 +208,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
self.isGlobalSearch = false
}
self.historyNode = ChatHistoryListNode(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, source: source, subject: .message(id: .id(initialMessageId), highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch))
self.historyNode = ChatHistoryListNode(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, source: source, subject: .message(id: .id(initialMessageId), highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch))
self.historyNode.clipsToBounds = true
super.init()
@ -550,7 +550,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
let historyNode = ChatHistoryListNode(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: self.chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: .message(id: .id(messageId), highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch))
let historyNode = ChatHistoryListNode(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: self.chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: .message(id: .id(messageId), highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch))
historyNode.clipsToBounds = true
historyNode.preloadPages = true
historyNode.stackFromBottom = true

View File

@ -76,7 +76,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
self.selectedMessages = chatControllerInteraction.selectionState.flatMap { $0.selectedIds }
self.selectedMessagesPromise.set(.single(self.selectedMessages))
self.listNode = ChatHistoryListNode(context: context, updatedPresentationData: updatedPresentationData ?? (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, 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 = ChatHistoryListNode(context: context, updatedPresentationData: updatedPresentationData ?? (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false, reverseGroups: false, displayHeaders: .allButLast, hintLinks: tagMask == .webPage, isGlobalSearch: false))
self.listNode.clipsToBounds = true
self.listNode.defaultToSynchronousTransactionWhileScrolling = true
self.listNode.scroller.bounces = false

View File

@ -229,7 +229,7 @@ private func navigatedMessageFromMessages(_ messages: [Message], anchorIndex: Me
}
}
private func navigatedMessageFromView(_ view: MessageHistoryView, anchorIndex: MessageIndex, position: NavigatedMessageFromViewPosition) -> (message: Message, around: [Message], exact: Bool)? {
private func navigatedMessageFromView(_ view: MessageHistoryView, anchorIndex: MessageIndex, position: NavigatedMessageFromViewPosition, reversed: Bool) -> (message: Message, around: [Message], exact: Bool)? {
var index = 0
for entry in view.entries {
@ -240,7 +240,7 @@ private func navigatedMessageFromView(_ view: MessageHistoryView, anchorIndex: M
case .exact:
return (entry.message, aroundMessagesFromView(view: view, centralIndex: entry.index), true)
case .later:
if let currentGroupKey {
if !reversed, let currentGroupKey {
if index - 1 > 0, view.entries[index - 1].message.groupingKey == currentGroupKey {
let message = view.entries[index - 1].message
return (message, aroundMessagesFromView(view: view, centralIndex: view.entries[index - 1].index), true)
@ -259,21 +259,34 @@ private func navigatedMessageFromView(_ view: MessageHistoryView, anchorIndex: M
return nil
}
case .earlier:
if let currentGroupKey {
if !reversed, let currentGroupKey {
if index + 1 < view.entries.count, view.entries[index + 1].message.groupingKey == currentGroupKey {
let message = view.entries[index + 1].message
return (message, aroundMessagesFromView(view: view, centralIndex: view.entries[index + 1].index), true)
} else {
var nextGroupingKey: Int64?
for i in (0 ..< index).reversed() {
if view.entries[i].message.groupingKey != currentGroupKey {
let message = view.entries[i].message
return (message, aroundMessagesFromView(view: view, centralIndex: view.entries[i].index), true)
if let nextGroupingKey {
if view.entries[i].message.groupingKey != nextGroupingKey {
let message = view.entries[i + 1].message
return (message, aroundMessagesFromView(view: view, centralIndex: view.entries[i + 1].index), true)
} else if i == 0 {
let message = view.entries[i].message
return (message, aroundMessagesFromView(view: view, centralIndex: view.entries[i].index), true)
}
} else if view.entries[i].message.groupingKey != currentGroupKey {
if let groupingKey = view.entries[i].message.groupingKey {
nextGroupingKey = groupingKey
} else {
let message = view.entries[i].message
return (message, aroundMessagesFromView(view: view, centralIndex: view.entries[i].index), true)
}
}
}
}
} else if index != 0 {
let message = view.entries[index - 1].message
if let nextGroupingKey = message.groupingKey {
if !reversed, let nextGroupingKey = message.groupingKey {
for i in (0 ..< index).reversed() {
if view.entries[i].message.groupingKey != nextGroupingKey {
let message = view.entries[i + 1].message
@ -397,7 +410,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
switch self.messagesLocation {
case let .messages(_, _, messageId), let .singleMessage(messageId), let .custom(_, messageId, _):
self.loadItem(anchor: .messageId(messageId), navigation: .later)
self.loadItem(anchor: .messageId(messageId), navigation: .later, reversed: self.order == .reversed)
case let .recentActions(message):
self.loadingItem = false
self.currentItem = (message, [])
@ -450,7 +463,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
self.currentItem = nil
self.updateState()
} else {
self.loadItem(anchor: .index(currentItem.current.index), navigation: navigation)
self.loadItem(anchor: .index(currentItem.current.index), navigation: navigation, reversed: self.order == .reversed)
}
}
}
@ -511,7 +524,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
}
}
private func loadItem(anchor: PeerMessagesMediaPlaylistLoadAnchor, navigation: PeerMessagesMediaPlaylistNavigation) {
private func loadItem(anchor: PeerMessagesMediaPlaylistLoadAnchor, navigation: PeerMessagesMediaPlaylistNavigation, reversed: Bool) {
self.loadingItem = true
self.updateState()
@ -535,7 +548,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic<ChatLocationContextHolder?>(value: nil)), anchor: .index(message.index), ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: [])
|> mapToSignal { view -> Signal<(Message, [Message])?, NoError> in
if let (message, aroundMessages, _) = navigatedMessageFromView(view.0, anchorIndex: message.index, position: .exact) {
if let (message, aroundMessages, _) = navigatedMessageFromView(view.0, anchorIndex: message.index, position: .exact, reversed: reversed) {
return .single((message, aroundMessages))
} else {
return .single((message, []))
@ -657,7 +670,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
position = .exact
}
if let (message, aroundMessages, exact) = navigatedMessageFromView(view.0, anchorIndex: inputIndex, position: position) {
if let (message, aroundMessages, exact) = navigatedMessageFromView(view.0, anchorIndex: inputIndex, position: position, reversed: reversed) {
switch navigation {
case .random:
return .single((message, []))
@ -684,7 +697,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
case .earlier:
position = .later
}
if let (message, aroundMessages, _) = navigatedMessageFromView(view.0, anchorIndex: MessageIndex.absoluteLowerBound(), position: position) {
if let (message, aroundMessages, _) = navigatedMessageFromView(view.0, anchorIndex: MessageIndex.absoluteLowerBound(), position: position, reversed: reversed) {
return .single((message, aroundMessages))
} else {
return .single(nil)
@ -819,7 +832,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
}
if messages.count > previousMessagesCount {
strongSelf.loadItem(anchor: anchor, navigation: navigation)
strongSelf.loadItem(anchor: anchor, navigation: navigation, reversed: strongSelf.order == .reversed)
strongSelf.loadMoreDisposable.set(nil)
strongSelf.loadingMore = false

View File

@ -21,6 +21,7 @@ import MultiAnimationRenderer
import AnimatedStickerNode
import TelegramAnimatedStickerNode
import SolidRoundedButtonNode
import ContextUI
final class PeerSelectionControllerNode: ASDisplayNode {
private let context: AccountContext
@ -36,13 +37,11 @@ final class PeerSelectionControllerNode: ASDisplayNode {
private let requestPeerType: [ReplyMarkupButtonRequestPeerType]?
private var presentationInterfaceState: ChatPresentationInterfaceState
private let presentationInterfaceStatePromise = ValuePromise<ChatPresentationInterfaceState>()
private var interfaceInteraction: ChatPanelInterfaceInteraction?
var inProgress: Bool = false {
didSet {
}
}
var inProgress: Bool = false
var navigationBar: NavigationBar?
@ -125,7 +124,8 @@ final class PeerSelectionControllerNode: ASDisplayNode {
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(id: PeerId(0)), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil)
self.presentationInterfaceState = self.presentationInterfaceState.updatedInterfaceState { $0.withUpdatedForwardMessageIds(forwardedMessageIds) }
self.presentationInterfaceState = self.presentationInterfaceState.updatedInterfaceState { $0.withUpdatedForwardMessageIds(forwardedMessageIds).withUpdatedForwardOptionsState(ChatInterfaceForwardOptionsState(hideNames: false, hideCaptions: false, unhideNamesOnCaptionChange: false)) }
self.presentationInterfaceStatePromise.set(self.presentationInterfaceState)
if let _ = self.requestPeerType {
self.requirementsBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.rootController.navigationBar.blurredBackgroundColor)
@ -346,7 +346,190 @@ final class PeerSelectionControllerNode: ASDisplayNode {
if let strongSelf = self {
strongSelf.updateChatPresentationInterfaceState(animated: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardOptionsState($0.forwardOptionsState) }) })
}
}, presentForwardOptions: { _ in
}, presentForwardOptions: { [weak self] sourceNode in
guard let strongSelf = self else {
return
}
let presentationData = strongSelf.presentationData
let peerIds = strongSelf.selectedPeers.0.map { $0.id }
let forwardOptions: Signal<ChatControllerSubject.ForwardOptions, NoError>
forwardOptions = strongSelf.presentationInterfaceStatePromise.get()
|> map { state -> ChatControllerSubject.ForwardOptions in
return ChatControllerSubject.ForwardOptions(hideNames: state.interfaceState.forwardOptionsState?.hideNames ?? false, hideCaptions: state.interfaceState.forwardOptionsState?.hideCaptions ?? false)
}
|> distinctUntilChanged
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: strongSelf.context.account.peerId), subject: .forwardedMessages(peerIds: peerIds, ids: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], options: forwardOptions), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let messageIds = strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? []
let messagesCount: Signal<Int, NoError>
if let chatController = chatController as? ChatControllerImpl, messageIds.count > 1 {
messagesCount = .single(messageIds.count)
|> then(
chatController.presentationInterfaceStatePromise.get()
|> map { state -> Int in
return state.interfaceState.selectionState?.selectedIds.count ?? 1
}
)
} else {
messagesCount = .single(1)
}
let accountPeerId = strongSelf.context.account.peerId
let items = combineLatest(forwardOptions, strongSelf.context.account.postbox.messagesAtIds(messageIds), messagesCount)
|> map { forwardOptions, messages, messagesCount -> [ContextMenuItem] in
var items: [ContextMenuItem] = []
var hasCaptions = false
var uniquePeerIds = Set<PeerId>()
var hasOther = false
var hasNotOwnMessages = false
for message in messages {
if let author = message.effectiveAuthor {
if !uniquePeerIds.contains(author.id) {
uniquePeerIds.insert(author.id)
}
if message.id.peerId == accountPeerId && message.forwardInfo == nil {
} else {
hasNotOwnMessages = true
}
}
var isDice = false
var isMusic = false
for media in message.media {
if let media = media as? TelegramMediaFile, media.isMusic {
isMusic = true
} else if media is TelegramMediaDice {
isDice = true
} else {
if !message.text.isEmpty {
if media is TelegramMediaImage || media is TelegramMediaFile {
hasCaptions = true
}
}
}
}
if !isDice && !isMusic {
hasOther = true
}
}
let canHideNames = hasNotOwnMessages && hasOther
let hideNames = forwardOptions.hideNames
let hideCaptions = forwardOptions.hideCaptions
if !"".isEmpty { // check if seecret chat
} else {
if canHideNames {
items.append(.action(ContextMenuActionItem(text: uniquePeerIds.count == 1 ? presentationData.strings.Conversation_ForwardOptions_ShowSendersName : presentationData.strings.Conversation_ForwardOptions_ShowSendersNames, icon: { theme in
if hideNames {
return nil
} else {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}
}, action: { [weak self] _, f in
self?.interfaceInteraction?.updateForwardOptionsState({ current in
var updated = current
updated.hideNames = false
updated.hideCaptions = false
updated.unhideNamesOnCaptionChange = false
return updated
})
})))
items.append(.action(ContextMenuActionItem(text: uniquePeerIds.count == 1 ? presentationData.strings.Conversation_ForwardOptions_HideSendersName : presentationData.strings.Conversation_ForwardOptions_HideSendersNames, icon: { theme in
if hideNames {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
}
}, action: { _, f in
self?.interfaceInteraction?.updateForwardOptionsState({ current in
var updated = current
updated.hideNames = true
updated.unhideNamesOnCaptionChange = false
return updated
})
})))
items.append(.separator)
}
if hasCaptions {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_ShowCaption, icon: { theme in
if hideCaptions {
return nil
} else {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}
}, action: { [weak self] _, f in
self?.interfaceInteraction?.updateForwardOptionsState({ current in
var updated = current
updated.hideCaptions = false
if updated.unhideNamesOnCaptionChange {
updated.unhideNamesOnCaptionChange = false
updated.hideNames = false
}
return updated
})
})))
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_HideCaption, icon: { theme in
if hideCaptions {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
}
}, action: { _, f in
self?.interfaceInteraction?.updateForwardOptionsState({ current in
var updated = current
updated.hideCaptions = true
if !updated.hideNames {
updated.hideNames = true
updated.unhideNamesOnCaptionChange = true
}
return updated
})
})))
items.append(.separator)
}
}
items.append(.action(ContextMenuActionItem(text: messagesCount == 1 ? presentationData.strings.Conversation_ForwardOptions_SendMessage : presentationData.strings.Conversation_ForwardOptions_SendMessages, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.contextMenu.primaryColor) }, action: { [weak self, weak chatController] c, f in
guard let strongSelf = self else {
return
}
if let selectedMessageIds = (chatController as? ChatControllerImpl)?.selectedMessageIds {
var forwardMessageIds = strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? []
forwardMessageIds = forwardMessageIds.filter { selectedMessageIds.contains($0) }
strongSelf.updateChatPresentationInterfaceState(animated: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardMessageIds(forwardMessageIds) }) })
}
strongSelf.textInputPanelNode?.sendMessage(.generic)
f(.default)
})))
return items
}
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, passthroughTouches: true)), items: items |> map { ContextController.Items(content: .list($0)) })
contextController.dismissedForCancel = { [weak chatController] in
if let selectedMessageIds = (chatController as? ChatControllerImpl)?.selectedMessageIds {
var forwardMessageIds = strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? []
forwardMessageIds = forwardMessageIds.filter { selectedMessageIds.contains($0) }
strongSelf.updateChatPresentationInterfaceState(animated: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardMessageIds(forwardMessageIds) }) })
}
}
contextController.immediateItemsTransitionAnimation = true
strongSelf.controller?.presentInGlobalOverlay(contextController)
}, shareSelectedMessages: {
}, updateTextInputStateAndMode: { [weak self] f in
if let strongSelf = self {
@ -536,6 +719,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
let updateInputTextState = self.presentationInterfaceState.interfaceState.effectiveInputState != presentationInterfaceState.interfaceState.effectiveInputState
self.presentationInterfaceState = presentationInterfaceState
self.presentationInterfaceStatePromise.set(presentationInterfaceState)
if let textInputPanelNode = self.textInputPanelNode, updateInputTextState {
textInputPanelNode.updateInputTextState(presentationInterfaceState.interfaceState.effectiveInputState, animated: transition.isAnimated)
@ -546,6 +730,46 @@ final class PeerSelectionControllerNode: ASDisplayNode {
}
}
private var selectedPeers: ([Peer], [PeerId: Peer]) {
if self.contactListActive {
let selectedContactPeers = self.contactListNode?.selectedPeers ?? []
var selectedPeers: [Peer] = []
var selectedPeerMap: [PeerId: Peer] = [:]
for contactPeer in selectedContactPeers {
if case let .peer(peer, _, _) = contactPeer {
selectedPeers.append(peer)
selectedPeerMap[peer.id] = peer
}
}
return (selectedPeers, selectedPeerMap)
} else {
var selectedPeerIds: [PeerId] = []
var selectedPeerMap: [PeerId: Peer] = [:]
if let mainContainerNode = self.mainContainerNode {
mainContainerNode.currentItemNode.updateState { state in
selectedPeerIds = Array(state.selectedPeerIds)
selectedPeerMap = state.selectedPeerMap.mapValues({ $0._asPeer() })
return state
}
}
if let chatListNode = self.chatListNode {
chatListNode.updateState { state in
selectedPeerIds = Array(state.selectedPeerIds)
selectedPeerMap = state.selectedPeerMap.mapValues({ $0._asPeer() })
return state
}
}
var selectedPeers: [Peer] = []
for peerId in selectedPeerIds {
if let peer = selectedPeerMap[peerId] {
selectedPeers.append(peer)
}
}
return (selectedPeers, selectedPeerMap)
}
}
func beginSelection() {
if let _ = self.textInputPanelNode {
} else {
@ -566,47 +790,9 @@ final class PeerSelectionControllerNode: ASDisplayNode {
let effectiveInputText = strongSelf.presentationInterfaceState.interfaceState.composeInputState.inputText
let forwardOptionsState = strongSelf.presentationInterfaceState.interfaceState.forwardOptionsState
if strongSelf.contactListActive {
strongSelf.contactListNode?.multipleSelection = true
let selectedContactPeers = strongSelf.contactListNode?.selectedPeers ?? []
var selectedPeers: [Peer] = []
var selectedPeerMap: [PeerId: Peer] = [:]
for contactPeer in selectedContactPeers {
if case let .peer(peer, _, _) = contactPeer {
selectedPeers.append(peer)
selectedPeerMap[peer.id] = peer
}
}
if !selectedPeers.isEmpty {
strongSelf.requestSend?(selectedPeers, selectedPeerMap, effectiveInputText, mode, forwardOptionsState)
}
} else {
var selectedPeerIds: [PeerId] = []
var selectedPeerMap: [PeerId: Peer] = [:]
if let mainContainerNode = strongSelf.mainContainerNode {
mainContainerNode.currentItemNode.updateState { state in
selectedPeerIds = Array(state.selectedPeerIds)
selectedPeerMap = state.selectedPeerMap.mapValues({ $0._asPeer() })
return state
}
}
if let chatListNode = strongSelf.chatListNode {
chatListNode.updateState { state in
selectedPeerIds = Array(state.selectedPeerIds)
selectedPeerMap = state.selectedPeerMap.mapValues({ $0._asPeer() })
return state
}
}
if !selectedPeerIds.isEmpty {
var selectedPeers: [Peer] = []
for peerId in selectedPeerIds {
if let peer = selectedPeerMap[peerId] {
selectedPeers.append(peer)
}
}
strongSelf.requestSend?(selectedPeers, selectedPeerMap, effectiveInputText, mode, forwardOptionsState)
}
let (selectedPeers, selectedPeerMap) = strongSelf.selectedPeers
if !selectedPeers.isEmpty {
strongSelf.requestSend?(selectedPeers, selectedPeerMap, effectiveInputText, mode, forwardOptionsState)
}
}
self.addSubnode(textInputPanelNode)
@ -1426,3 +1612,35 @@ private func stringForRequestPeerType(strings: PresentationStrings, peerType: Re
return String(lines.joined(separator: "\n"))
}
}
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
let controller: ViewController
weak var sourceNode: ASDisplayNode?
let sourceRect: CGRect?
let navigationController: NavigationController? = nil
let passthroughTouches: Bool
init(controller: ViewController, sourceNode: ASDisplayNode?, sourceRect: CGRect? = nil, passthroughTouches: Bool) {
self.controller = controller
self.sourceNode = sourceNode
self.sourceRect = sourceRect
self.passthroughTouches = passthroughTouches
}
func transitionInfo() -> ContextControllerTakeControllerInfo? {
let sourceNode = self.sourceNode
let sourceRect = self.sourceRect
return ContextControllerTakeControllerInfo(contentAreaInScreenSpace: CGRect(origin: CGPoint(), size: CGSize(width: 10.0, height: 10.0)), sourceNode: { [weak sourceNode] in
if let sourceNode = sourceNode {
return (sourceNode.view, sourceRect ?? sourceNode.bounds)
} else {
return nil
}
})
}
func animatedIn() {
}
}