Add undo to pinned message list unpinning

This commit is contained in:
Ali 2020-10-27 22:24:39 +04:00
parent f2477074ae
commit 91a8a98fc8
8 changed files with 163 additions and 100 deletions

View File

@ -5232,62 +5232,104 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
} }
} }
}, unpinMessage: { [weak self] id, askForConfirmation in }, unpinMessage: { [weak self] id, askForConfirmation, contextController in
if let strongSelf = self { let impl: () -> Void = {
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { guard let strongSelf = self else {
if strongSelf.canManagePin() { return
let action: () -> Void = { }
if let strongSelf = self { guard let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
let disposable: MetaDisposable return
if let current = strongSelf.unpinMessageDisposable { }
disposable = current
} else { if strongSelf.canManagePin() {
disposable = MetaDisposable() let action: () -> Void = {
strongSelf.unpinMessageDisposable = disposable if let strongSelf = self {
} let disposable: MetaDisposable
if let current = strongSelf.unpinMessageDisposable {
if askForConfirmation { disposable = current
strongSelf.chatDisplayNode.historyNode.pendingUnpinnedAllMessages = true } else {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { disposable = MetaDisposable()
return $0.updatedPendingUnpinnedAllMessages(true) 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( strongSelf.present(
UndoOverlayController( UndoOverlayController(
presentationData: strongSelf.presentationData, presentationData: strongSelf.presentationData,
content: .messagesUnpinned( content: .messagesUnpinned(
title: strongSelf.presentationData.strings.Chat_MessagesUnpinned(1), title: strongSelf.presentationData.strings.Chat_MessagesUnpinned(1),
text: "", text: "",
undo: askForConfirmation, undo: true,
isHidden: false isHidden: false
), ),
elevatedLayout: false, elevatedLayout: false,
action: { action in action: { action in
guard let strongSelf = self else {
return true
}
switch action { switch action {
case .commit: case .commit:
disposable.set((requestUpdatePinnedMessage(account: strongSelf.context.account, peerId: peer.id, update: .clear(id: id)) let _ = (requestUpdatePinnedMessage(account: strongSelf.context.account, peerId: peer.id, update: .clear(id: id))
|> deliverOnMainQueue).start(error: { _ in |> deliverOnMainQueue).start(completed: {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
strongSelf.chatDisplayNode.historyNode.pendingUnpinnedAllMessages = false strongSelf.chatDisplayNode.historyNode.pendingRemovedMessages.remove(id)
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)
}) })
case .undo:
strongSelf.chatDisplayNode.historyNode.pendingRemovedMessages.remove(id)
default: default:
break 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: { if askForConfirmation {
action() 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: {
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {})], actionLayout: .vertical), in: .window(.root))
} else {
action() action()
} }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {})], actionLayout: .vertical), in: .window(.root))
} else { } else {
if let pinnedMessage = strongSelf.presentationInterfaceState.pinnedMessage { action()
let previousClosedPinnedMessageId = strongSelf.presentationInterfaceState.interfaceState.messageActionsState.closedPinnedMessageId }
} else {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { if let pinnedMessage = strongSelf.presentationInterfaceState.pinnedMessage {
return $0.updatedInterfaceState({ $0.withUpdatedMessageActionsState({ value in let previousClosedPinnedMessageId = strongSelf.presentationInterfaceState.interfaceState.messageActionsState.closedPinnedMessageId
var value = value
value.closedPinnedMessageId = pinnedMessage.topMessageId strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
return value return $0.updatedInterfaceState({ $0.withUpdatedMessageActionsState({ value in
}) }) var value = value
}) value.closedPinnedMessageId = pinnedMessage.topMessageId
strongSelf.present( return value
UndoOverlayController( }) })
presentationData: strongSelf.presentationData, })
content: .messagesUnpinned( strongSelf.present(
title: strongSelf.presentationData.strings.Chat_PinnedMessagesHiddenTitle, UndoOverlayController(
text: strongSelf.presentationData.strings.Chat_PinnedMessagesHiddenText, presentationData: strongSelf.presentationData,
undo: true, content: .messagesUnpinned(
isHidden: false title: strongSelf.presentationData.strings.Chat_PinnedMessagesHiddenTitle,
), text: strongSelf.presentationData.strings.Chat_PinnedMessagesHiddenText,
elevatedLayout: false, undo: true,
action: { action in isHidden: false
guard let strongSelf = self else { ),
return true elevatedLayout: false,
} action: { action in
switch action { guard let strongSelf = self else {
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 return true
} }
), switch action {
in: .current case .commit:
) break
strongSelf.updatedClosedPinnedMessageId?(pinnedMessage.topMessageId) 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 }, unpinAllMessages: { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return

View File

@ -8,7 +8,7 @@ import AccountContext
import TelegramPresentationData import TelegramPresentationData
func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView, includeUnreadEntry: Bool, includeEmptyEntry: Bool, includeChatInfoEntry: Bool, includeSearchEntry: Bool, reverse: Bool, groupMessages: Bool, selectedMessages: Set<MessageId>?, 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<MessageId>?, presentationData: ChatPresentationData, historyAppearsCleared: Bool, pendingUnpinnedAllMessages: Bool, pendingRemovedMessages: Set<MessageId>, associatedData: ChatMessageItemAssociatedData, updatingMedia: [MessageId: ChatUpdatingMessageMedia], customChannelDiscussionReadState: MessageId?, customThreadOutgoingReadState: MessageId?) -> [ChatHistoryEntry] {
if historyAppearsCleared { if historyAppearsCleared {
return [] return []
} }
@ -34,6 +34,10 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
var message = entry.message var message = entry.message
var isRead = entry.isRead var isRead = entry.isRead
if pendingRemovedMessages.contains(message.id) {
continue
}
if let customThreadOutgoingReadState = customThreadOutgoingReadState { if let customThreadOutgoingReadState = customThreadOutgoingReadState {
isRead = customThreadOutgoingReadState >= message.id isRead = customThreadOutgoingReadState >= message.id
} }

View File

@ -565,6 +565,15 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
} }
} }
private let pendingRemovedMessagesPromise = ValuePromise<Set<MessageId>>(Set())
var pendingRemovedMessages: Set<MessageId> = Set() {
didSet {
if self.pendingRemovedMessages != oldValue {
self.pendingRemovedMessagesPromise.set(self.pendingRemovedMessages)
}
}
}
private(set) var isScrollAtBottomPosition = false private(set) var isScrollAtBottomPosition = false
public var isScrollAtBottomPositionUpdated: (() -> Void)? public var isScrollAtBottomPositionUpdated: (() -> Void)?
@ -846,10 +855,11 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
automaticDownloadNetworkType, automaticDownloadNetworkType,
self.historyAppearsClearedPromise.get(), self.historyAppearsClearedPromise.get(),
self.pendingUnpinnedAllMessagesPromise.get(), self.pendingUnpinnedAllMessagesPromise.get(),
self.pendingRemovedMessagesPromise.get(),
animatedEmojiStickers, animatedEmojiStickers,
customChannelDiscussionReadState, customChannelDiscussionReadState,
customThreadOutgoingReadState 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() { func applyHole() {
Queue.mainQueue().async { Queue.mainQueue().async {
if let strongSelf = self { 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 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 lastHeaderId = filteredEntries.last.flatMap { listMessageDateHeaderId(timestamp: $0.index.timestamp) } ?? 0
let processedView = ChatHistoryView(originalView: view, filteredEntries: filteredEntries, associatedData: associatedData, lastHeaderId: lastHeaderId, id: id) let processedView = ChatHistoryView(originalView: view, filteredEntries: filteredEntries, associatedData: associatedData, lastHeaderId: lastHeaderId, id: id)
let previousValueAndVersion = previousView.swap((processedView, update.1, selectedMessages)) let previousValueAndVersion = previousView.swap((processedView, update.1, selectedMessages))

View File

@ -687,9 +687,8 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
if let pinnedSelectedMessageId = pinnedSelectedMessageId { if let pinnedSelectedMessageId = pinnedSelectedMessageId {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Unpin, icon: { theme in 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) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unpin"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in }, action: { c, _ in
interfaceInteraction.unpinMessage(pinnedSelectedMessageId, false) interfaceInteraction.unpinMessage(pinnedSelectedMessageId, false, c)
f(.default)
}))) })))
} else { } else {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Pin, icon: { theme in actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Pin, icon: { theme in

View File

@ -95,7 +95,7 @@ final class ChatPanelInterfaceInteraction {
let sendSticker: (FileMediaReference, ASDisplayNode, CGRect) -> Bool let sendSticker: (FileMediaReference, ASDisplayNode, CGRect) -> Bool
let unblockPeer: () -> Void let unblockPeer: () -> Void
let pinMessage: (MessageId, ContextController?) -> Void let pinMessage: (MessageId, ContextController?) -> Void
let unpinMessage: (MessageId, Bool) -> Void let unpinMessage: (MessageId, Bool, ContextController?) -> Void
let unpinAllMessages: () -> Void let unpinAllMessages: () -> Void
let openPinnedList: (MessageId) -> Void let openPinnedList: (MessageId) -> Void
let shareAccountContact: () -> Void let shareAccountContact: () -> Void
@ -174,7 +174,7 @@ final class ChatPanelInterfaceInteraction {
sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool,
unblockPeer: @escaping () -> Void, unblockPeer: @escaping () -> Void,
pinMessage: @escaping (MessageId, ContextController?) -> Void, pinMessage: @escaping (MessageId, ContextController?) -> Void,
unpinMessage: @escaping (MessageId, Bool) -> Void, unpinMessage: @escaping (MessageId, Bool, ContextController?) -> Void,
unpinAllMessages: @escaping () -> Void, unpinAllMessages: @escaping () -> Void,
openPinnedList: @escaping (MessageId) -> Void, openPinnedList: @escaping (MessageId) -> Void,
shareAccountContact: @escaping () -> Void, shareAccountContact: @escaping () -> Void,

View File

@ -462,7 +462,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
@objc func closePressed() { @objc func closePressed() {
if let interfaceInteraction = self.interfaceInteraction, let message = self.currentMessage { if let interfaceInteraction = self.interfaceInteraction, let message = self.currentMessage {
interfaceInteraction.unpinMessage(message.message.id, true) interfaceInteraction.unpinMessage(message.message.id, true, nil)
} }
} }

View File

@ -100,7 +100,7 @@ final class ChatRecentActionsController: TelegramBaseController {
return false return false
}, unblockPeer: { }, unblockPeer: {
}, pinMessage: { _, _ in }, pinMessage: { _, _ in
}, unpinMessage: { _, _ in }, unpinMessage: { _, _, _ in
}, unpinAllMessages: { }, unpinAllMessages: {
}, openPinnedList: { _ in }, openPinnedList: { _ in
}, shareAccountContact: { }, shareAccountContact: {

View File

@ -406,7 +406,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
return false return false
}, unblockPeer: { }, unblockPeer: {
}, pinMessage: { _, _ in }, pinMessage: { _, _ in
}, unpinMessage: { _, _ in }, unpinMessage: { _, _, _ in
}, unpinAllMessages: { }, unpinAllMessages: {
}, openPinnedList: { _ in }, openPinnedList: { _ in
}, shareAccountContact: { }, shareAccountContact: {