From 263d78c1b3c99f58fd18226856767c5530eebe01 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 21 Aug 2019 02:05:10 +0300 Subject: [PATCH] Scheduled Messages UI improvements --- Telegram-iOS/en.lproj/Localizable.strings | 7 +- .../Sources/AccountContext.swift | 6 +- .../Sources/ChatController.swift | 4 +- .../Sources/ChatListController.swift | 4 +- .../Sources/HashtagSearchController.swift | 2 +- .../Sources/InstantPageControllerNode.swift | 4 +- .../TelegramUI/ApplicationContext.swift | 4 +- .../TelegramUI/ChatBotInfoItem.swift | 2 +- .../ChatButtonKeyboardInputNode.swift | 2 +- .../TelegramUI/ChatController.swift | 112 ++++++++++++------ .../TelegramUI/TelegramUI/ChatEmptyNode.swift | 4 +- .../TelegramUI/ChatHistoryListNode.swift | 4 - .../ChatHistoryViewForLocation.swift | 44 +++---- .../ChatMessageAnimatedStickerItemNode.swift | 16 ++- .../ChatMessageBubbleItemNode.swift | 17 +-- .../ChatMessageInstantVideoItemNode.swift | 6 +- .../ChatMessageStickerItemNode.swift | 15 ++- .../TelegramUI/ChatPresentationData.swift | 7 +- .../ChatRecentActionsControllerNode.swift | 3 +- .../ChatScheduleTimeControllerNode.swift | 1 + ...SendMessageActionSheetControllerNode.swift | 102 +++++++--------- .../TelegramUI/DebugController.swift | 21 +--- .../TelegramUI/TelegramUI/LegacyCamera.swift | 3 +- .../TelegramUI/NavigateToChatController.swift | 6 +- .../TelegramUI/OpenResolvedUrl.swift | 8 +- .../TelegramUI/TelegramUI/OpenUrl.swift | 4 +- .../PeerMediaCollectionController.swift | 6 +- .../PeerMessagesMediaPlaylist.swift | 2 +- .../Resources/PresentationStrings.mapping | Bin 124491 -> 124672 bytes .../TelegramUI/TextLinkHandling.swift | 8 +- .../TelegramUI/TelegramUI/UrlHandling.swift | 10 +- 31 files changed, 234 insertions(+), 200 deletions(-) diff --git a/Telegram-iOS/en.lproj/Localizable.strings b/Telegram-iOS/en.lproj/Localizable.strings index 4771f22914..a45da3d3cd 100644 --- a/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram-iOS/en.lproj/Localizable.strings @@ -4609,8 +4609,11 @@ Any member of this group will be able to see messages in the channel."; "ScheduledMessages.SendNow" = "Send Now"; "ScheduledMessages.EditTime" = "Reschedule"; "ScheduledMessages.ClearAll" = "Clear All"; -"ScheduledMessages.Delete" = "Delete"; +"ScheduledMessages.ClearAllConfirmation" = "Clear Scheduled Messages"; +"ScheduledMessages.Delete" = "Delete Scheduled Message"; +"ScheduledMessages.DeleteMany" = "Delete Scheduled Messages"; "ScheduledMessages.EmptyPlaceholder" = "No scheduled messages here yet..."; +"ScheduledMessages.BotActionUnavailable" = "This action will become available after the message is published."; "Conversation.SendMessage.SetReminder" = "Set a Reminder"; @@ -4657,3 +4660,5 @@ Any member of this group will be able to see messages in the channel."; "Appearance.ThemePreview.Chat.4.Text" = "Nearly missed the sunrise."; "GroupInfo.Permissions.SlowmodeValue.Off" = "Off"; + +"Undo.ScheduledMessagesCleared" = "Scheduled messages cleared"; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index d85b8eaa9b..e5808adeaf 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -179,7 +179,7 @@ public final class NavigateToChatControllerParams { public let chatController: ChatController? public let context: AccountContext public let chatLocation: ChatLocation - public let messageId: MessageId? + public let subject: ChatControllerSubject? public let botStart: ChatControllerInitialBotStart? public let updateTextInputState: ChatTextInputState? public let activateInput: Bool @@ -191,12 +191,12 @@ public final class NavigateToChatControllerParams { public let parentGroupId: PeerGroupId? public let completion: () -> Void - public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, messageId: MessageId? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, completion: @escaping () -> Void = {}) { + public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, completion: @escaping () -> Void = {}) { self.navigationController = navigationController self.chatController = chatController self.context = context self.chatLocation = chatLocation - self.messageId = messageId + self.subject = subject self.botStart = botStart self.updateTextInputState = updateTextInputState self.activateInput = activateInput diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index 52a97a46bd..7f119048aa 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -7,7 +7,7 @@ import SwiftSignalKit public enum ChatControllerInitialBotStartBehavior { case interactive - case automatic(returnToPeerId: PeerId) + case automatic(returnToPeerId: PeerId, scheduled: Bool) } public struct ChatControllerInitialBotStart { @@ -22,7 +22,7 @@ public struct ChatControllerInitialBotStart { public enum ChatControllerInteractionNavigateToPeer { case `default` - case chat(textInputState: ChatTextInputState?, messageId: MessageId?) + case chat(textInputState: ChatTextInputState?, subject: ChatControllerSubject?) case info case withBotStartPayload(ChatControllerInitialBotStart) } diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 9a62d8c8ff..21ea059670 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -570,7 +570,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, return } - let beginClear: (InteractiveMessagesDeletionType) -> Void = { type in + let beginClear: (InteractiveHistoryClearingType) -> Void = { type in guard let strongSelf = self else { return } @@ -748,7 +748,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, } - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), messageId: messageId, purposefulAction: { + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), subject: .message(messageId), purposefulAction: { self?.deactivateSearch(animated: false) }, scrollToEndIfExists: scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [])) strongSelf.chatListDisplayNode.chatListNode.clearHighlightAnimated(true) diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index 602e896b6a..150ce02736 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -53,7 +53,7 @@ public final class HashtagSearchController: TelegramBaseController { if let strongSelf = self { strongSelf.openMessageFromSearchDisposable.set((storedMessageFromSearchPeer(account: strongSelf.context.account, peer: peer) |> deliverOnMainQueue).start(next: { actualPeerId in if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), messageId: message.id.peerId == actualPeerId ? message.id : nil, keepStack: .always)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), subject: message.id.peerId == actualPeerId ? .message(message.id) : nil, keepStack: .always)) } })) strongSelf.controllerNode.listNode.clearHighlightAnimated(true) diff --git a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift index e1d217d93d..0568bf986f 100644 --- a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift @@ -1199,9 +1199,9 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { strongSelf.loadProgress.set(1.0) strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.getNavigationController(), openPeer: { peerId, navigation in switch navigation { - case let .chat(_, messageId): + case let .chat(_, subject): if let navigationController = strongSelf.getNavigationController() { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), messageId: messageId)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject)) } case let .withBotStartPayload(botStart): if let navigationController = strongSelf.getNavigationController() { diff --git a/submodules/TelegramUI/TelegramUI/ApplicationContext.swift b/submodules/TelegramUI/TelegramUI/ApplicationContext.swift index d4cc28d80b..107fb94ac1 100644 --- a/submodules/TelegramUI/TelegramUI/ApplicationContext.swift +++ b/submodules/TelegramUI/TelegramUI/ApplicationContext.swift @@ -787,7 +787,7 @@ final class AuthorizedApplicationContext { } let navigateToMessage = { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(messageId.peerId), messageId: messageId)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(messageId.peerId), subject: .message(messageId))) } if chatIsVisible { @@ -832,7 +832,7 @@ final class AuthorizedApplicationContext { if visiblePeerId != peerId || messageId != nil { if self.rootController.rootTabController != nil { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: self.rootController, context: self.context, chatLocation: .peer(peerId), messageId: messageId, activateInput: activateInput)) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: self.rootController, context: self.context, chatLocation: .peer(peerId), subject: messageId.flatMap { .message($0) }, activateInput: activateInput)) } else { self.scheduledOperChatWithPeerId = (peerId, messageId, activateInput) } diff --git a/submodules/TelegramUI/TelegramUI/ChatBotInfoItem.swift b/submodules/TelegramUI/TelegramUI/ChatBotInfoItem.swift index f307665b24..372cf9a173 100644 --- a/submodules/TelegramUI/TelegramUI/ChatBotInfoItem.swift +++ b/submodules/TelegramUI/TelegramUI/ChatBotInfoItem.swift @@ -301,7 +301,7 @@ final class ChatBotInfoItemNode: ListViewItemNode { case let .url(url, concealed): self.item?.controllerInteraction.openUrl(url, concealed, nil) case let .peerMention(peerId, _): - self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, messageId: nil), nil) + self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil), nil) case let .textMention(name): self.item?.controllerInteraction.openPeerMention(name) case let .botCommand(command): diff --git a/submodules/TelegramUI/TelegramUI/ChatButtonKeyboardInputNode.swift b/submodules/TelegramUI/TelegramUI/ChatButtonKeyboardInputNode.swift index 4b6c27902a..9a2f2c65f5 100644 --- a/submodules/TelegramUI/TelegramUI/ChatButtonKeyboardInputNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatButtonKeyboardInputNode.swift @@ -198,7 +198,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode { peerId = message.id.peerId } if let botPeer = botPeer, let addressName = botPeer.addressName { - self.controllerInteraction.openPeer(peerId, .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: "@\(addressName) \(query)")), messageId: nil), nil) + self.controllerInteraction.openPeer(peerId, .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: "@\(addressName) \(query)")), subject: nil), nil) } } case .payment: diff --git a/submodules/TelegramUI/TelegramUI/ChatController.swift b/submodules/TelegramUI/TelegramUI/ChatController.swift index d36d08dc5f..a208bd5c22 100644 --- a/submodules/TelegramUI/TelegramUI/ChatController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatController.swift @@ -319,6 +319,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var isScheduledMessages = false if let subject = subject, case .scheduledMessages = subject { + self.canReadHistory.set(false) isScheduledMessages = true } @@ -588,6 +589,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState) else { return } + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({ if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { @@ -659,6 +664,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return true }, requestMessageActionCallback: { [weak self] messageId, data, isGame in if let strongSelf = self { + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } + if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { return $0.updatedTitlePanelContext { @@ -728,6 +738,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, requestMessageActionUrlAuth: { [weak self] defaultUrl, messageId, buttonId in if let strongSelf = self { + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } if let _ = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { return $0.updatedTitlePanelContext { @@ -845,10 +859,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } - if let botStart = strongSelf.botStart, case let .automatic(returnToPeerId) = botStart.behavior { - strongSelf.openPeer(peerId: returnToPeerId, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), messageId: nil), fromMessage: nil) + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } + if let botStart = strongSelf.botStart, case let .automatic(returnToPeerId, scheduled) = botStart.behavior { + strongSelf.openPeer(peerId: returnToPeerId, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), subject: scheduled ? .scheduledMessages : nil), fromMessage: nil) } else { - strongSelf.openPeer(peerId: peerId, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), messageId: nil), fromMessage: nil) + strongSelf.openPeer(peerId: peerId, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), subject: nil), fromMessage: nil) } }, openUrl: { [weak self] url, concealed, _ in if let strongSelf = self { @@ -856,6 +874,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, shareCurrentLocation: { [weak self] in if let strongSelf = self { + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Conversation_ShareBotLocationConfirmationTitle, text: strongSelf.presentationData.strings.Conversation_ShareBotLocationConfirmation, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { if let strongSelf = self, let locationManager = strongSelf.context.sharedContext.locationManager { let _ = (currentLocationManagerCoordinate(manager: locationManager, timeout: 5.0) @@ -873,6 +895,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, shareAccountContact: { [weak self] in if let strongSelf = self { + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Conversation_ShareBotContactConfirmationTitle, text: strongSelf.presentationData.strings.Conversation_ShareBotContactConfirmation, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { if let strongSelf = self { let _ = (strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.context.account.peerId) @@ -1134,7 +1160,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { - strongSelf.openPeer(peerId: peerId, navigation: .chat(textInputState: nil, messageId: nil), fromMessage: nil) + strongSelf.openPeer(peerId: peerId, navigation: .chat(textInputState: nil, subject: nil), fromMessage: nil) } })) if !mention.isEmpty { @@ -1458,7 +1484,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, sendScheduledMessagesNow: { [weak self] messageIds in if let strongSelf = self { - strongSelf.sendScheduledMessagesNow(messageIds) + let _ = sendScheduledMessageNowInteractively(postbox: strongSelf.context.account.postbox, messageId: messageIds.first!).start() } }, editScheduledMessagesTime: { [weak self] messageIds in if let strongSelf = self, let messageId = messageIds.first { @@ -2183,7 +2209,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var slowmodeState: ChatSlowmodeState? if let cachedData = combinedInitialData.cachedData as? CachedChannelData { pinnedMessageId = cachedData.pinnedMessageId - if let channel = combinedInitialData.initialData?.peer as? TelegramChannel, channel.isRestrictedBySlowmode, let timeout = cachedData.slowModeTimeout { + if let channel = combinedInitialData.initialData?.peer as? TelegramChannel, channel.isRestrictedBySlowmode, let timeout = cachedData.slowModeTimeout, !strongSelf.presentationInterfaceState.isScheduledMessages { if let slowmodeUntilTimestamp = calculateSlowmodeActiveUntilTimestamp(account: strongSelf.context.account, untilTimestamp: cachedData.slowModeValidUntilTimestamp) { slowmodeState = ChatSlowmodeState(timeout: timeout, variant: .timestamp(slowmodeUntilTimestamp)) } @@ -2480,6 +2506,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let strongSelf = self, let validLayout = strongSelf.validLayout { var mappedTransition: (ChatHistoryListViewTransition, ListViewUpdateSizeAndInsets?)? + let isScheduledMessages = strongSelf.presentationInterfaceState.isScheduledMessages strongSelf.chatDisplayNode.containerLayoutUpdated(validLayout, navigationBarHeight: strongSelf.navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut), listViewTransaction: { updateSizeAndInsets, _, _ in var options = transition.options let _ = options.insert(.Synchronous) @@ -2493,17 +2520,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) var maxInsertedItem: Int? + var insertedIndex: Int? var insertItems: [ListViewInsertItem] = [] for i in 0 ..< transition.insertItems.count { let item = transition.insertItems[i] if item.directionHint == .Down && (maxInsertedItem == nil || maxInsertedItem! < item.index) { maxInsertedItem = item.index } + insertedIndex = item.index insertItems.append(ListViewInsertItem(index: item.index, previousIndex: item.previousIndex, item: item.item, directionHint: item.directionHint == .Down ? .Up : nil)) } var scrollToItem: ListViewScrollToItem? - if transition.historyView.originalView.laterId == nil { + if isScheduledMessages, let insertedIndex = insertedIndex { + scrollToItem = ListViewScrollToItem(index: insertedIndex, position: .visible, animated: true, curve: .Default(duration: 0.2), directionHint: .Down) + } else if transition.historyView.originalView.laterId == nil { scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: 0.2), directionHint: .Up) } @@ -2559,9 +2590,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: transformedMessages) - |> deliverOnMainQueue).start(next: { _ in + |> deliverOnMainQueue).start(next: { messageIds in if let strongSelf = self { - strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory() + if strongSelf.presentationInterfaceState.isScheduledMessages { + } else { + strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory() + } } }) @@ -3078,7 +3112,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } if let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), messageId: nil, keepStack: .always)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, keepStack: .always)) } }, openPeerInfo: { [weak self] in self?.navigationButtonAction(.openChatInfo) @@ -3132,7 +3166,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, botSwitchChatWithPayload: { [weak self] peerId, payload in if let strongSelf = self, case let .peer(currentPeerId) = strongSelf.chatLocation { - strongSelf.openPeer(peerId: peerId, navigation: .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .automatic(returnToPeerId: currentPeerId))), fromMessage: nil) + strongSelf.openPeer(peerId: peerId, navigation: .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .automatic(returnToPeerId: currentPeerId, scheduled: strongSelf.presentationInterfaceState.isScheduledMessages))), fromMessage: nil) } }, beginMediaRecording: { [weak self] isVideo in guard let strongSelf = self else { @@ -3921,7 +3955,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let actions: [TextAlertAction] if moreInfo { actions = [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Generic_ErrorMoreInfo, action: { - self?.openPeerMention("spambot", navigation: .chat(textInputState: nil, messageId: nil)) + self?.openPeerMention("spambot", navigation: .chat(textInputState: nil, subject: nil)) }), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})] } else { actions = [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})] @@ -4634,7 +4668,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let account = self.context.account - let beginClear: (InteractiveMessagesDeletionType) -> Void = { [weak self] type in + let beginClear: (InteractiveHistoryClearingType) -> Void = { [weak self] type in guard let strongSelf = self else { return } @@ -4642,7 +4676,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.historyNode.historyAppearsCleared = true let statusText: String - if case .forEveryone = type { + if strongSelf.presentationInterfaceState.isScheduledMessages { + statusText = strongSelf.presentationData.strings.Undo_ScheduledMessagesCleared + } else if case .forEveryone = type { statusText = strongSelf.presentationData.strings.Undo_ChatClearedForBothSides } else { statusText = strongSelf.presentationData.strings.Undo_ChatCleared @@ -4661,16 +4697,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let actionSheet = ActionSheetController(presentationTheme: self.presentationData.theme) var items: [ActionSheetItem] = [] - - if canRemoveGlobally { + + if self.presentationInterfaceState.isScheduledMessages { + items.append(ActionSheetButtonItem(title: self.presentationData.strings.ScheduledMessages_ClearAllConfirmation, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + beginClear(.scheduledMessages) + })) + } else if canRemoveGlobally { items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: self.presentationData.strings)) items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in beginClear(.forEveryone) actionSheet?.dismissAnimated() })) items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in - beginClear(.forLocalPeer) actionSheet?.dismissAnimated() + beginClear(.forLocalPeer) })) } else { items.append(ActionSheetTextItem(title: text)) @@ -4876,13 +4917,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Chat_AttachmentMultipleFilesDisabled, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - }, sendMessagesWithSignals: { [weak self] signals, silentPosting in + }, sendMessagesWithSignals: { [weak self] signals, mode in if !inputText.string.isEmpty { //strongSelf.clearInputText() } if editMediaOptions != nil { self?.editMessageMediaWithLegacySignals(signals!) } else { + var silentPosting = false + //if mode == self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting) } }, selectRecentlyUsedInlineBot: { [weak self] peer in @@ -5043,9 +5086,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Chat_AttachmentLimitReached, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) }) controller.descriptionGenerator = legacyAssetPickerItemGenerator() - controller.completionBlock = { [weak legacyController] signals, silentPosting in + controller.completionBlock = { [weak legacyController] signals, mode in if let legacyController = legacyController { legacyController.dismiss() + var silentPosting = false completion(signals!, silentPosting) } } @@ -5149,7 +5193,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) strongSelf.sendMessages([message]) } - }, theme: strongSelf.presentationData.theme), in: .window(.root)) + }, theme: strongSelf.presentationData.theme, hasLiveLocation: !strongSelf.presentationInterfaceState.isScheduledMessages), in: .window(.root)) }) } @@ -5292,10 +5336,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - private func sendScheduledMessagesNow(_ messageId: [MessageId]) { - let _ = sendScheduledMessageNowInteractively(postbox: self.context.account.postbox, messageId: messageId.first!).start() - } - private func sendMessages(_ messages: [EnqueueMessage], commit: Bool = false) { guard case let .peer(peerId) = self.chatLocation else { return @@ -5327,12 +5367,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - private func enqueueMediaMessages(signals: [Any]?, silentPosting: Bool) { + private func enqueueMediaMessages(signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil) { if case .peer = self.chatLocation { self.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(account: self.context.account, signals: signals!) |> deliverOnMainQueue).start(next: { [weak self] messages in if let strongSelf = self { - let messages = strongSelf.transformEnqueueMessages(messages, silentPosting: silentPosting) + let messages = strongSelf.transformEnqueueMessages(messages, silentPosting: silentPosting, scheduleTime: scheduleTime) let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({ if let strongSelf = self { @@ -5838,9 +5878,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, (messageId.peerId != peerId && !forceInCurrentChat) || (self.presentationInterfaceState.isScheduledMessages && messageId.id != 0) { + if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, (messageId.peerId != peerId && !forceInCurrentChat) || (self.presentationInterfaceState.isScheduledMessages && messageId.id != 0 && !Namespaces.Message.allScheduled.contains(messageId.namespace)) { if let navigationController = self.navigationController as? NavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageId.peerId), messageId: messageId, keepStack: .always)) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageId.peerId), subject: .message(messageId), keepStack: .always)) } } else if case let .peer(peerId) = self.chatLocation, (messageLocation.peerId == peerId || forceInCurrentChat) { if let fromIndex = fromIndex { @@ -5853,6 +5893,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.messageIndexDisposable.set(nil) self.chatDisplayNode.historyNode.scrollToMessage(from: fromIndex, to: message.index, animated: animated, scrollPosition: scrollPosition) completion?() + } else if case let .index(index) = messageLocation, index.id.id == 0 && index.timestamp > 0, self.presentationInterfaceState.isScheduledMessages { + self.chatDisplayNode.historyNode.scrollToMessage(from: fromIndex, to: index, animated: animated, scrollPosition: scrollPosition) } else { self.loadingMessage.set(true) let searchLocation: ChatHistoryInitialSearchLocation @@ -6117,7 +6159,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } })) - case let .chat(textInputState, messageId): + case let .chat(textInputState, subject): if let textInputState = textInputState { let _ = (self.context.account.postbox.transaction({ transaction -> Void in transaction.updatePeerChatInterfaceState(peerId, update: { currentState in @@ -6130,11 +6172,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) |> deliverOnMainQueue).start(completed: { [weak self] in if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), messageId: messageId, updateTextInputState: textInputState)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, updateTextInputState: textInputState)) } }) } else { - (self.navigationController as? NavigationController)?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), subject: messageId.flatMap({ .message($0) }))) + (self.navigationController as? NavigationController)?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), subject: subject)) } case let .withBotStartPayload(botStart): (self.navigationController as? NavigationController)?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), botStart: botStart)) @@ -6253,7 +6295,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var navigation = navigation if case .default = navigation { if let peer = peer as? TelegramUser, peer.botInfo != nil { - navigation = .chat(textInputState: nil, messageId: nil) + navigation = .chat(textInputState: nil, subject: nil) } } strongSelf.openResolved(.peer(peer.id, navigation)) @@ -6504,13 +6546,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } switch navigation { - case let .chat(_, messageId): + case let .chat(_, subject): if case .peer(peerId) = strongSelf.chatLocation { - if let messageId = messageId { + if let subject = subject, case let .message(messageId) = subject { strongSelf.navigateToMessage(from: nil, to: .id(messageId)) } } else if let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), messageId: messageId, keepStack: .always)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, keepStack: .always)) } case .info: strongSelf.navigationActionDisposable.set((strongSelf.context.account.postbox.loadedPeerWithId(peerId) diff --git a/submodules/TelegramUI/TelegramUI/ChatEmptyNode.swift b/submodules/TelegramUI/TelegramUI/ChatEmptyNode.swift index 4a6914766f..3cfdd04a63 100644 --- a/submodules/TelegramUI/TelegramUI/ChatEmptyNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatEmptyNode.swift @@ -455,8 +455,8 @@ final class ChatEmptyNode: ASDisplayNode { } let contentType: ChatEmptyNodeContentType - if let peer = interfaceState.renderedPeer?.peer { - if peer.id == self.accountPeerId && !interfaceState.isScheduledMessages { + if let peer = interfaceState.renderedPeer?.peer, !interfaceState.isScheduledMessages { + if peer.id == self.accountPeerId { contentType = .cloud } else if let _ = peer as? TelegramSecretChat { contentType = .secret diff --git a/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift b/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift index 6efa58a4db..733d040d61 100644 --- a/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift @@ -1113,10 +1113,6 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Scroll(index: .message(toIndex), anchorIndex: .message(toIndex), sourceIndex: .message(fromIndex), scrollPosition: scrollPosition, animated: animated), id: self.takeNextHistoryLocationId()) } - func scrollWithDeltaOffset(_ offset: CGFloat) { - - } - public func anchorMessageInCurrentHistoryView() -> Message? { if let historyView = self.historyView { if let visibleRange = self.displayedItemRange.visibleRange { diff --git a/submodules/TelegramUI/TelegramUI/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/TelegramUI/ChatHistoryViewForLocation.swift index ae12d1ee33..ceb3afdf85 100644 --- a/submodules/TelegramUI/TelegramUI/ChatHistoryViewForLocation.swift +++ b/submodules/TelegramUI/TelegramUI/ChatHistoryViewForLocation.swift @@ -26,36 +26,36 @@ func preloadedChatHistoryViewForLocation(_ location: ChatHistoryLocationInput, a } func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, account: Account, chatLocation: ChatLocation, scheduled: Bool, fixedCombinedReadStates: MessageHistoryViewReadState?, tagMask: MessageTags?, additionalData: [AdditionalMessageHistoryViewData], orderStatistics: MessageHistoryViewOrderStatistics = []) -> Signal { - if scheduled { - var preloaded = false - var fadeIn = false + var first = true + var chatScrollPosition: ChatHistoryViewScrollPosition? + if case let .Scroll(index, _, sourceIndex, position, animated) = location.content { + let directionHint: ListViewScrollToItemDirectionHint = sourceIndex > index ? .Down : .Up + chatScrollPosition = .index(index: index, position: position, directionHint: directionHint, animated: animated) + } return account.viewTracker.scheduledMessagesViewForLocation(chatLocation) |> map { view, updateType, initialData -> ChatHistoryViewUpdate in let (cachedData, cachedDataMessages, readStateData) = extractAdditionalData(view: view, chatLocation: chatLocation) let combinedInitialData = ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData) - if preloaded { - return .HistoryView(view: view, type: .Generic(type: updateType), scrollPosition: nil, flashIndicators: false, originalScrollPosition: nil, initialData: combinedInitialData, id: location.id) - } else { - if view.isLoading { - return .Loading(initialData: combinedInitialData, type: .Generic(type: updateType)) - } - var scrollPosition: ChatHistoryViewScrollPosition? - -// if let historyScrollState = (initialData?.chatInterfaceState as? ChatInterfaceState)?.historyScrollState, tagMask == nil { -// scrollPosition = .positionRestoration(index: historyScrollState.messageIndex, relativeOffset: CGFloat(historyScrollState.relativeOffset)) -// } else { -// if view.entries.isEmpty && (view.holeEarlier || view.holeLater) { -// fadeIn = true -// return .Loading(initialData: combinedInitialData, type: .Generic(type: updateType)) -// } -// } - - preloaded = true - return .HistoryView(view: view, type: .Initial(fadeIn: fadeIn), scrollPosition: scrollPosition, flashIndicators: false, originalScrollPosition: nil, initialData: ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData), id: location.id) + if view.isLoading { + return .Loading(initialData: combinedInitialData, type: .Generic(type: updateType)) } + + let type: ChatHistoryViewUpdateType + let scrollPosition: ChatHistoryViewScrollPosition? = first ? chatScrollPosition : nil + if first { + first = false + if chatScrollPosition == nil { + type = .Initial(fadeIn: false) + } else { + type = .Generic(type: .UpdateVisible) + } + } else { + type = .Generic(type: updateType) + } + return .HistoryView(view: view, type: type, scrollPosition: scrollPosition, flashIndicators: false, originalScrollPosition: chatScrollPosition, initialData: ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData), id: location.id) } } else { switch location.content { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift index 9c1ba28f25..fcef87b219 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift @@ -379,8 +379,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { avatarInset = 0.0 } + let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) + var needShareButton = false - if item.message.id.peerId == item.context.account.peerId { + if isFailed { + needShareButton = false + } else if item.message.id.peerId == item.context.account.peerId { for attribute in item.content.firstMessage.attributes { if let _ = attribute as? SourceReferenceMessageAttribute { needShareButton = true @@ -424,7 +428,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } var deliveryFailedInset: CGFloat = 0.0 - if item.content.firstMessage.flags.contains(.Failed) { + if isFailed { deliveryFailedInset += 24.0 } @@ -446,7 +450,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if item.message.effectivelyIncoming(item.context.account.peerId) { statusType = .FreeIncoming } else { - if item.message.flags.contains(.Failed) { + if isFailed { statusType = .FreeOutgoing(.Failed) } else if item.message.flags.isSending && !item.message.isSentOrAcknowledged { statusType = .FreeOutgoing(.Sending) @@ -659,7 +663,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { strongSelf.replyInfoNode = nil } - if item.content.firstMessage.flags.contains(.Failed) { + if isFailed { let deliveryFailedNode: ChatMessageDeliveryFailedNode var isAppearing = false if let current = strongSelf.deliveryFailedNode { @@ -740,7 +744,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { var navigate: ChatControllerInteractionNavigateToPeer if item.content.firstMessage.id.peerId == item.context.account.peerId { - navigate = .chat(textInputState: nil, messageId: nil) + navigate = .chat(textInputState: nil, subject: nil) } else { navigate = .info } @@ -748,7 +752,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { for attribute in item.content.firstMessage.attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { openPeerId = attribute.messageId.peerId - navigate = .chat(textInputState: nil, messageId: attribute.messageId) + navigate = .chat(textInputState: nil, subject: .message(attribute.messageId)) } } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift index 56be379212..63243f3871 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift @@ -666,8 +666,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { avatarInset = 0.0 } + let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) + var needShareButton = false - if item.message.flags.contains(.Failed) { + if isFailed { needShareButton = false } else if item.message.id.peerId == item.context.account.peerId { if let _ = sourceReference { @@ -731,7 +733,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { } var deliveryFailedInset: CGFloat = 0.0 - if item.content.firstMessage.flags.contains(.Failed) { + if isFailed { deliveryFailedInset += 24.0 } @@ -1032,7 +1034,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { if message.effectivelyIncoming(item.context.account.peerId) { statusType = .ImageIncoming } else { - if message.flags.contains(.Failed) { + if isFailed { statusType = .ImageOutgoing(.Failed) } else if message.flags.isSending && !message.isSentOrAcknowledged { statusType = .ImageOutgoing(.Sending) @@ -1587,7 +1589,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { strongSelf.backgroundType = backgroundType - if item.content.firstMessage.flags.contains(.Failed) { + let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) + if isFailed { let deliveryFailedNode: ChatMessageDeliveryFailedNode var isAppearing = false if let current = strongSelf.deliveryFailedNode { @@ -2133,7 +2136,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { var navigate: ChatControllerInteractionNavigateToPeer if item.content.firstMessage.id.peerId == item.context.account.peerId { - navigate = .chat(textInputState: nil, messageId: nil) + navigate = .chat(textInputState: nil, subject: nil) } else { navigate = .info } @@ -2141,7 +2144,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { for attribute in item.content.firstMessage.attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { openPeerId = attribute.messageId.peerId - navigate = .chat(textInputState: nil, messageId: attribute.messageId) + navigate = .chat(textInputState: nil, subject: .message(attribute.messageId)) } } @@ -2225,7 +2228,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { break loop case let .peerMention(peerId, _): foundTapAction = true - self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, messageId: nil), nil) + self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil), nil) break loop case let .textMention(name): foundTapAction = true diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift index 6758350385..f0763787e9 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift @@ -580,7 +580,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { var navigate: ChatControllerInteractionNavigateToPeer if item.content.firstMessage.id.peerId == item.context.account.peerId { - navigate = .chat(textInputState: nil, messageId: nil) + navigate = .chat(textInputState: nil, subject: nil) } else { navigate = .info } @@ -588,7 +588,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { for attribute in item.content.firstMessage.attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { openPeerId = attribute.messageId.peerId - navigate = .chat(textInputState: nil, messageId: attribute.messageId) + navigate = .chat(textInputState: nil, subject: .message(attribute.messageId)) } } @@ -631,7 +631,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { } item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId) } else if let id = forwardInfo.source?.id ?? forwardInfo.author?.id { - item.controllerInteraction.openPeer(id, .chat(textInputState: nil, messageId: nil), nil) + item.controllerInteraction.openPeer(id, .chat(textInputState: nil, subject: nil), nil) } else if let _ = forwardInfo.authorSignature { item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil) } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift index 262e137a64..eeb2a51b28 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift @@ -181,8 +181,10 @@ class ChatMessageStickerItemNode: ChatMessageItemView { avatarInset = 0.0 } + let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) + var needShareButton = false - if item.message.flags.contains(.Failed) { + if isFailed { needShareButton = false } else if item.message.id.peerId == item.context.account.peerId { for attribute in item.content.firstMessage.attributes { @@ -228,7 +230,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } var deliveryFailedInset: CGFloat = 0.0 - if item.content.firstMessage.flags.contains(.Failed) { + if isFailed { deliveryFailedInset += 24.0 } @@ -246,7 +248,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { if item.message.effectivelyIncoming(item.context.account.peerId) { statusType = .FreeIncoming } else { - if item.message.flags.contains(.Failed) { + if isFailed { statusType = .FreeOutgoing(.Failed) } else if item.message.flags.isSending && !item.message.isSentOrAcknowledged { statusType = .FreeOutgoing(.Sending) @@ -503,7 +505,8 @@ class ChatMessageStickerItemNode: ChatMessageItemView { strongSelf.replyInfoNode = nil } - if item.content.firstMessage.flags.contains(.Failed) { + + if isFailed { let deliveryFailedNode: ChatMessageDeliveryFailedNode var isAppearing = false if let current = strongSelf.deliveryFailedNode { @@ -584,7 +587,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { var navigate: ChatControllerInteractionNavigateToPeer if item.content.firstMessage.id.peerId == item.context.account.peerId { - navigate = .chat(textInputState: nil, messageId: nil) + navigate = .chat(textInputState: nil, subject: nil) } else { navigate = .info } @@ -592,7 +595,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { for attribute in item.content.firstMessage.attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { openPeerId = attribute.messageId.peerId - navigate = .chat(textInputState: nil, messageId: attribute.messageId) + navigate = .chat(textInputState: nil, subject: .message(attribute.messageId)) } } diff --git a/submodules/TelegramUI/TelegramUI/ChatPresentationData.swift b/submodules/TelegramUI/TelegramUI/ChatPresentationData.swift index 5a0d87496f..58acf63749 100644 --- a/submodules/TelegramUI/TelegramUI/ChatPresentationData.swift +++ b/submodules/TelegramUI/TelegramUI/ChatPresentationData.swift @@ -48,6 +48,8 @@ public final class ChatPresentationData { let nameDisplayOrder: PresentationPersonNameOrder let disableAnimations: Bool let largeEmoji: Bool + let animatedEmojiScale: CGFloat + let isPreview: Bool let messageFont: UIFont let messageEmojiFont1: UIFont @@ -59,9 +61,7 @@ public final class ChatPresentationData { let messageFixedFont: UIFont let messageBlockQuoteFont: UIFont - let animatedEmojiScale: CGFloat - - init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool, animatedEmojiScale: CGFloat = 1.0) { + init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool, animatedEmojiScale: CGFloat = 1.0, isPreview: Bool = false) { self.theme = theme self.fontSize = fontSize self.strings = strings @@ -69,6 +69,7 @@ public final class ChatPresentationData { self.nameDisplayOrder = nameDisplayOrder self.disableAnimations = disableAnimations self.largeEmoji = largeEmoji + self.isPreview = isPreview let baseFontSize = fontSize.baseDisplaySize self.messageFont = UIFont.systemFont(ofSize: baseFontSize) diff --git a/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift index 65c1109da2..0e377b5f14 100644 --- a/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift @@ -785,12 +785,11 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { strongSelf.controllerInteraction.presentController(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_ErrorInaccessibleMessage, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) case .botStart: break - //strongSelf.openPeer(peerId: peerId, navigation: .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .interactive)), fromMessage: nil) case .groupBotStart: break case let .channelMessage(peerId, messageId): if let navigationController = strongSelf.getNavigationController() { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), messageId: messageId)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: .message(messageId))) } case let .stickerPack(name): strongSelf.presentController(StickerPackPreviewController(context: strongSelf.context, stickerPack: .name(name), parentNavigationController: strongSelf.getNavigationController()), nil) diff --git a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift index 2b5b3ebc45..37a54dc28b 100644 --- a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift @@ -118,6 +118,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel strongSelf.updateMinimumDate() strongSelf.pickerView.layer.addShakeAnimation() } else { + strongSelf.doneButton.isUserInteractionEnabled = false strongSelf.completion?(Int32(strongSelf.pickerView.date.timeIntervalSince1970)) } } diff --git a/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift index 64e78945e9..c3deda52ee 100644 --- a/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift @@ -23,7 +23,7 @@ private enum ChatSendMessageActionIcon { case .schedule: imageName = "Chat/Input/Menu/ScheduleIcon" } - return generateTintedImage(image: UIImage(bundleImageName: imageName), color: theme.actionSheet.primaryTextColor) + return generateTintedImage(image: UIImage(bundleImageName: imageName), color: theme.contextMenu.primaryColor) } } @@ -33,6 +33,7 @@ private final class ActionSheetItemNode: ASDisplayNode { private let action: () -> Void private let separatorNode: ASDisplayNode + private let backgroundNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode private let buttonNode: HighlightTrackingButtonNode private let iconNode: ASImageNode @@ -46,21 +47,35 @@ private final class ActionSheetItemNode: ASDisplayNode { self.action = action self.separatorNode = ASDisplayNode() - self.separatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor + self.separatorNode.backgroundColor = theme.contextMenu.itemSeparatorColor + + self.backgroundNode = ASDisplayNode() + self.backgroundNode.isAccessibilityElement = false + self.backgroundNode.backgroundColor = theme.contextMenu.itemBackgroundColor self.highlightedBackgroundNode = ASDisplayNode() - self.highlightedBackgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor + self.highlightedBackgroundNode.isAccessibilityElement = false + self.highlightedBackgroundNode.backgroundColor = theme.contextMenu.itemHighlightedBackgroundColor self.highlightedBackgroundNode.alpha = 0.0 self.buttonNode = HighlightTrackingButtonNode() + self.buttonNode.isAccessibilityElement = true + self.buttonNode.accessibilityLabel = title self.titleNode = ImmediateTextNode() + self.titleNode.isAccessibilityElement = false self.titleNode.maximumNumberOfLines = 1 - self.titleNode.attributedText = NSAttributedString(string: title, font: Font.regular(17.0), textColor: theme.actionSheet.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.regular(17.0), textColor: theme.contextMenu.primaryColor) + self.titleNode.isUserInteractionEnabled = false + self.titleNode.displaysAsynchronously = false self.iconNode = ASImageNode() self.iconNode.image = icon.image(theme: theme) self.iconNode.contentMode = .center + self.iconNode.isAccessibilityElement = false + self.iconNode.displaysAsynchronously = false + self.iconNode.displayWithoutProcessing = true + self.iconNode.isUserInteractionEnabled = false super.init() @@ -87,9 +102,10 @@ private final class ActionSheetItemNode: ASDisplayNode { } func updateTheme(_ theme: PresentationTheme) { - self.separatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor - self.highlightedBackgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.regular(17.0), textColor: theme.actionSheet.primaryTextColor) + self.separatorNode.backgroundColor = theme.contextMenu.itemSeparatorColor + self.backgroundNode.backgroundColor = theme.contextMenu.itemBackgroundColor + self.highlightedBackgroundNode.backgroundColor = theme.contextMenu.itemHighlightedBackgroundColor + self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.regular(17.0), textColor: theme.contextMenu.primaryColor) self.iconNode.image = self.icon.image(theme: theme) if let maxWidth = self.maxWidth { @@ -180,11 +196,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.dimNode = ASDisplayNode() self.dimNode.alpha = 1.0 - if self.presentationData.theme.chatList.searchBarKeyboardColor == .light { - self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.04) - } else { - self.dimNode.backgroundColor = presentationData.theme.chatList.backgroundColor.withAlphaComponent(0.2) - } + self.dimNode.backgroundColor = self.presentationData.theme.contextMenu.dimColor self.sendButtonNode = HighlightableButtonNode() self.sendButtonNode.imageNode.displayWithoutProcessing = false @@ -205,8 +217,8 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.scrollNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) self.contentContainerNode = ASDisplayNode() - self.contentContainerNode.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor - self.contentContainerNode.cornerRadius = 12.0 + self.contentContainerNode.backgroundColor = self.presentationData.theme.contextMenu.backgroundColor + self.contentContainerNode.cornerRadius = 14.0 self.contentContainerNode.clipsToBounds = true var contentNodes: [ActionSheetItemNode] = [] @@ -296,19 +308,18 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, } self.presentationData = presentationData - if self.presentationData.theme.chatList.searchBarKeyboardColor == .dark { - self.effectView.effect = UIBlurEffect(style: .dark) + if #available(iOS 9.0, *) { } else { - self.effectView.effect = UIBlurEffect(style: .light) + if self.presentationData.theme.chatList.searchBarKeyboardColor == .dark { + self.effectView.effect = UIBlurEffect(style: .dark) + } else { + self.effectView.effect = UIBlurEffect(style: .light) + } } - if self.presentationData.theme.chatList.searchBarKeyboardColor == .light { - self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.04) - } else { - self.dimNode.backgroundColor = presentationData.theme.chatList.backgroundColor.withAlphaComponent(0.2) - } + self.dimNode.backgroundColor = presentationData.theme.contextMenu.dimColor - self.contentContainerNode.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor + self.contentContainerNode.backgroundColor = self.presentationData.theme.contextMenu.backgroundColor self.textCoverNode.backgroundColor = self.presentationData.theme.chat.inputPanel.inputBackgroundColor self.buttonCoverNode.backgroundColor = self.presentationData.theme.chat.inputPanel.panelBackgroundColor self.sendButtonNode.setImage(PresentationResourcesChat.chatInputPanelSendButtonImage(self.presentationData.theme), for: []) @@ -329,39 +340,14 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, func animateIn() { self.textInputNode.textView.setContentOffset(self.textInputNode.textView.contentOffset, animated: false) - UIView.animate(withDuration: 0.4, animations: { + UIView.animate(withDuration: 0.2, animations: { if #available(iOS 9.0, *) { - if self.presentationData.theme.chatList.searchBarKeyboardColor == .dark { - if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { - self.effectView.effect = UIBlurEffect(style: .regular) - if self.effectView.subviews.count == 2 { - self.effectView.subviews[1].isHidden = true - } - } else { - self.effectView.effect = UIBlurEffect(style: .dark) - } - } else { - if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { - self.effectView.effect = UIBlurEffect(style: .regular) - } else { - self.effectView.effect = UIBlurEffect(style: .light) - } - } + self.effectView.effect = makeCustomZoomBlurEffect() } else { self.effectView.alpha = 1.0 } - }, completion: { [weak self] _ in - guard let strongSelf = self else { - return - } - if strongSelf.presentationData.theme.chatList.searchBarKeyboardColor == .dark { - if strongSelf.effectView.subviews.count == 2 { - strongSelf.effectView.subviews[1].isHidden = true - } - } - }) - self.effectView.subviews[1].layer.removeAnimation(forKey: "backgroundColor") - self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) + }, completion: { _ in }) + self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.contentContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.messageBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) @@ -401,9 +387,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, let textOffset = self.textInputNode.textView.contentSize.height - self.textInputNode.textView.contentOffset.y - self.textInputNode.textView.frame.height self.fromMessageTextNode.layer.animatePosition(from: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true) self.toMessageTextNode.layer.animatePosition(from: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true) - - self.contentContainerNode.layer.animatePosition(from: CGPoint(x: 160.0, y: 0.0), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true) - self.contentContainerNode.layer.animateScale(from: 0.45, to: 1.0, duration: duration, timingFunction: kCAMediaTimingFunctionSpring) + + let springDuration: Double = 0.42 + let springDamping: CGFloat = 104.0 + self.contentContainerNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping) + self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: 160.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true) } } @@ -436,7 +424,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, intermediateCompletion() }) - self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in }) if cancel { @@ -522,7 +510,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, let sideInset: CGFloat = 43.0 var contentSize = CGSize() - contentSize.width = min(layout.size.width - 40.0, 240.0) + contentSize.width = min(layout.size.width - 40.0, 250.0) var applyNodes: [(ASDisplayNode, CGFloat, (CGFloat) -> Void)] = [] for itemNode in self.contentNodes { let (width, height, apply) = itemNode.updateLayout(maxWidth: layout.size.width - sideInset * 2.0) diff --git a/submodules/TelegramUI/TelegramUI/DebugController.swift b/submodules/TelegramUI/TelegramUI/DebugController.swift index 125465f295..0c96ed38dd 100644 --- a/submodules/TelegramUI/TelegramUI/DebugController.swift +++ b/submodules/TelegramUI/TelegramUI/DebugController.swift @@ -57,7 +57,6 @@ private enum DebugControllerEntry: ItemListNodeEntry { case resetData(PresentationTheme) case resetDatabase(PresentationTheme) case resetHoles(PresentationTheme) - case resetBiometricsData(PresentationTheme) case optimizeDatabase(PresentationTheme) case photoPreview(PresentationTheme, Bool) case knockoutWallpaper(PresentationTheme, Bool) @@ -74,7 +73,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { return DebugControllerSection.logging.rawValue case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries: return DebugControllerSection.experiments.rawValue - case .clearTips, .reimport, .resetData, .resetDatabase, .resetHoles, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .exportTheme: + case .clearTips, .reimport, .resetData, .resetDatabase, .resetHoles, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .exportTheme: return DebugControllerSection.experiments.rawValue case .versionInfo: return DebugControllerSection.info.rawValue @@ -117,18 +116,16 @@ private enum DebugControllerEntry: ItemListNodeEntry { return 15 case .resetHoles: return 16 - case .resetBiometricsData: - return 17 case .optimizeDatabase: - return 18 + return 17 case .photoPreview: - return 19 + return 18 case .knockoutWallpaper: - return 21 + return 19 case .exportTheme: - return 22 + return 20 case .versionInfo: - return 23 + return 21 } } @@ -443,12 +440,6 @@ private enum DebugControllerEntry: ItemListNodeEntry { controller.dismiss() }) }) - case let .resetBiometricsData(theme): - return ItemListActionItem(theme: theme, title: "Reset Biometrics Data", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { - let _ = updatePresentationPasscodeSettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in - return settings.withUpdatedBiometricsDomainState(nil).withUpdatedShareBiometricsDomainState(nil) - }).start() - }) case let .optimizeDatabase(theme): return ItemListActionItem(theme: theme, title: "Optimize Database", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { guard let context = arguments.context else { diff --git a/submodules/TelegramUI/TelegramUI/LegacyCamera.swift b/submodules/TelegramUI/TelegramUI/LegacyCamera.swift index b8af1a9efd..0dc3f57023 100644 --- a/submodules/TelegramUI/TelegramUI/LegacyCamera.swift +++ b/submodules/TelegramUI/TelegramUI/LegacyCamera.swift @@ -63,8 +63,9 @@ func presentedLegacyCamera(context: AccountContext, peer: Peer, cameraView: TGAt if peer is TelegramUser { controller.hasTimer = true } - controller.hasSilentPosting = true + controller.hasSilentPosting = !isSecretChat } + controller.hasSchedule = !isSecretChat let screenSize = parentController.view.bounds.size var startFrame = CGRect(x: 0, y: screenSize.height, width: screenSize.width, height: screenSize.height) diff --git a/submodules/TelegramUI/TelegramUI/NavigateToChatController.swift b/submodules/TelegramUI/TelegramUI/NavigateToChatController.swift index 48d8e757b3..fd99abd103 100644 --- a/submodules/TelegramUI/TelegramUI/NavigateToChatController.swift +++ b/submodules/TelegramUI/TelegramUI/NavigateToChatController.swift @@ -13,11 +13,11 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam var found = false var isFirst = true for controller in params.navigationController.viewControllers.reversed() { - if let controller = controller as? ChatControllerImpl, controller.chatLocation == params.chatLocation && controller.subject != .scheduledMessages { + if let controller = controller as? ChatControllerImpl, controller.chatLocation == params.chatLocation && (controller.subject != .scheduledMessages || controller.subject == params.subject) { if let updateTextInputState = params.updateTextInputState { controller.updateTextInputState(updateTextInputState) } - if let messageId = params.messageId { + if let subject = params.subject, case let .message(messageId) = subject { let navigationController = params.navigationController let animated = params.animated controller.navigateToMessage(messageLocation: .id(messageId), animated: isFirst, completion: { [weak navigationController, weak controller] in @@ -60,7 +60,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam } } } else { - controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation, subject: params.messageId.flatMap({ .message($0) }), botStart: params.botStart) + controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation, subject: params.subject, botStart: params.botStart) } controller.purposefulAction = params.purposefulAction let resolvedKeepStack: Bool diff --git a/submodules/TelegramUI/TelegramUI/OpenResolvedUrl.swift b/submodules/TelegramUI/TelegramUI/OpenResolvedUrl.swift index c8e6f00fd6..97471f8e52 100644 --- a/submodules/TelegramUI/TelegramUI/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/TelegramUI/OpenResolvedUrl.swift @@ -18,9 +18,9 @@ private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatContr if case .default = navigation { if let peerId = peerId { if peerId.namespace == Namespaces.Peer.CloudUser { - return .chat(textInputState: nil, messageId: nil) + return .chat(textInputState: nil, subject: nil) } else { - return .chat(textInputState: nil, messageId: nil) + return .chat(textInputState: nil, subject: nil) } } else { return .info @@ -81,7 +81,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur dismissInput() present(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet)) case let .channelMessage(peerId, messageId): - openPeer(peerId, .chat(textInputState: nil, messageId: messageId)) + openPeer(peerId, .chat(textInputState: nil, subject: .message(messageId))) case let .stickerPack(name): dismissInput() let controller = StickerPackPreviewController(context: context, stickerPack: .name(name), parentNavigationController: navigationController) @@ -92,7 +92,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur case let .join(link): dismissInput() present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: { peerId in - openPeer(peerId, .chat(textInputState: nil, messageId: nil)) + openPeer(peerId, .chat(textInputState: nil, subject: nil)) }), nil) case let .localization(identifier): dismissInput() diff --git a/submodules/TelegramUI/TelegramUI/OpenUrl.swift b/submodules/TelegramUI/TelegramUI/OpenUrl.swift index dff3178505..44bfd4fd27 100644 --- a/submodules/TelegramUI/TelegramUI/OpenUrl.swift +++ b/submodules/TelegramUI/TelegramUI/OpenUrl.swift @@ -198,10 +198,10 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur navigationController?.pushViewController(infoController) } }) - case let .chat(_, messageId): + case let .chat(_, subject): context.sharedContext.applicationBindings.dismissNativeController() if let navigationController = navigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), messageId: messageId)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: subject)) } case let .withBotStartPayload(payload): context.sharedContext.applicationBindings.dismissNativeController() diff --git a/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift b/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift index f44edfbf6a..3eb05773a6 100644 --- a/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift +++ b/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift @@ -132,7 +132,7 @@ public class PeerMediaCollectionController: TelegramBaseController { ActionSheetButtonItem(title: strongSelf.presentationData.strings.SharedMedia_ViewInChat, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), messageId: message.id)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), subject: .message(message.id))) } }), ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_ContextMenuForward, color: .accent, action: { [weak actionSheet] in @@ -606,9 +606,9 @@ public class PeerMediaCollectionController: TelegramBaseController { strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.navigationController as? NavigationController, openPeer: { peerId, navigation in if let strongSelf = self { switch navigation { - case let .chat(_, messageId): + case let .chat(_, subject): if let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), messageId: messageId, keepStack: .always)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, keepStack: .always)) } case .info: strongSelf.navigationActionDisposable.set((strongSelf.context.account.postbox.loadedPeerWithId(peerId) diff --git a/submodules/TelegramUI/TelegramUI/PeerMessagesMediaPlaylist.swift b/submodules/TelegramUI/TelegramUI/PeerMessagesMediaPlaylist.swift index 232b7875c7..54d2bbab4d 100644 --- a/submodules/TelegramUI/TelegramUI/PeerMessagesMediaPlaylist.swift +++ b/submodules/TelegramUI/TelegramUI/PeerMessagesMediaPlaylist.swift @@ -487,7 +487,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { self.loadingItem = true self.updateState() - let namespaces: HistoryViewNamespaces + let namespaces: MessageIdNamespaces if Namespaces.Message.allScheduled.contains(anchor.id.namespace) { namespaces = .just(Namespaces.Message.allScheduled) } else { diff --git a/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping b/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping index dfbb09288be2a683a61d03b642f0ac73ec0a3bfb..48966ed8aa8a59e95ddfcbf5e32580defb14d108 100644 GIT binary patch delta 30128 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 NeKpbwu|J_Y{{#0&VEh08 delta 30330 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%0DVxy8(RnX=CueXf3Qg;r`AkvzPXeqV*xa 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 JNh2i_&%ds&lnnp? diff --git a/submodules/TelegramUI/TelegramUI/TextLinkHandling.swift b/submodules/TelegramUI/TelegramUI/TextLinkHandling.swift index b938b39131..cf65db921c 100644 --- a/submodules/TelegramUI/TelegramUI/TextLinkHandling.swift +++ b/submodules/TelegramUI/TelegramUI/TextLinkHandling.swift @@ -22,9 +22,9 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate let openResolvedPeerImpl: (PeerId?, ChatControllerInteractionNavigateToPeer) -> Void = { [weak controller] peerId, navigation in context.sharedContext.openResolvedUrl(.peer(peerId, navigation), context: context, urlContext: .generic, navigationController: (controller?.navigationController as? NavigationController), openPeer: { (peerId, navigation) in switch navigation { - case let .chat(_, messageId): + case let .chat(_, subject): if let navigationController = controller?.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), messageId: messageId, keepStack: .always)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: subject, keepStack: .always)) } case .info: let peerSignal: Signal @@ -54,7 +54,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate openResolvedPeerImpl(peerId, .default) case let .channelMessage(peerId, messageId): if let navigationController = controller.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), messageId: messageId)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: .message(messageId))) } case let .stickerPack(name): controller.present(StickerPackPreviewController(context: context, stickerPack: .name(name), parentNavigationController: controller.navigationController as? NavigationController), in: .window(.root)) @@ -62,7 +62,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate (controller.navigationController as? NavigationController)?.pushViewController(InstantPageController(context: context, webPage: webpage, sourcePeerType: .group, anchor: anchor)) case let .join(link): controller.present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: { peerId in - openResolvedPeerImpl(peerId, .chat(textInputState: nil, messageId: nil)) + openResolvedPeerImpl(peerId, .chat(textInputState: nil, subject: nil)) }), in: .window(.root)) default: break diff --git a/submodules/TelegramUI/TelegramUI/UrlHandling.swift b/submodules/TelegramUI/TelegramUI/UrlHandling.swift index 9e818703f8..21df4622ad 100644 --- a/submodules/TelegramUI/TelegramUI/UrlHandling.swift +++ b/submodules/TelegramUI/TelegramUI/UrlHandling.swift @@ -248,9 +248,9 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig } } else { if let peer = peer as? TelegramUser, peer.botInfo == nil { - return .peer(peer.id, .chat(textInputState: nil, messageId: nil)) + return .peer(peer.id, .chat(textInputState: nil, subject: nil)) } else { - return .peer(peer.id, .chat(textInputState: nil, messageId: nil)) + return .peer(peer.id, .chat(textInputState: nil, subject: nil)) } } } else { @@ -263,7 +263,7 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig } |> mapToSignal { peer -> Signal in if let peer = peer { - return .single(.peer(peer.id, .chat(textInputState: nil, messageId: nil))) + return .single(.peer(peer.id, .chat(textInputState: nil, subject: nil))) } else { return .single(.inaccessiblePeer) } @@ -274,12 +274,12 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig } |> mapToSignal { peer -> Signal in if let peer = peer { - return .single(.peer(peer.id, .chat(textInputState: nil, messageId: messageId))) + return .single(.peer(peer.id, .chat(textInputState: nil, subject: .message(messageId)))) } else { return findChannelById(postbox: account.postbox, network: account.network, channelId: messageId.peerId.id) |> map { foundPeer -> ResolvedUrl? in if let foundPeer = foundPeer { - return .peer(foundPeer.id, .chat(textInputState: nil, messageId: messageId)) + return .peer(foundPeer.id, .chat(textInputState: nil, subject: .message(messageId))) } else { return .inaccessiblePeer }