diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 9a4318b5cc..d273438545 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -5232,62 +5232,104 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } } - }, unpinMessage: { [weak self] id, askForConfirmation in - if let strongSelf = self { - if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { - if strongSelf.canManagePin() { - let action: () -> Void = { - if let strongSelf = self { - let disposable: MetaDisposable - if let current = strongSelf.unpinMessageDisposable { - disposable = current - } else { - disposable = MetaDisposable() - strongSelf.unpinMessageDisposable = disposable - } - - if askForConfirmation { - strongSelf.chatDisplayNode.historyNode.pendingUnpinnedAllMessages = true - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { - return $0.updatedPendingUnpinnedAllMessages(true) - }) - + }, unpinMessage: { [weak self] id, askForConfirmation, contextController in + let impl: () -> Void = { + guard let strongSelf = self else { + return + } + guard let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { + return + } + + if strongSelf.canManagePin() { + let action: () -> Void = { + if let strongSelf = self { + let disposable: MetaDisposable + if let current = strongSelf.unpinMessageDisposable { + disposable = current + } else { + disposable = MetaDisposable() + strongSelf.unpinMessageDisposable = disposable + } + + if askForConfirmation { + strongSelf.chatDisplayNode.historyNode.pendingUnpinnedAllMessages = true + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { + return $0.updatedPendingUnpinnedAllMessages(true) + }) + + strongSelf.present( + UndoOverlayController( + presentationData: strongSelf.presentationData, + content: .messagesUnpinned( + title: strongSelf.presentationData.strings.Chat_MessagesUnpinned(1), + text: "", + undo: askForConfirmation, + isHidden: false + ), + elevatedLayout: false, + action: { action in + switch action { + case .commit: + disposable.set((requestUpdatePinnedMessage(account: strongSelf.context.account, peerId: peer.id, update: .clear(id: id)) + |> deliverOnMainQueue).start(error: { _ in + guard let strongSelf = self else { + return + } + strongSelf.chatDisplayNode.historyNode.pendingUnpinnedAllMessages = false + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { + return $0.updatedPendingUnpinnedAllMessages(false) + }) + }, completed: { + guard let strongSelf = self else { + return + } + strongSelf.chatDisplayNode.historyNode.pendingUnpinnedAllMessages = false + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { + return $0.updatedPendingUnpinnedAllMessages(false) + }) + })) + case .undo: + strongSelf.chatDisplayNode.historyNode.pendingUnpinnedAllMessages = false + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { + return $0.updatedPendingUnpinnedAllMessages(false) + }) + default: + break + } + return true + } + ), + in: .current + ) + } else { + if case .pinnedMessages = strongSelf.presentationInterfaceState.subject { + strongSelf.chatDisplayNode.historyNode.pendingRemovedMessages.insert(id) strongSelf.present( UndoOverlayController( presentationData: strongSelf.presentationData, content: .messagesUnpinned( title: strongSelf.presentationData.strings.Chat_MessagesUnpinned(1), text: "", - undo: askForConfirmation, + undo: true, isHidden: false ), elevatedLayout: false, action: { action in + guard let strongSelf = self else { + return true + } switch action { case .commit: - disposable.set((requestUpdatePinnedMessage(account: strongSelf.context.account, peerId: peer.id, update: .clear(id: id)) - |> deliverOnMainQueue).start(error: { _ in + let _ = (requestUpdatePinnedMessage(account: strongSelf.context.account, peerId: peer.id, update: .clear(id: id)) + |> deliverOnMainQueue).start(completed: { guard let strongSelf = self else { return } - strongSelf.chatDisplayNode.historyNode.pendingUnpinnedAllMessages = false - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { - return $0.updatedPendingUnpinnedAllMessages(false) - }) - }, completed: { - guard let strongSelf = self else { - return - } - strongSelf.chatDisplayNode.historyNode.pendingUnpinnedAllMessages = false - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { - return $0.updatedPendingUnpinnedAllMessages(false) - }) - })) - case .undo: - strongSelf.chatDisplayNode.historyNode.pendingUnpinnedAllMessages = false - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { - return $0.updatedPendingUnpinnedAllMessages(false) + strongSelf.chatDisplayNode.historyNode.pendingRemovedMessages.remove(id) }) + case .undo: + strongSelf.chatDisplayNode.historyNode.pendingRemovedMessages.remove(id) default: break } @@ -5302,62 +5344,70 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } } - if askForConfirmation { - strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_UnpinMessageAlert, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Conversation_Unpin, action: { - action() - }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {})], actionLayout: .vertical), in: .window(.root)) - } else { + } + if askForConfirmation { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_UnpinMessageAlert, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Conversation_Unpin, action: { action() - } + }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {})], actionLayout: .vertical), in: .window(.root)) } else { - if let pinnedMessage = strongSelf.presentationInterfaceState.pinnedMessage { - let previousClosedPinnedMessageId = strongSelf.presentationInterfaceState.interfaceState.messageActionsState.closedPinnedMessageId - - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { - return $0.updatedInterfaceState({ $0.withUpdatedMessageActionsState({ value in - var value = value - value.closedPinnedMessageId = pinnedMessage.topMessageId - return value - }) }) - }) - strongSelf.present( - UndoOverlayController( - presentationData: strongSelf.presentationData, - content: .messagesUnpinned( - title: strongSelf.presentationData.strings.Chat_PinnedMessagesHiddenTitle, - text: strongSelf.presentationData.strings.Chat_PinnedMessagesHiddenText, - undo: true, - isHidden: false - ), - elevatedLayout: false, - action: { action in - guard let strongSelf = self else { - return true - } - switch action { - case .commit: - break - case .undo: - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { - return $0.updatedInterfaceState({ $0.withUpdatedMessageActionsState({ value in - var value = value - value.closedPinnedMessageId = previousClosedPinnedMessageId - return value - }) }) - }) - default: - break - } + action() + } + } else { + if let pinnedMessage = strongSelf.presentationInterfaceState.pinnedMessage { + let previousClosedPinnedMessageId = strongSelf.presentationInterfaceState.interfaceState.messageActionsState.closedPinnedMessageId + + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { + return $0.updatedInterfaceState({ $0.withUpdatedMessageActionsState({ value in + var value = value + value.closedPinnedMessageId = pinnedMessage.topMessageId + return value + }) }) + }) + strongSelf.present( + UndoOverlayController( + presentationData: strongSelf.presentationData, + content: .messagesUnpinned( + title: strongSelf.presentationData.strings.Chat_PinnedMessagesHiddenTitle, + text: strongSelf.presentationData.strings.Chat_PinnedMessagesHiddenText, + undo: true, + isHidden: false + ), + elevatedLayout: false, + action: { action in + guard let strongSelf = self else { return true } - ), - in: .current - ) - strongSelf.updatedClosedPinnedMessageId?(pinnedMessage.topMessageId) - } + switch action { + case .commit: + break + case .undo: + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { + return $0.updatedInterfaceState({ $0.withUpdatedMessageActionsState({ value in + var value = value + value.closedPinnedMessageId = previousClosedPinnedMessageId + return value + }) }) + }) + default: + break + } + return true + } + ), + in: .current + ) + strongSelf.updatedClosedPinnedMessageId?(pinnedMessage.topMessageId) } } } + + if let contextController = contextController { + contextController.dismiss(completion: { + impl() + }) + } else { + impl() + } }, unpinAllMessages: { [weak self] in guard let strongSelf = self else { return diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index 52274d6575..4d408b82f5 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -8,7 +8,7 @@ import AccountContext import TelegramPresentationData -func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView, includeUnreadEntry: Bool, includeEmptyEntry: Bool, includeChatInfoEntry: Bool, includeSearchEntry: Bool, reverse: Bool, groupMessages: Bool, selectedMessages: Set?, presentationData: ChatPresentationData, historyAppearsCleared: Bool, pendingUnpinnedAllMessages: Bool, associatedData: ChatMessageItemAssociatedData, updatingMedia: [MessageId: ChatUpdatingMessageMedia], customChannelDiscussionReadState: MessageId?, customThreadOutgoingReadState: MessageId?) -> [ChatHistoryEntry] { +func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView, includeUnreadEntry: Bool, includeEmptyEntry: Bool, includeChatInfoEntry: Bool, includeSearchEntry: Bool, reverse: Bool, groupMessages: Bool, selectedMessages: Set?, presentationData: ChatPresentationData, historyAppearsCleared: Bool, pendingUnpinnedAllMessages: Bool, pendingRemovedMessages: Set, associatedData: ChatMessageItemAssociatedData, updatingMedia: [MessageId: ChatUpdatingMessageMedia], customChannelDiscussionReadState: MessageId?, customThreadOutgoingReadState: MessageId?) -> [ChatHistoryEntry] { if historyAppearsCleared { return [] } @@ -34,6 +34,10 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView, var message = entry.message var isRead = entry.isRead + if pendingRemovedMessages.contains(message.id) { + continue + } + if let customThreadOutgoingReadState = customThreadOutgoingReadState { isRead = customThreadOutgoingReadState >= message.id } diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 3d8c2f6446..b78bbf8691 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -565,6 +565,15 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } } + private let pendingRemovedMessagesPromise = ValuePromise>(Set()) + var pendingRemovedMessages: Set = Set() { + didSet { + if self.pendingRemovedMessages != oldValue { + self.pendingRemovedMessagesPromise.set(self.pendingRemovedMessages) + } + } + } + private(set) var isScrollAtBottomPosition = false public var isScrollAtBottomPositionUpdated: (() -> Void)? @@ -846,10 +855,11 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { automaticDownloadNetworkType, self.historyAppearsClearedPromise.get(), self.pendingUnpinnedAllMessagesPromise.get(), + self.pendingRemovedMessagesPromise.get(), animatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState - ).start(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, historyAppearsCleared, pendingUnpinnedAllMessages, animatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState in + ).start(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, animatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState in func applyHole() { Queue.mainQueue().async { if let strongSelf = self { @@ -930,7 +940,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, subject: subject) - let filteredEntries = chatHistoryEntriesForView(location: chatLocation, view: view, includeUnreadEntry: mode == .bubbles, includeEmptyEntry: mode == .bubbles && tagMask == nil, includeChatInfoEntry: mode == .bubbles, includeSearchEntry: includeSearchEntry && tagMask != nil, reverse: reverse, groupMessages: mode == .bubbles, selectedMessages: selectedMessages, presentationData: chatPresentationData, historyAppearsCleared: historyAppearsCleared, pendingUnpinnedAllMessages: pendingUnpinnedAllMessages, associatedData: associatedData, updatingMedia: updatingMedia, customChannelDiscussionReadState: customChannelDiscussionReadState, customThreadOutgoingReadState: customThreadOutgoingReadState) + let filteredEntries = chatHistoryEntriesForView(location: chatLocation, view: view, includeUnreadEntry: mode == .bubbles, includeEmptyEntry: mode == .bubbles && tagMask == nil, includeChatInfoEntry: mode == .bubbles, includeSearchEntry: includeSearchEntry && tagMask != nil, reverse: reverse, groupMessages: mode == .bubbles, selectedMessages: selectedMessages, presentationData: chatPresentationData, historyAppearsCleared: historyAppearsCleared, pendingUnpinnedAllMessages: pendingUnpinnedAllMessages, pendingRemovedMessages: pendingRemovedMessages, associatedData: associatedData, updatingMedia: updatingMedia, customChannelDiscussionReadState: customChannelDiscussionReadState, customThreadOutgoingReadState: customThreadOutgoingReadState) let lastHeaderId = filteredEntries.last.flatMap { listMessageDateHeaderId(timestamp: $0.index.timestamp) } ?? 0 let processedView = ChatHistoryView(originalView: view, filteredEntries: filteredEntries, associatedData: associatedData, lastHeaderId: lastHeaderId, id: id) let previousValueAndVersion = previousView.swap((processedView, update.1, selectedMessages)) diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index a68301a3a3..8d6590dca3 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -687,9 +687,8 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: if let pinnedSelectedMessageId = pinnedSelectedMessageId { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Unpin, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unpin"), color: theme.actionSheet.primaryTextColor) - }, action: { _, f in - interfaceInteraction.unpinMessage(pinnedSelectedMessageId, false) - f(.default) + }, action: { c, _ in + interfaceInteraction.unpinMessage(pinnedSelectedMessageId, false, c) }))) } else { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Pin, icon: { theme in diff --git a/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift b/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift index 829ab1145f..5b7b604d99 100644 --- a/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift +++ b/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift @@ -95,7 +95,7 @@ final class ChatPanelInterfaceInteraction { let sendSticker: (FileMediaReference, ASDisplayNode, CGRect) -> Bool let unblockPeer: () -> Void let pinMessage: (MessageId, ContextController?) -> Void - let unpinMessage: (MessageId, Bool) -> Void + let unpinMessage: (MessageId, Bool, ContextController?) -> Void let unpinAllMessages: () -> Void let openPinnedList: (MessageId) -> Void let shareAccountContact: () -> Void @@ -174,7 +174,7 @@ final class ChatPanelInterfaceInteraction { sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId, ContextController?) -> Void, - unpinMessage: @escaping (MessageId, Bool) -> Void, + unpinMessage: @escaping (MessageId, Bool, ContextController?) -> Void, unpinAllMessages: @escaping () -> Void, openPinnedList: @escaping (MessageId) -> Void, shareAccountContact: @escaping () -> Void, diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index 0561245ff2..a6bab54813 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -462,7 +462,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { @objc func closePressed() { if let interfaceInteraction = self.interfaceInteraction, let message = self.currentMessage { - interfaceInteraction.unpinMessage(message.message.id, true) + interfaceInteraction.unpinMessage(message.message.id, true, nil) } } diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsController.swift b/submodules/TelegramUI/Sources/ChatRecentActionsController.swift index f92dab1d5b..b350a848f9 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsController.swift @@ -100,7 +100,7 @@ final class ChatRecentActionsController: TelegramBaseController { return false }, unblockPeer: { }, pinMessage: { _, _ in - }, unpinMessage: { _, _ in + }, unpinMessage: { _, _, _ in }, unpinAllMessages: { }, openPinnedList: { _ in }, shareAccountContact: { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index e7f55c474e..ab94f05d5f 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -406,7 +406,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { return false }, unblockPeer: { }, pinMessage: { _, _ in - }, unpinMessage: { _, _ in + }, unpinMessage: { _, _, _ in }, unpinAllMessages: { }, openPinnedList: { _ in }, shareAccountContact: {