Pinned messages improvements

This commit is contained in:
Ali 2020-10-14 01:29:49 +04:00
parent de7ca018f6
commit cd96e69361
8 changed files with 2225 additions and 2100 deletions

View File

@ -69,6 +69,11 @@ debug_deps = select({
"//conditions:default": [],
})
strip_framework = select({
":debug": None,
"//conditions:default": ":StripFramework",
})
filegroup(
name = "AppResources",
srcs = glob([
@ -542,7 +547,7 @@ ios_framework(
":MtProtoKitInfoPlist",
],
minimum_os_version = "9.0",
ipa_post_processor = ":StripFramework",
ipa_post_processor = strip_framework,
deps = [
"//submodules/MtProtoKit:MtProtoKit",
],
@ -583,7 +588,7 @@ ios_framework(
":SwiftSignalKitInfoPlist",
],
minimum_os_version = "9.0",
ipa_post_processor = ":StripFramework",
ipa_post_processor = strip_framework,
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
],
@ -627,7 +632,7 @@ ios_framework(
":SwiftSignalKitFramework",
],
minimum_os_version = "9.0",
ipa_post_processor = ":StripFramework",
ipa_post_processor = strip_framework,
deps = [
"//submodules/Postbox:Postbox",
],
@ -668,7 +673,7 @@ ios_framework(
":TelegramApiInfoPlist",
],
minimum_os_version = "9.0",
ipa_post_processor = ":StripFramework",
ipa_post_processor = strip_framework,
deps = [
"//submodules/TelegramApi:TelegramApi",
],
@ -713,7 +718,7 @@ ios_framework(
":PostboxFramework",
],
minimum_os_version = "9.0",
ipa_post_processor = ":StripFramework",
ipa_post_processor = strip_framework,
deps = [
"//submodules/SyncCore:SyncCore",
],
@ -761,7 +766,7 @@ ios_framework(
":TelegramApiFramework",
],
minimum_os_version = "9.0",
ipa_post_processor = ":StripFramework",
ipa_post_processor = strip_framework,
deps = [
"//submodules/TelegramCore:TelegramCore",
],
@ -802,7 +807,7 @@ ios_framework(
":AsyncDisplayKitInfoPlist",
],
minimum_os_version = "9.0",
ipa_post_processor = ":StripFramework",
ipa_post_processor = strip_framework,
deps = [
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
],
@ -863,7 +868,7 @@ ios_framework(
":AsyncDisplayKitFramework",
],
minimum_os_version = "9.0",
ipa_post_processor = ":StripFramework",
ipa_post_processor = strip_framework,
deps = [
"//submodules/Display:Display",
],
@ -914,7 +919,7 @@ ios_framework(
":DisplayFramework",
],
minimum_os_version = "9.0",
ipa_post_processor = ":StripFramework",
ipa_post_processor = strip_framework,
deps = [
"//submodules/TelegramUI:TelegramUI",
] + debug_deps,

View File

@ -5822,3 +5822,8 @@ Any member of this group will be able to see messages in the channel.";
"ChatList.MessageMusic_1" = "1 Music File";
"ChatList.MessageMusic_any" = "%@ Music Files";
"Conversation.PinOlderMessageAlertTitle" = "Pin Message";
"Conversation.PinOlderMessageAlertText" = "Do you want to pin an older message while leaving a more recent one pinned?";
"Conversation.PinMessageAlertPin" = "Pin";

View File

@ -140,7 +140,7 @@ final class HistoryViewStateValidationContexts {
func updateView(id: Int32, view: MessageHistoryView?, location: ChatLocationInput? = nil) {
assert(self.queue.isCurrent())
guard let view = view, view.tagMask == nil || view.tagMask == MessageTags.unseenPersonalMessage || view.tagMask == MessageTags.music else {
guard let view = view, view.tagMask == nil || view.tagMask == MessageTags.unseenPersonalMessage || view.tagMask == MessageTags.music || view.tagMask == MessageTags.pinned else {
if self.contexts[id] != nil {
self.contexts.removeValue(forKey: id)
}

View File

@ -50,7 +50,37 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update:
}
|> mapToSignal { updates -> Signal<Void, UpdatePinnedMessageError> in
account.stateManager.addUpdates(updates)
return account.postbox.transaction { _ in
return account.postbox.transaction { transaction in
switch updates {
case let .updates(updates, _, _, _, _):
if updates.isEmpty {
if peerId.namespace == Namespaces.Peer.CloudChannel {
let messageId: MessageId
switch update {
case let .pin(id, _):
messageId = id
case let .clear(id):
messageId = id
}
transaction.updateMessage(messageId, update: { currentMessage in
let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
var updatedTags = currentMessage.tags
switch update {
case .pin:
updatedTags.insert(.pinned)
case .clear:
updatedTags.remove(.pinned)
}
if updatedTags == currentMessage.tags {
return .skip
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
})
}
}
default:
break
}
/*transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
if let current = current as? CachedChannelData {
let pinnedMessageId: MessageId?
@ -111,6 +141,37 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update:
|> mapToSignal { updates -> Signal<Void, UpdatePinnedMessageError> in
account.stateManager.addUpdates(updates)
return account.postbox.transaction { transaction in
switch updates {
case let .updates(updates, _, _, _, _):
if updates.isEmpty {
if peerId.namespace == Namespaces.Peer.CloudChannel {
let messageId: MessageId
switch update {
case let .pin(id, _):
messageId = id
case let .clear(id):
messageId = id
}
transaction.updateMessage(messageId, update: { currentMessage in
let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
var updatedTags = currentMessage.tags
switch update {
case .pin:
updatedTags.insert(.pinned)
case .clear:
updatedTags.remove(.pinned)
}
if updatedTags == currentMessage.tags {
return .skip
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
})
}
}
default:
break
}
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
if let _ = peer as? TelegramGroup {
let current = current as? CachedGroupData ?? CachedGroupData()

View File

@ -3233,6 +3233,64 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.chatTitleView?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, hasEmbeddedTitleContent: self.hasEmbeddedTitleContent)
}
private func topPinnedMessageSignal(latest: Bool) -> Signal<ChatPinnedMessage?, NoError> {
let topPinnedMessage: Signal<ChatPinnedMessage?, NoError>
switch self.chatLocation {
case let .peer(peerId) where peerId.namespace == Namespaces.Peer.CloudChannel:
let replyHistory: Signal<ChatHistoryViewUpdate, NoError> = (chatHistoryViewForLocation(ChatHistoryLocationInput(content: .Initial(count: 100), id: 0), context: self.context, chatLocation: .peer(peerId), chatLocationContextHolder: Atomic<ChatLocationContextHolder?>(value: nil), scheduled: false, fixedCombinedReadStates: nil, tagMask: MessageTags.pinned, additionalData: [])
|> castError(Bool.self)
|> mapToSignal { update -> Signal<ChatHistoryViewUpdate, Bool> in
switch update {
case let .Loading(_, type):
if case .Generic(.FillHole) = type {
return .fail(true)
}
case let .HistoryView(_, type, _, _, _, _, _):
if case .Generic(.FillHole) = type {
return .fail(true)
}
}
return .single(update)
})
|> restartIfError
topPinnedMessage = combineLatest(
replyHistory,
latest ? .single(nil) : self.chatDisplayNode.historyNode.topVisibleMessageRange.get()
)
|> map { update, topVisibleMessageRange -> ChatPinnedMessage? in
var message: ChatPinnedMessage?
switch update {
case .Loading:
break
case let .HistoryView(view, _, _, _, _, _, _):
for i in 0 ..< view.entries.count {
let entry = view.entries[i]
var matches = false
if message == nil {
matches = true
} else if let topVisibleMessageRange = topVisibleMessageRange {
if entry.message.id < topVisibleMessageRange.upperBound {
matches = true
}
} else {
matches = true
}
if matches {
message = ChatPinnedMessage(message: entry.message, isLatest: i == view.entries.count - 1)
}
}
break
}
return message
}
|> distinctUntilChanged
default:
topPinnedMessage = .single(nil)
}
return topPinnedMessage
}
override public func loadDisplayNode() {
self.displayNode = ChatControllerNode(context: self.context, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, subject: self.subject, controllerInteraction: self.controllerInteraction!, chatPresentationInterfaceState: self.presentationInterfaceState, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, navigationBar: self.navigationBar, controller: self)
@ -3402,60 +3460,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let isTopReplyThreadMessageShown: Signal<Bool, NoError> = self.chatDisplayNode.historyNode.isTopReplyThreadMessageShown.get()
|> distinctUntilChanged
let topPinnedMessage: Signal<ChatPinnedMessage?, NoError>
switch self.chatLocation {
case let .peer(peerId) where peerId.namespace == Namespaces.Peer.CloudChannel:
let replyHistory: Signal<ChatHistoryViewUpdate, NoError> = (chatHistoryViewForLocation(ChatHistoryLocationInput(content: .Initial(count: 100), id: 0), context: self.context, chatLocation: .peer(peerId), chatLocationContextHolder: Atomic<ChatLocationContextHolder?>(value: nil), scheduled: false, fixedCombinedReadStates: nil, tagMask: MessageTags.pinned, additionalData: [])
|> castError(Bool.self)
|> mapToSignal { update -> Signal<ChatHistoryViewUpdate, Bool> in
switch update {
case let .Loading(_, type):
if case .Generic(.FillHole) = type {
return .fail(true)
}
case let .HistoryView(_, type, _, _, _, _, _):
if case .Generic(.FillHole) = type {
return .fail(true)
}
}
return .single(update)
})
|> restartIfError
topPinnedMessage = combineLatest(
replyHistory,
self.chatDisplayNode.historyNode.topVisibleMessageRange.get()
)
|> map { update, topVisibleMessageRange -> ChatPinnedMessage? in
var message: ChatPinnedMessage?
switch update {
case .Loading:
break
case let .HistoryView(view, _, _, _, _, _, _):
for i in 0 ..< view.entries.count {
let entry = view.entries[i]
var matches = false
if message == nil {
matches = true
} else if let topVisibleMessageRange = topVisibleMessageRange {
if entry.message.id < topVisibleMessageRange.lowerBound {
matches = true
}
} else {
matches = true
}
if matches {
message = ChatPinnedMessage(message: entry.message, isLatest: i == view.entries.count - 1)
}
}
break
}
return message
}
|> distinctUntilChanged
default:
topPinnedMessage = .single(nil)
}
let topPinnedMessage: Signal<ChatPinnedMessage?, NoError> = self.topPinnedMessageSignal(latest: false)
self.cachedDataDisposable = combineLatest(queue: .mainQueue(), self.chatDisplayNode.historyNode.cachedPeerDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage).start(next: { [weak self] cachedDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage in
if let strongSelf = self {
@ -3586,7 +3591,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if case .replyThread = self.chatLocation {
effectiveCachedDataReady = self.cachedDataReady.get()
} else {
effectiveCachedDataReady = .single(true)
//effectiveCachedDataReady = .single(true)
effectiveCachedDataReady = self.cachedDataReady.get()
}
self.ready.set(combineLatest(self.chatDisplayNode.historyNode.historyState.get(), self._chatLocationInfoReady.get(), effectiveCachedDataReady, initialData) |> map { _, chatLocationInfoReady, cachedDataReady, _ in
return chatLocationInfoReady && cachedDataReady
@ -4812,11 +4818,46 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if pinImmediately {
pinAction(true)
} else {
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_PinMessageAlertGroup, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Conversation_PinMessageAlert_OnlyPin, action: {
let topPinnedMessage: Signal<ChatPinnedMessage?, NoError> = strongSelf.topPinnedMessageSignal(latest: true)
|> take(1)
let _ = (topPinnedMessage
|> deliverOnMainQueue).start(next: { value in
guard let strongSelf = self else {
return
}
let title: String?
let text: String
let actionLayout: TextAlertContentActionLayout
let actions: [TextAlertAction]
if let value = value, value.message.id > messageId {
title = strongSelf.presentationData.strings.Conversation_PinOlderMessageAlertTitle
text = strongSelf.presentationData.strings.Conversation_PinOlderMessageAlertText
actionLayout = .vertical
actions = [
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Conversation_PinMessageAlertPin, action: {
pinAction(false)
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Yes, action: {
}),
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
})
]
} else {
title = nil
text = strongSelf.presentationData.strings.Conversation_PinMessageAlertGroup
actionLayout = .horizontal
actions = [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Conversation_PinMessageAlert_OnlyPin, action: {
pinAction(false)
}),
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Yes, action: {
pinAction(true)
})]), in: .window(.root))
})
]
}
strongSelf.present(textAlertController(context: strongSelf.context, title: title, text: text, actions: actions, actionLayout: actionLayout), in: .window(.root))
})
}
} else {
if let pinnedMessageId = strongSelf.presentationInterfaceState.pinnedMessage?.message.id {
@ -4854,7 +4895,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
if canManagePin {
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_UnpinMessageAlert, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Conversation_Unpin, 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: {
if let strongSelf = self {
let disposable: MetaDisposable
if let current = strongSelf.unpinMessageDisposable {
@ -4865,7 +4906,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
disposable.set(requestUpdatePinnedMessage(account: strongSelf.context.account, peerId: peer.id, update: .clear(id: id)).start())
}
})]), in: .window(.root))
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {})], actionLayout: .vertical), in: .window(.root))
} else {
if let pinnedMessage = strongSelf.presentationInterfaceState.pinnedMessage {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {

View File

@ -643,27 +643,37 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
}
if data.canPin, case .peer = chatPresentationInterfaceState.chatLocation {
let isPinned: Bool
var pinnedSelectedMessageId: MessageId?
if let _ = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
isPinned = messages[0].tags.contains(.pinned)
for message in messages {
if message.tags.contains(.pinned) {
pinnedSelectedMessageId = message.id
break
}
}
} else {
isPinned = chatPresentationInterfaceState.pinnedMessage?.message.id == messages[0].id
for message in messages {
if chatPresentationInterfaceState.pinnedMessage?.message.id == message.id {
pinnedSelectedMessageId = message.id
break
}
}
}
if !isPinned {
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)
f(.default)
})))
} else {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Pin, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Pin"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
interfaceInteraction.pinMessage(messages[0].id)
f(.dismissWithoutContent)
})))
} else {
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(messages[0].id)
f(.default)
})))
}
}