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
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

View File

@ -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<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 {
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
}

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
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))

View File

@ -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

View File

@ -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,

View File

@ -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)
}
}

View File

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

View File

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