This commit is contained in:
Ali 2021-10-29 19:27:10 +04:00
parent b5d8390a98
commit 03f696fd8f
29 changed files with 402 additions and 192 deletions

View File

@ -7032,3 +7032,5 @@ Sorry for the inconvenience.";
"Themes.BuildOwn" = "Build Your Own Theme";
"Themes.EditCurrentTheme" = "Edit Current Theme";
"Themes.CreateNewTheme" = "Create a New Theme";
"Chat.JumpToDate" = "Jump to Date";

View File

@ -364,6 +364,11 @@ public struct ChatTextInputStateText: Codable, Equatable {
}
public enum ChatControllerSubject: Equatable {
public enum MessageSubject: Equatable {
case id(MessageId)
case timestamp(Int32)
}
public struct ForwardOptions: Equatable {
public let hideNames: Bool
public let hideCaptions: Bool
@ -374,7 +379,7 @@ public enum ChatControllerSubject: Equatable {
}
}
case message(id: EngineMessage.Id, highlight: Bool, timecode: Double?)
case message(id: MessageSubject, highlight: Bool, timecode: Double?)
case scheduledMessages
case pinnedMessages(id: EngineMessage.Id?)
case forwardedMessages(ids: [EngineMessage.Id], options: Signal<ForwardOptions, NoError>)

View File

@ -364,6 +364,7 @@ private final class DayComponent: Component {
let isEnabled: Bool
let theme: PresentationTheme
let context: AccountContext
let timestamp: Int32
let media: DayMedia?
let selection: DaySelection
let isSelecting: Bool
@ -375,6 +376,7 @@ private final class DayComponent: Component {
isEnabled: Bool,
theme: PresentationTheme,
context: AccountContext,
timestamp: Int32,
media: DayMedia?,
selection: DaySelection,
isSelecting: Bool,
@ -385,6 +387,7 @@ private final class DayComponent: Component {
self.isEnabled = isEnabled
self.theme = theme
self.context = context
self.timestamp = timestamp
self.media = media
self.selection = selection
self.isSelecting = isSelecting
@ -410,6 +413,9 @@ private final class DayComponent: Component {
if lhs.media != rhs.media {
return false
}
if lhs.timestamp != rhs.timestamp {
return false
}
if lhs.selection != rhs.selection {
return false
}
@ -430,6 +436,7 @@ private final class DayComponent: Component {
private var action: (() -> Void)?
private var currentMedia: DayMedia?
private(set) var timestamp: Int32?
private(set) var index: MessageIndex?
private var isHighlightingEnabled: Bool = false
@ -473,6 +480,7 @@ private final class DayComponent: Component {
let isFirstTime = self.action == nil
self.action = component.action
self.timestamp = component.timestamp
self.index = component.media?.message.index
self.isHighlightingEnabled = component.isEnabled && component.media != nil && !component.isSelecting
@ -745,6 +753,7 @@ private final class MonthComponent: CombinedComponent {
isEnabled: isEnabled,
theme: context.component.theme,
context: context.component.context,
timestamp: dayTimestamp,
media: context.component.model.mediaByDay[index],
selection: daySelection,
isSelecting: context.component.selectedDays != nil,
@ -959,8 +968,10 @@ public final class CalendarMessageScreen: ViewController {
private let context: AccountContext
private let peerId: PeerId
private let initialTimestamp: Int32
private let navigateToOffset: (Int) -> Void
private let previewDay: (MessageIndex, ASDisplayNode, CGRect, ContextGesture) -> Void
private let enableMessageRangeDeletion: Bool
private let canNavigateToEmptyDays: Bool
private let navigateToOffset: (Int, Int32) -> Void
private let previewDay: (Int32, MessageIndex?, ASDisplayNode, CGRect, ContextGesture) -> Void
private var presentationData: PresentationData
private var scrollView: Scroller
@ -988,11 +999,23 @@ public final class CalendarMessageScreen: ViewController {
private var ignoreContentOffset: Bool = false
init(controller: CalendarMessageScreen, context: AccountContext, peerId: PeerId, calendarSource: SparseMessageCalendar, initialTimestamp: Int32, navigateToOffset: @escaping (Int) -> Void, previewDay: @escaping (MessageIndex, ASDisplayNode, CGRect, ContextGesture) -> Void) {
init(
controller: CalendarMessageScreen,
context: AccountContext,
peerId: PeerId,
calendarSource: SparseMessageCalendar,
initialTimestamp: Int32,
enableMessageRangeDeletion: Bool,
canNavigateToEmptyDays: Bool,
navigateToOffset: @escaping (Int, Int32) -> Void,
previewDay: @escaping (Int32, MessageIndex?, ASDisplayNode, CGRect, ContextGesture) -> Void
) {
self.controller = controller
self.context = context
self.peerId = peerId
self.initialTimestamp = initialTimestamp
self.enableMessageRangeDeletion = enableMessageRangeDeletion
self.canNavigateToEmptyDays = canNavigateToEmptyDays
self.calendarSource = calendarSource
self.navigateToOffset = navigateToOffset
self.previewDay = previewDay
@ -1086,8 +1109,11 @@ public final class CalendarMessageScreen: ViewController {
currentGestureDayView.isUserInteractionEnabled = false
currentGestureDayView.isUserInteractionEnabled = true
if let index = currentGestureDayView.index {
strongSelf.previewDay(index, strongSelf, currentGestureDayView.convert(currentGestureDayView.bounds, to: strongSelf.view), gesture)
if currentGestureDayView.index == nil && !strongSelf.canNavigateToEmptyDays {
return
}
if let timestamp = currentGestureDayView.timestamp {
strongSelf.previewDay(timestamp, currentGestureDayView.index, strongSelf, currentGestureDayView.convert(currentGestureDayView.bounds, to: strongSelf.view), gesture)
}
}
@ -1174,6 +1200,21 @@ public final class CalendarMessageScreen: ViewController {
}
}
func selectDay(timestamp: Int32) {
self.selectionState = SelectionState(dayRange: timestamp ... timestamp)
self.contextGestureContainerNode.isGestureEnabled = self.selectionState == nil
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.5, curve: .spring))
}
}
func openClearHistory(timestamp: Int32) {
self.selectionState = SelectionState(dayRange: timestamp ... timestamp)
self.selectionToolbarActionSelected()
}
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
let isFirstLayout = self.validLayout == nil
self.validLayout = (layout, navigationHeight)
@ -1292,8 +1333,8 @@ public final class CalendarMessageScreen: ViewController {
let dayTimestamp = firstDayTimestamp + 24 * 60 * 60 * Int32(day)
let nextDayTimestamp = dayTimestamp + 24 * 60 * 60
let minDayTimestamp = dayTimestamp - 24 * 60 * 60
let maxDayTimestamp = nextDayTimestamp - 24 * 60 * 60
let minDayTimestamp = dayTimestamp
let maxDayTimestamp = nextDayTimestamp
if dayRange.contains(dayTimestamp) {
if let currentMinTimestamp = minTimestamp {
@ -1401,6 +1442,13 @@ public final class CalendarMessageScreen: ViewController {
return
}
let _ = strongSelf.calendarSource.removeMessagesInRange(minTimestamp: minTimestampValue, maxTimestamp: maxTimestampValue, type: type, completion: {
Queue.mainQueue().async {
guard let strongSelf = self else {
return
}
strongSelf.controller?.dismiss(completion: nil)
}
})
}
@ -1423,18 +1471,6 @@ public final class CalendarMessageScreen: ViewController {
actionSheet?.dismissAnimated()
beginClear(.forEveryone)
/*guard let strongSelf = self else {
return
}
strongSelf.controller?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: confirmationText, actions: [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}),
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: {
beginClear(.forEveryone)
})
], parseMarkdown: true), in: .window(.root))*/
}))
}
if let canClearForMyself = info.canClearForMyself {
@ -1588,7 +1624,7 @@ public final class CalendarMessageScreen: ViewController {
for day in 0 ..< month.numberOfDays {
let dayTimestamp = firstDayTimestamp + 24 * 60 * 60 * Int32(day)
if dayTimestamp == timestamp {
if month.mediaByDay[day] != nil {
if month.mediaByDay[day] != nil || strongSelf.canNavigateToEmptyDays {
var offset = 0
for key in calendarState.messagesByDay.keys.sorted(by: { $0 > $1 }) {
if key == dayTimestamp {
@ -1597,7 +1633,7 @@ public final class CalendarMessageScreen: ViewController {
offset += item.count
}
}
strongSelf.navigateToOffset(offset)
strongSelf.navigateToOffset(offset, dayTimestamp)
}
break outer
@ -1709,16 +1745,29 @@ public final class CalendarMessageScreen: ViewController {
private let peerId: PeerId
private let calendarSource: SparseMessageCalendar
private let initialTimestamp: Int32
private let navigateToDay: (CalendarMessageScreen, Int) -> Void
private let previewDay: (MessageIndex, ASDisplayNode, CGRect, ContextGesture) -> Void
private let enableMessageRangeDeletion: Bool
private let canNavigateToEmptyDays: Bool
private let navigateToDay: (CalendarMessageScreen, Int, Int32) -> Void
private let previewDay: (Int32, MessageIndex?, ASDisplayNode, CGRect, ContextGesture) -> Void
private var presentationData: PresentationData
public init(context: AccountContext, peerId: PeerId, calendarSource: SparseMessageCalendar, initialTimestamp: Int32, navigateToDay: @escaping (CalendarMessageScreen, Int) -> Void, previewDay: @escaping (MessageIndex, ASDisplayNode, CGRect, ContextGesture) -> Void) {
public init(
context: AccountContext,
peerId: PeerId,
calendarSource: SparseMessageCalendar,
initialTimestamp: Int32,
enableMessageRangeDeletion: Bool,
canNavigateToEmptyDays: Bool,
navigateToDay: @escaping (CalendarMessageScreen, Int, Int32) -> Void,
previewDay: @escaping (Int32, MessageIndex?, ASDisplayNode, CGRect, ContextGesture) -> Void
) {
self.context = context
self.peerId = peerId
self.calendarSource = calendarSource
self.initialTimestamp = initialTimestamp
self.enableMessageRangeDeletion = enableMessageRangeDeletion
self.canNavigateToEmptyDays = canNavigateToEmptyDays
self.navigateToDay = navigateToDay
self.previewDay = previewDay
@ -1731,9 +1780,11 @@ public final class CalendarMessageScreen: ViewController {
self.navigationItem.setLeftBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(dismissPressed)), animated: false)
self.navigationItem.setTitle(self.presentationData.strings.MessageCalendar_Title, animated: false)
/*if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat {
self.navigationItem.setRightBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Select, style: .plain, target: self, action: #selector(self.toggleSelectPressed)), animated: false)
}*/
if self.enableMessageRangeDeletion {
if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat {
self.navigationItem.setRightBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Select, style: .plain, target: self, action: #selector(self.toggleSelectPressed)), animated: false)
}
}
}
required public init(coder aDecoder: NSCoder) {
@ -1745,6 +1796,10 @@ public final class CalendarMessageScreen: ViewController {
}
@objc fileprivate func toggleSelectPressed() {
if !self.enableMessageRangeDeletion {
return
}
self.node.toggleSelectionMode()
if self.node.selectionState != nil {
@ -1754,13 +1809,35 @@ public final class CalendarMessageScreen: ViewController {
}
}
public func selectDay(timestamp: Int32) {
self.node.selectDay(timestamp: timestamp)
if self.node.selectionState != nil {
self.navigationItem.setRightBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.toggleSelectPressed)), animated: true)
}
}
public func openClearHistory(timestamp: Int32) {
self.node.openClearHistory(timestamp: timestamp)
}
override public func loadDisplayNode() {
self.displayNode = Node(controller: self, context: self.context, peerId: self.peerId, calendarSource: self.calendarSource, initialTimestamp: self.initialTimestamp, navigateToOffset: { [weak self] index in
guard let strongSelf = self else {
return
}
strongSelf.navigateToDay(strongSelf, index)
}, previewDay: self.previewDay)
self.displayNode = Node(
controller: self,
context: self.context,
peerId: self.peerId,
calendarSource: self.calendarSource,
initialTimestamp: self.initialTimestamp,
enableMessageRangeDeletion: self.enableMessageRangeDeletion,
canNavigateToEmptyDays: self.canNavigateToEmptyDays,
navigateToOffset: { [weak self] index, timestamp in
guard let strongSelf = self else {
return
}
strongSelf.navigateToDay(strongSelf, index, timestamp)
},
previewDay: self.previewDay
)
self.displayNodeDidLoad()
}

View File

@ -691,7 +691,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
if let layout = strongSelf.validLayout, case .regular = layout.metrics.widthClass {
scrollToEndIfExists = true
}
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), subject: .message(id: messageId, highlight: true, timecode: nil), purposefulAction: {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil), purposefulAction: {
if deactivateOnAction {
self?.deactivateSearch(animated: false)
}
@ -861,7 +861,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} else {
var subject: ChatControllerSubject?
if case let .search(messageId) = source, let id = messageId {
subject = .message(id: id, highlight: false, timecode: nil)
subject = .message(id: .id(id), highlight: false, timecode: nil)
}
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.id), subject: subject, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)

View File

@ -56,7 +56,7 @@ public final class HashtagSearchController: TelegramBaseController {
if let strongSelf = self {
strongSelf.openMessageFromSearchDisposable.set((storedMessageFromSearchPeer(account: strongSelf.context.account, peer: peer._asPeer()) |> 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), subject: message.id.peerId == actualPeerId ? .message(id: message.id, highlight: true, timecode: nil) : nil, keepStack: .always))
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), subject: message.id.peerId == actualPeerId ? .message(id: .id(message.id), highlight: true, timecode: nil) : nil, keepStack: .always))
}
}))
strongSelf.controllerNode.listNode.clearHighlightAnimated(true)

View File

@ -1119,7 +1119,7 @@ public final class SparseItemGrid: ASDisplayNode {
let previousProgress = self.currentProgress
self.currentProgress = progress
let fixedAnchorPoint = CGPoint(x: fromAnchorFrame.minX + 1.0, y: fromAnchorFrame.minY + 1.0)
let fixedAnchorPoint = CGPoint(x: toAnchorFrame.midX, y: toAnchorFrame.midY)
if let fromItem = self.fromViewport.anchorItem(at: fixedAnchorPoint), let fromFrame = self.fromViewport.frameForItem(at: fromItem.index) {
fromAnchorFrame.origin.y = fromFrame.midY

View File

@ -518,7 +518,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
items.append(.action(ContextMenuActionItem(text: presentationData.strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { [weak controller] c, _ in
c.dismiss(completion: {
if let navigationController = controller?.navigationController as? NavigationController {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: .message(id: messageId, highlight: true, timecode: nil)))
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil)))
}
})
})))

View File

@ -263,7 +263,7 @@ public func messageStatsController(context: AccountContext, messageId: MessageId
}
navigateToMessageImpl = { [weak controller] messageId in
if let navigationController = controller?.navigationController as? NavigationController {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(messageId.peerId), subject: .message(id: messageId, highlight: true, timecode: nil), keepStack: .always, useExisting: false, purposefulAction: {}, peekData: nil))
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(messageId.peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil), keepStack: .always, useExisting: false, purposefulAction: {}, peekData: nil))
}
}
return controller

View File

@ -758,7 +758,7 @@ final class AuthorizedApplicationContext {
}
let navigateToMessage = {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(messageId.peerId), subject: .message(id: messageId, highlight: true, timecode: nil)))
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(messageId.peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil)))
}
if chatIsVisible {
@ -837,7 +837,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), subject: messageId.flatMap { .message(id: $0, highlight: true, timecode: nil) }, activateInput: activateInput))
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: self.rootController, context: self.context, chatLocation: .peer(peerId), subject: messageId.flatMap { .message(id: .id($0), highlight: true, timecode: nil) }, activateInput: activateInput))
} else {
self.scheduledOpenChatWithPeerId = (peerId, messageId, activateInput)
}

View File

@ -63,6 +63,7 @@ import Speak
import UniversalMediaPlayer
import WallpaperBackgroundNode
import ChatListUI
import CalendarMessageScreen
#if DEBUG
import os.signpost
@ -2100,16 +2101,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
return .none
}, navigateToFirstDateMessage: { [weak self] timestamp in
}, navigateToFirstDateMessage: { [weak self] timestamp, alreadyThere in
guard let strongSelf = self else {
return
}
switch strongSelf.chatLocation {
case let .peer(peerId):
strongSelf.navigateToMessage(from: nil, to: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp - Int32(NSTimeZone.local.secondsFromGMT()))), scrollPosition: .bottom(0.0), rememberInStack: false, animated: true, completion: nil)
case let .replyThread(replyThreadMessage):
let peerId = replyThreadMessage.messageId.peerId
case let .peer(peerId):
if alreadyThere {
strongSelf.openCalendarSearch(timestamp: timestamp)
} else {
strongSelf.navigateToMessage(from: nil, to: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp - Int32(NSTimeZone.local.secondsFromGMT()))), scrollPosition: .bottom(0.0), rememberInStack: false, animated: true, completion: nil)
}
case let .replyThread(replyThreadMessage):
let peerId = replyThreadMessage.messageId.peerId
strongSelf.navigateToMessage(from: nil, to: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp - Int32(NSTimeZone.local.secondsFromGMT()))), scrollPosition: .bottom(0.0), rememberInStack: false, animated: true, completion: nil)
}
}, requestRedeliveryOfFailedMessages: { [weak self] id in
guard let strongSelf = self else {
@ -5006,7 +5011,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.chatDisplayNode.historyNode.scrolledToIndex = { [weak self] toIndex, initial in
if let strongSelf = self, case let .message(index) = toIndex {
if case let .message(messageId, _, _) = strongSelf.subject, initial, messageId != index.id {
if case let .message(messageSubject, _, _) = strongSelf.subject, initial, case let .id(messageId) = messageSubject, messageId != index.id {
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .info(text: strongSelf.presentationData.strings.Conversation_MessageDoesntExist), elevatedLayout: false, action: { _ in return true }), in: .current)
} else if let controllerInteraction = strongSelf.controllerInteraction {
if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(index.id) {
@ -6076,37 +6081,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
}, openCalendarSearch: { [weak self] in
if let strongSelf = self {
strongSelf.chatDisplayNode.dismissInput()
let controller = ChatDateSelectionSheet(presentationData: strongSelf.presentationData, completion: { timestamp in
guard let strongSelf = self else {
return
}
strongSelf.loadingMessage.set(.single(.generic))
let peerId: PeerId
let threadId: Int64?
switch strongSelf.chatLocation {
case let .peer(peerIdValue):
peerId = peerIdValue
threadId = nil
case let .replyThread(replyThreadMessage):
peerId = replyThreadMessage.messageId.peerId
threadId = makeMessageThreadId(replyThreadMessage.messageId)
}
strongSelf.messageIndexDisposable.set((strongSelf.context.engine.messages.searchMessageIdByTimestamp(peerId: peerId, threadId: threadId, timestamp: timestamp) |> deliverOnMainQueue).start(next: { messageId in
if let strongSelf = self {
strongSelf.loadingMessage.set(.single(nil))
if let messageId = messageId {
strongSelf.navigateToMessage(from: nil, to: .id(messageId, nil), forceInCurrentChat: true)
}
}
}))
})
strongSelf.present(controller, in: .window(.root))
}
self?.openCalendarSearch(timestamp: Int32(Date().timeIntervalSince1970))
}, toggleMembersSearch: { [weak self] value in
if let strongSelf = self {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
@ -7288,7 +7263,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
if let navigationController = strongSelf.effectiveNavigationController {
let subject: ChatControllerSubject? = sourceMessageId.flatMap { ChatControllerSubject.message(id: $0, highlight: true, timecode: nil) }
let subject: ChatControllerSubject? = sourceMessageId.flatMap { ChatControllerSubject.message(id: .id($0), highlight: true, timecode: nil) }
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(replyThreadResult), subject: subject, keepStack: .always))
}
}, activatePinnedListPreview: { [weak self] node, gesture in
@ -11308,6 +11283,127 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
})
}
private func openCalendarSearch(timestamp: Int32) {
guard case let .peer(peerId) = self.chatLocation else {
return
}
self.chatDisplayNode.dismissInput()
let initialTimestamp = timestamp
var dismissCalendarScreen: (() -> Void)?
var selectDay: ((Int32) -> Void)?
var openClearHistory: ((Int32) -> Void)?
let calendarScreen = CalendarMessageScreen(
context: self.context,
peerId: peerId,
calendarSource: self.context.engine.messages.sparseMessageCalendar(peerId: peerId, tag: .photoOrVideo),
initialTimestamp: initialTimestamp,
enableMessageRangeDeletion: true,
canNavigateToEmptyDays: true,
navigateToDay: { [weak self] c, index, timestamp in
guard let strongSelf = self else {
c.dismiss()
return
}
c.dismiss()
strongSelf.loadingMessage.set(.single(.generic))
let peerId: PeerId
let threadId: Int64?
switch strongSelf.chatLocation {
case let .peer(peerIdValue):
peerId = peerIdValue
threadId = nil
case let .replyThread(replyThreadMessage):
peerId = replyThreadMessage.messageId.peerId
threadId = makeMessageThreadId(replyThreadMessage.messageId)
}
strongSelf.messageIndexDisposable.set((strongSelf.context.engine.messages.searchMessageIdByTimestamp(peerId: peerId, threadId: threadId, timestamp: timestamp) |> deliverOnMainQueue).start(next: { messageId in
if let strongSelf = self {
strongSelf.loadingMessage.set(.single(nil))
if let messageId = messageId {
strongSelf.navigateToMessage(from: nil, to: .id(messageId, nil), forceInCurrentChat: true)
}
}
}))
},
previewDay: { [weak self] timestamp, _, sourceNode, sourceRect, gesture in
guard let strongSelf = self else {
return
}
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Chat_JumpToDate, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor)
}, action: { _, f in
f(.dismissWithoutContent)
dismissCalendarScreen?()
strongSelf.loadingMessage.set(.single(.generic))
let peerId: PeerId
let threadId: Int64?
switch strongSelf.chatLocation {
case let .peer(peerIdValue):
peerId = peerIdValue
threadId = nil
case let .replyThread(replyThreadMessage):
peerId = replyThreadMessage.messageId.peerId
threadId = makeMessageThreadId(replyThreadMessage.messageId)
}
strongSelf.messageIndexDisposable.set((strongSelf.context.engine.messages.searchMessageIdByTimestamp(peerId: peerId, threadId: threadId, timestamp: timestamp) |> deliverOnMainQueue).start(next: { messageId in
if let strongSelf = self {
strongSelf.loadingMessage.set(.single(nil))
if let messageId = messageId {
strongSelf.navigateToMessage(from: nil, to: .id(messageId, nil), forceInCurrentChat: true)
}
}
}))
})))
if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.DialogList_ClearHistoryConfirmation, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
}, action: { _, f in
f(.dismissWithoutContent)
openClearHistory?(timestamp)
})))
items.append(.separator)
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Common_Select, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor)
}, action: { _, f in
f(.dismissWithoutContent)
selectDay?(timestamp)
})))
}
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: .message(id: .timestamp(timestamp), highlight: false, timecode: nil), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(items: items)), gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
)
self.push(calendarScreen)
dismissCalendarScreen = { [weak calendarScreen] in
calendarScreen?.dismiss(completion: nil)
}
selectDay = { [weak calendarScreen] timestamp in
calendarScreen?.selectDay(timestamp: timestamp)
}
openClearHistory = { [weak calendarScreen] timestamp in
calendarScreen?.openClearHistory(timestamp: timestamp)
}
}
private func openMessageReplies(messageId: MessageId, displayProgressInMessage: MessageId?, isChannelPost: Bool, atMessage atMessageId: MessageId?, displayModalProgress: Bool) {
guard let navigationController = self.navigationController as? NavigationController else {
@ -11382,9 +11478,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let subject: ChatControllerSubject?
if let atMessageId = atMessageId {
subject = .message(id: atMessageId, highlight: true, timecode: nil)
subject = .message(id: .id(atMessageId), highlight: true, timecode: nil)
} else if let index = result.scrollToLowerBoundMessage {
subject = .message(id: index.id, highlight: false, timecode: nil)
subject = .message(id: .id(index.id), highlight: false, timecode: nil)
} else {
subject = nil
}
@ -11447,11 +11543,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if isPinnedMessages, let messageId = messageLocation.messageId {
if let navigationController = self.effectiveNavigationController {
self.dismiss()
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageId.peerId), subject: .message(id: messageId, highlight: true, timecode: nil), keepStack: .always))
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageId.peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil), keepStack: .always))
}
} else if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, (messageId.peerId != peerId && !forceInCurrentChat) || (isScheduledMessages && messageId.id != 0 && !Namespaces.Message.allScheduled.contains(messageId.namespace)) {
if let navigationController = self.effectiveNavigationController {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageId.peerId), subject: .message(id: messageId, highlight: true, timecode: nil), keepStack: .always))
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageId.peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil), keepStack: .always))
}
} else if forceInCurrentChat {
if let _ = fromId, let fromIndex = fromIndex, rememberInStack {
@ -11613,7 +11709,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
completion?()
} else {
if let navigationController = strongSelf.effectiveNavigationController {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(messageLocation.peerId), subject: messageLocation.messageId.flatMap { .message(id: $0, highlight: true, timecode: nil) }))
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(messageLocation.peerId), subject: messageLocation.messageId.flatMap { .message(id: .id($0), highlight: true, timecode: nil) }))
}
completion?()
}
@ -11625,7 +11721,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}))
} else {
if let navigationController = self.effectiveNavigationController {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageLocation.peerId), subject: messageLocation.messageId.flatMap { .message(id: $0, highlight: true, timecode: nil) }))
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageLocation.peerId), subject: messageLocation.messageId.flatMap { .message(id: .id($0), highlight: true, timecode: nil) }))
}
completion?()
}
@ -12399,8 +12495,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
switch navigation {
case let .chat(_, subject, peekData):
if case .peer(peerId) = strongSelf.chatLocation {
if let subject = subject, case let .message(messageId, _, timecode) = subject {
strongSelf.navigateToMessage(from: sourceMessageId, to: .id(messageId, timecode))
if let subject = subject, case let .message(messageSubject, _, timecode) = subject {
if case let .id(messageId) = messageSubject {
strongSelf.navigateToMessage(from: sourceMessageId, to: .id(messageId, timecode))
}
}
} else if let navigationController = strongSelf.effectiveNavigationController {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, keepStack: .always, peekData: peekData))
@ -13604,22 +13702,25 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
let controller: ViewController
weak var sourceNode: ASDisplayNode?
let sourceRect: CGRect?
let navigationController: NavigationController? = nil
let passthroughTouches: Bool
init(controller: ViewController, sourceNode: ASDisplayNode?, passthroughTouches: Bool) {
init(controller: ViewController, sourceNode: ASDisplayNode?, sourceRect: CGRect? = nil, passthroughTouches: Bool) {
self.controller = controller
self.sourceNode = sourceNode
self.sourceRect = sourceRect
self.passthroughTouches = passthroughTouches
}
func transitionInfo() -> ContextControllerTakeControllerInfo? {
let sourceNode = self.sourceNode
let sourceRect = self.sourceRect
return ContextControllerTakeControllerInfo(contentAreaInScreenSpace: CGRect(origin: CGPoint(), size: CGSize(width: 10.0, height: 10.0)), sourceNode: { [weak sourceNode] in
if let sourceNode = sourceNode {
return (sourceNode, sourceNode.bounds)
return (sourceNode, sourceRect ?? sourceNode.bounds)
} else {
return nil
}

View File

@ -87,7 +87,7 @@ public final class ChatControllerInteraction {
let openSearch: () -> Void
let setupReply: (MessageId) -> Void
let canSetupReply: (Message) -> ChatControllerInteractionSwipeAction
let navigateToFirstDateMessage: (Int32) -> Void
let navigateToFirstDateMessage: (Int32, Bool) -> Void
let requestRedeliveryOfFailedMessages: (MessageId) -> Void
let addContact: (String) -> Void
let rateCall: (Message, CallId, Bool) -> Void
@ -181,7 +181,7 @@ public final class ChatControllerInteraction {
openSearch: @escaping () -> Void,
setupReply: @escaping (MessageId) -> Void,
canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction,
navigateToFirstDateMessage: @escaping(Int32) ->Void,
navigateToFirstDateMessage: @escaping(Int32, Bool) ->Void,
requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void,
addContact: @escaping (String) -> Void,
rateCall: @escaping (Message, CallId, Bool) -> Void,
@ -315,7 +315,7 @@ public final class ChatControllerInteraction {
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in
}, canSetupReply: { _ in
return .none
}, navigateToFirstDateMessage: { _ in
}, navigateToFirstDateMessage: { _, _ in
}, requestRedeliveryOfFailedMessages: { _ in
}, addContact: { _ in
}, rateCall: { _, _, _ in

View File

@ -951,8 +951,15 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Navigation(index: .message(anchorIndex), anchorIndex: .message(anchorIndex), count: historyMessageCount, highlight: false), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0)
}
} else {
if let subject = subject, case let .message(messageId, highlight, _) = subject {
strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(location: .id(messageId), count: 60, highlight: highlight), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0)
if let subject = subject, case let .message(messageSubject, highlight, _) = subject {
let initialSearchLocation: ChatHistoryInitialSearchLocation
switch messageSubject {
case let .id(id):
initialSearchLocation = .id(id)
case let .timestamp(timestamp):
initialSearchLocation = .index(MessageIndex(id: MessageId(peerId: strongSelf.chatLocation.peerId, namespace: Namespaces.Message.Cloud, id: 1), timestamp: timestamp))
}
strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(location: initialSearchLocation, count: 60, highlight: highlight), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0)
} else if let subject = subject, case let .pinnedMessages(maybeMessageId) = subject, let messageId = maybeMessageId {
strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(location: .id(messageId), count: 60, highlight: true), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0)
} else if var chatHistoryLocation = strongSelf.chatHistoryLocationValue {
@ -1207,8 +1214,15 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
}
})
if let subject = subject, case let .message(messageId, highlight, _) = subject {
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(location: .id(messageId), count: 60, highlight: highlight), id: 0)
if let subject = subject, case let .message(messageSubject, highlight, _) = subject {
let initialSearchLocation: ChatHistoryInitialSearchLocation
switch messageSubject {
case let .id(id):
initialSearchLocation = .id(id)
case let .timestamp(timestamp):
initialSearchLocation = .index(MessageIndex(id: MessageId(peerId: self.chatLocation.peerId, namespace: Namespaces.Message.Cloud, id: 1), timestamp: timestamp))
}
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(location: initialSearchLocation, count: 60, highlight: highlight), id: 0)
} else if let subject = subject, case let .pinnedMessages(maybeMessageId) = subject, let messageId = maybeMessageId {
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(location: .id(messageId), count: 60, highlight: true), id: 0)
} else {

View File

@ -1520,7 +1520,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, subject: .message(id: attribute.messageId, highlight: true, timecode: nil), peekData: nil)
navigate = .chat(textInputState: nil, subject: .message(id: .id(attribute.messageId), highlight: true, timecode: nil), peekData: nil)
}
}

View File

@ -2849,7 +2849,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(id: attribute.messageId, highlight: true, timecode: nil), peekData: nil)
navigate = .chat(textInputState: nil, subject: .message(id: .id(attribute.messageId), highlight: true, timecode: nil), peekData: nil)
}
}

View File

@ -26,9 +26,9 @@ final class ChatMessageDateHeader: ListViewItemHeader {
let id: ListViewItemNode.HeaderId
let presentationData: ChatPresentationData
let context: AccountContext
let action: ((Int32) -> Void)?
let action: ((Int32, Bool) -> Void)?
init(timestamp: Int32, scheduled: Bool, presentationData: ChatPresentationData, context: AccountContext, action: ((Int32) -> Void)? = nil) {
init(timestamp: Int32, scheduled: Bool, presentationData: ChatPresentationData, context: AccountContext, action: ((Int32, Bool) -> Void)? = nil) {
self.timestamp = timestamp
self.scheduled = scheduled
self.presentationData = presentationData
@ -117,9 +117,9 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
private var flashingOnScrolling = false
private var stickDistanceFactor: CGFloat = 0.0
private var action: ((Int32) -> Void)? = nil
private var action: ((Int32, Bool) -> Void)? = nil
init(localTimestamp: Int32, scheduled: Bool, presentationData: ChatPresentationData, context: AccountContext, action: ((Int32) -> Void)? = nil) {
init(localTimestamp: Int32, scheduled: Bool, presentationData: ChatPresentationData, context: AccountContext, action: ((Int32, Bool) -> Void)? = nil) {
self.presentationData = presentationData
self.context = context
@ -309,7 +309,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
@objc func tapGesture(_ recognizer: ListViewTapGestureRecognizer) {
if case .ended = recognizer.state {
self.action?(self.localTimestamp)
self.action?(self.localTimestamp, self.stickDistanceFactor < 0.5)
}
}
}

View File

@ -827,7 +827,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(id: attribute.messageId, highlight: true, timecode: nil), peekData: nil)
navigate = .chat(textInputState: nil, subject: .message(id: .id(attribute.messageId), highlight: true, timecode: nil), peekData: nil)
}
}

View File

@ -328,14 +328,14 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
isScheduledMessages = true
}
self.dateHeader = ChatMessageDateHeader(timestamp: content.index.timestamp, scheduled: isScheduledMessages, presentationData: presentationData, context: context, action: { timestamp in
self.dateHeader = ChatMessageDateHeader(timestamp: content.index.timestamp, scheduled: isScheduledMessages, presentationData: presentationData, context: context, action: { timestamp, alreadyThere in
var calendar = NSCalendar.current
calendar.timeZone = TimeZone(abbreviation: "UTC")!
let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
let components = calendar.dateComponents([.year, .month, .day], from: date)
if let date = calendar.date(from: components) {
controllerInteraction.navigateToFirstDateMessage(Int32(date.timeIntervalSince1970))
controllerInteraction.navigateToFirstDateMessage(Int32(date.timeIntervalSince1970), alreadyThere)
}
})

View File

@ -975,7 +975,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, subject: .message(id: attribute.messageId, highlight: true, timecode: nil), peekData: nil)
navigate = .chat(textInputState: nil, subject: .message(id: .id(attribute.messageId), highlight: true, timecode: nil), peekData: nil)
}
}

View File

@ -70,7 +70,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
} else {
var subject: ChatControllerSubject?
if let messageId = adAttribute.messageId {
subject = .message(id: messageId, highlight: true, timecode: nil)
subject = .message(id: .id(messageId), highlight: true, timecode: nil)
}
navigationData = .chat(textInputState: nil, subject: subject, peekData: nil)
}

View File

@ -489,7 +489,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, setupReply: { _ in
}, canSetupReply: { _ in
return .none
}, navigateToFirstDateMessage: { _ in
}, navigateToFirstDateMessage: { _, _ in
}, requestRedeliveryOfFailedMessages: { _ in
}, addContact: { _ in
}, rateCall: { _, _, _ in
@ -884,11 +884,11 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
break
case let .channelMessage(peerId, messageId, timecode):
if let navigationController = strongSelf.getNavigationController() {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: .message(id: messageId, highlight: true, timecode: timecode)))
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: .message(id: .id(messageId), highlight: true, timecode: timecode)))
}
case let .replyThreadMessage(replyThreadMessage, messageId):
if let navigationController = strongSelf.getNavigationController() {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(replyThreadMessage), subject: .message(id: messageId, highlight: true, timecode: nil)))
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(replyThreadMessage), subject: .message(id: .id(messageId), highlight: true, timecode: nil)))
}
case let .stickerPack(name):
let packReference: StickerPackReference = .name(name)

View File

@ -232,7 +232,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
switch item.content {
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _):
if let message = messages.first {
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peerId), subject: .message(id: message.id, highlight: true, timecode: nil), botStart: nil, mode: .standard(previewing: true))
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(items: [])), gesture: gesture)
presentInGlobalOverlay(contextController)

View File

@ -118,7 +118,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in
}, canSetupReply: { _ in
return .none
}, navigateToFirstDateMessage: { _ in
}, navigateToFirstDateMessage: { _, _ in
}, requestRedeliveryOfFailedMessages: { _ in
}, addContact: { _ in
}, rateCall: { _, _, _ in

View File

@ -20,16 +20,18 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
if let updateTextInputState = params.updateTextInputState {
controller.updateTextInputState(updateTextInputState)
}
if let subject = params.subject, case let .message(messageId, _, timecode) = subject {
let navigationController = params.navigationController
let animated = params.animated
controller.navigateToMessage(messageLocation: .id(messageId, timecode), animated: isFirst, completion: { [weak navigationController, weak controller] in
if let navigationController = navigationController, let controller = controller {
let _ = navigationController.popToViewController(controller, animated: animated)
}
}, customPresentProgress: { [weak navigationController] c, a in
(navigationController?.viewControllers.last as? ViewController)?.present(c, in: .window(.root), with: a)
})
if let subject = params.subject, case let .message(messageSubject, _, timecode) = subject {
if case let .id(messageId) = messageSubject {
let navigationController = params.navigationController
let animated = params.animated
controller.navigateToMessage(messageLocation: .id(messageId, timecode), animated: isFirst, completion: { [weak navigationController, weak controller] in
if let navigationController = navigationController, let controller = controller {
let _ = navigationController.popToViewController(controller, animated: animated)
}
}, customPresentProgress: { [weak navigationController] c, a in
(navigationController?.viewControllers.last as? ViewController)?.present(c, in: .window(.root), with: a)
})
}
} else if params.scrollToEndIfExists && isFirst {
controller.scrollToEndOfHistory()
let _ = params.navigationController.popToViewController(controller, animated: params.animated)

View File

@ -103,7 +103,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
dismissInput()
navigationController?.pushViewController(controller)
case let .channelMessage(peerId, messageId, timecode):
openPeer(peerId, .chat(textInputState: nil, subject: .message(id: messageId, highlight: true, timecode: timecode), peekData: nil))
openPeer(peerId, .chat(textInputState: nil, subject: .message(id: .id(messageId), highlight: true, timecode: timecode), peekData: nil))
case let .replyThreadMessage(replyThreadMessage, messageId):
if let navigationController = navigationController {
let _ = ChatControllerImpl.openMessageReplies(context: context, navigationController: navigationController, present: { c, a in

View File

@ -110,7 +110,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}, setupReply: { _ in
}, canSetupReply: { _ in
return .none
}, navigateToFirstDateMessage: { _ in
}, navigateToFirstDateMessage: { _, _ in
}, requestRedeliveryOfFailedMessages: { _ in
}, addContact: { _ in
}, rateCall: { _, _, _ in
@ -186,7 +186,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
self.isGlobalSearch = false
}
self.historyNode = ChatHistoryListNode(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: .peer(peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, source: source, subject: .message(id: initialMessageId, highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch))
self.historyNode = ChatHistoryListNode(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: .peer(peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, source: source, subject: .message(id: .id(initialMessageId), highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch))
self.historyNode.clipsToBounds = true
super.init()
@ -528,7 +528,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
let historyNode = ChatHistoryListNode(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: .peer(self.peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: .message(id: messageId, highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch))
let historyNode = ChatHistoryListNode(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: .peer(self.peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: .message(id: .id(messageId), highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch))
historyNode.clipsToBounds = true
historyNode.preloadPages = true
historyNode.stackFromBottom = true

View File

@ -1800,7 +1800,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
c.dismiss(completion: {
if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
let currentPeerId = strongSelf.peerId
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(currentPeerId), subject: .message(id: message.id, highlight: true, timecode: nil), keepStack: .always, useExisting: false, purposefulAction: {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(currentPeerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), keepStack: .always, useExisting: false, purposefulAction: {
var viewControllers = navigationController.viewControllers
var indexesToRemove = Set<Int>()
var keptCurrentChatController = false
@ -1946,7 +1946,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
c.dismiss(completion: {
if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
let currentPeerId = strongSelf.peerId
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(currentPeerId), subject: .message(id: message.id, highlight: true, timecode: nil), keepStack: .always, useExisting: false, purposefulAction: {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(currentPeerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), keepStack: .always, useExisting: false, purposefulAction: {
var viewControllers = navigationController.viewControllers
var indexesToRemove = Set<Int>()
var keptCurrentChatController = false
@ -2198,7 +2198,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}, setupReply: { _ in
}, canSetupReply: { _ in
return .none
}, navigateToFirstDateMessage: { _ in
}, navigateToFirstDateMessage: { _, _ in
}, requestRedeliveryOfFailedMessages: { _ in
}, addContact: { _ in
}, rateCall: { _, _, _ in
@ -6249,68 +6249,77 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
var dismissCalendarScreen: (() -> Void)?
let calendarScreen = CalendarMessageScreen(context: self.context, peerId: self.peerId, calendarSource: calendarSource, initialTimestamp: initialTimestamp, navigateToDay: { [weak self] c, index in
guard let strongSelf = self else {
c.dismiss()
return
}
guard let pane = strongSelf.paneContainerNode.currentPane?.node as? PeerInfoVisualMediaPaneNode else {
c.dismiss()
return
}
pane.scrollToItem(index: index)
c.dismiss()
}, previewDay: { [weak self] index, sourceNode, sourceRect, gesture in
guard let strongSelf = self else {
return
}
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.SharedMedia_ViewInChat, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor)
}, action: { _, f in
f(.dismissWithoutContent)
dismissCalendarScreen?()
guard let strongSelf = self, let controller = strongSelf.controller, let navigationController = controller.navigationController as? NavigationController else {
let calendarScreen = CalendarMessageScreen(
context: self.context,
peerId: self.peerId,
calendarSource: calendarSource,
initialTimestamp: initialTimestamp,
enableMessageRangeDeletion: false,
canNavigateToEmptyDays: false,
navigateToDay: { [weak self] c, index, _ in
guard let strongSelf = self else {
c.dismiss()
return
}
guard let pane = strongSelf.paneContainerNode.currentPane?.node as? PeerInfoVisualMediaPaneNode else {
c.dismiss()
return
}
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
navigationController: navigationController,
chatController: nil,
context: strongSelf.context,
chatLocation: .peer(strongSelf.peerId),
subject: .message(id: index.id, highlight: false, timecode: nil),
botStart: nil,
updateTextInputState: nil,
activateInput: false,
keepStack: .never,
useExisting: true,
purposefulAction: nil,
scrollToEndIfExists: false,
activateMessageSearch: nil,
peekData: nil,
peerNearbyData: nil,
reportReason: nil,
animated: true,
options: [],
parentGroupId: nil,
chatListFilter: nil,
changeColors: false,
completion: { _ in
}
))
})))
pane.scrollToItem(index: index)
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), subject: .message(id: index.id, highlight: false, timecode: nil), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(items: items)), gesture: gesture)
strongSelf.controller?.presentInGlobalOverlay(contextController)
})
c.dismiss()
},
previewDay: { [weak self] _, index, sourceNode, sourceRect, gesture in
guard let strongSelf = self, let index = index else {
return
}
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.SharedMedia_ViewInChat, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor)
}, action: { _, f in
f(.dismissWithoutContent)
dismissCalendarScreen?()
guard let strongSelf = self, let controller = strongSelf.controller, let navigationController = controller.navigationController as? NavigationController else {
return
}
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
navigationController: navigationController,
chatController: nil,
context: strongSelf.context,
chatLocation: .peer(strongSelf.peerId),
subject: .message(id: .id(index.id), highlight: false, timecode: nil),
botStart: nil,
updateTextInputState: nil,
activateInput: false,
keepStack: .never,
useExisting: true,
purposefulAction: nil,
scrollToEndIfExists: false,
activateMessageSearch: nil,
peekData: nil,
peerNearbyData: nil,
reportReason: nil,
animated: true,
options: [],
parentGroupId: nil,
chatListFilter: nil,
changeColors: false,
completion: { _ in
}
))
})))
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), subject: .message(id: .id(index.id), highlight: false, timecode: nil), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(items: items)), gesture: gesture)
strongSelf.controller?.presentInGlobalOverlay(contextController)
}
)
self.controller?.push(calendarScreen)
dismissCalendarScreen = { [weak calendarScreen] in

View File

@ -1255,7 +1255,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in
}, canSetupReply: { _ in
return .none
}, navigateToFirstDateMessage: { _ in
}, navigateToFirstDateMessage: { _, _ in
}, requestRedeliveryOfFailedMessages: { _ in
}, addContact: { _ in
}, rateCall: { _, _, _ in

View File

@ -59,7 +59,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
openResolvedPeerImpl(peerId, navigation)
case let .channelMessage(peerId, messageId, timecode):
if let navigationController = controller.navigationController as? NavigationController {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: .message(id: messageId, highlight: true, timecode: timecode)))
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: .message(id: .id(messageId), highlight: true, timecode: timecode)))
}
case let .replyThreadMessage(replyThreadMessage, messageId):
if let navigationController = controller.navigationController as? NavigationController {

View File

@ -425,7 +425,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
return .replyThreadMessage(replyThreadMessage: result, messageId: messageId)
}
} else {
return .single(.peer(foundPeer.id, .chat(textInputState: nil, subject: .message(id: messageId, highlight: true, timecode: timecode), peekData: nil)))
return .single(.peer(foundPeer.id, .chat(textInputState: nil, subject: .message(id: .id(messageId), highlight: true, timecode: timecode), peekData: nil)))
}
} else {
return .single(.inaccessiblePeer)