diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index 0fffa6a76e..5e652529b5 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -758,7 +758,13 @@ public enum ChatControllerSubject: Equatable { } public enum ChatControllerPresentationMode: Equatable { - case standard(previewing: Bool) + public enum StandardPresentation { + case `default` + case previewing + case embedded + } + + case standard(StandardPresentation) case overlay(NavigationController?) case inline(NavigationController?) } diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 0e75a73214..3e107d4c80 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -737,7 +737,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { self.makeEntityInputView = makeEntityInputView - self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: chatLocation ?? .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(.default), chatLocation: chatLocation ?? .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil) self.containerNode = ASDisplayNode() self.containerNode.clipsToBounds = true diff --git a/submodules/AvatarNode/Sources/AvatarNode.swift b/submodules/AvatarNode/Sources/AvatarNode.swift index 29a0223254..2ae27d6417 100644 --- a/submodules/AvatarNode/Sources/AvatarNode.swift +++ b/submodules/AvatarNode/Sources/AvatarNode.swift @@ -92,6 +92,8 @@ private func calculateColors(context: AccountContext?, explicitColorIndex: Int?, colors = AvatarNode.repostColors } else if case .repliesIcon = icon { colors = AvatarNode.savedMessagesColors + } else if case .anonymousSavedMessagesIcon = icon { + colors = AvatarNode.savedMessagesColors } else if case .editAvatarIcon = icon, let theme { colors = [theme.list.itemAccentColor.withAlphaComponent(0.1), theme.list.itemAccentColor.withAlphaComponent(0.1)] } else if case let .archivedChatsIcon(hiddenByDefault) = icon, let theme = theme { @@ -172,6 +174,7 @@ private enum AvatarNodeIcon: Equatable { case none case savedMessagesIcon case repliesIcon + case anonymousSavedMessagesIcon case archivedChatsIcon(hiddenByDefault: Bool) case editAvatarIcon case deletedIcon @@ -184,6 +187,7 @@ public enum AvatarNodeImageOverride: Equatable { case image(TelegramMediaImageRepresentation) case savedMessagesIcon case repliesIcon + case anonymousSavedMessagesIcon case archivedChatsIcon(hiddenByDefault: Bool) case editAvatarIcon(forceNone: Bool) case deletedIcon @@ -470,32 +474,35 @@ public final class AvatarNode: ASDisplayNode { var icon = AvatarNodeIcon.none if let overrideImage = overrideImage { switch overrideImage { - case .none: - representation = nil - case let .image(image): - representation = image - synchronousLoad = false - case .savedMessagesIcon: - representation = nil - icon = .savedMessagesIcon - case .repostIcon: - representation = nil - icon = .repostIcon - case .repliesIcon: - representation = nil - icon = .repliesIcon - case let .archivedChatsIcon(hiddenByDefault): - representation = nil - icon = .archivedChatsIcon(hiddenByDefault: hiddenByDefault) - case let .editAvatarIcon(forceNone): - representation = forceNone ? nil : peer?.smallProfileImage - icon = .editAvatarIcon - case .deletedIcon: - representation = nil - icon = .deletedIcon - case .phoneIcon: - representation = nil - icon = .phoneIcon + case .none: + representation = nil + case let .image(image): + representation = image + synchronousLoad = false + case .savedMessagesIcon: + representation = nil + icon = .savedMessagesIcon + case .repostIcon: + representation = nil + icon = .repostIcon + case .repliesIcon: + representation = nil + icon = .repliesIcon + case .anonymousSavedMessagesIcon: + representation = nil + icon = .anonymousSavedMessagesIcon + case let .archivedChatsIcon(hiddenByDefault): + representation = nil + icon = .archivedChatsIcon(hiddenByDefault: hiddenByDefault) + case let .editAvatarIcon(forceNone): + representation = forceNone ? nil : peer?.smallProfileImage + icon = .editAvatarIcon + case .deletedIcon: + representation = nil + icon = .deletedIcon + case .phoneIcon: + representation = nil + icon = .phoneIcon } } else if peer?.restrictionText(platform: "ios", contentSettings: contentSettings) == nil { representation = peer?.smallProfileImage @@ -637,32 +644,35 @@ public final class AvatarNode: ASDisplayNode { var icon = AvatarNodeIcon.none if let overrideImage = overrideImage { switch overrideImage { - case .none: - representation = nil - case let .image(image): - representation = image - synchronousLoad = false - case .savedMessagesIcon: - representation = nil - icon = .savedMessagesIcon - case .repostIcon: - representation = nil - icon = .repostIcon - case .repliesIcon: - representation = nil - icon = .repliesIcon - case let .archivedChatsIcon(hiddenByDefault): - representation = nil - icon = .archivedChatsIcon(hiddenByDefault: hiddenByDefault) - case let .editAvatarIcon(forceNone): - representation = forceNone ? nil : peer?.smallProfileImage - icon = .editAvatarIcon - case .deletedIcon: - representation = nil - icon = .deletedIcon - case .phoneIcon: - representation = nil - icon = .phoneIcon + case .none: + representation = nil + case let .image(image): + representation = image + synchronousLoad = false + case .savedMessagesIcon: + representation = nil + icon = .savedMessagesIcon + case .repostIcon: + representation = nil + icon = .repostIcon + case .repliesIcon: + representation = nil + icon = .repliesIcon + case .anonymousSavedMessagesIcon: + representation = nil + icon = .repliesIcon + case let .archivedChatsIcon(hiddenByDefault): + representation = nil + icon = .archivedChatsIcon(hiddenByDefault: hiddenByDefault) + case let .editAvatarIcon(forceNone): + representation = forceNone ? nil : peer?.smallProfileImage + icon = .editAvatarIcon + case .deletedIcon: + representation = nil + icon = .deletedIcon + case .phoneIcon: + representation = nil + icon = .phoneIcon } } else if peer?.restrictionText(platform: "ios", contentSettings: genericContext.currentContentSettings.with { $0 }) == nil { representation = peer?.smallProfileImage @@ -878,6 +888,15 @@ public final class AvatarNode: ASDisplayNode { context.scaleBy(x: factor, y: -factor) context.translateBy(x: -bounds.size.width / 2.0, y: -bounds.size.height / 2.0) + if let repliesIcon = repliesIcon { + context.draw(repliesIcon.cgImage!, in: CGRect(origin: CGPoint(x: floor((bounds.size.width - repliesIcon.size.width) / 2.0), y: floor((bounds.size.height - repliesIcon.size.height) / 2.0)), size: repliesIcon.size)) + } + } else if case .anonymousSavedMessagesIcon = parameters.icon { + let factor = bounds.size.width / 60.0 + context.translateBy(x: bounds.size.width / 2.0, y: bounds.size.height / 2.0) + context.scaleBy(x: factor, y: -factor) + context.translateBy(x: -bounds.size.width / 2.0, y: -bounds.size.height / 2.0) + if let repliesIcon = repliesIcon { context.draw(repliesIcon.cgImage!, in: CGRect(origin: CGPoint(x: floor((bounds.size.width - repliesIcon.size.width) / 2.0), y: floor((bounds.size.height - repliesIcon.size.height) / 2.0)), size: repliesIcon.size)) } diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 240e16e4fc..c90d247473 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1346,7 +1346,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let source: ContextContentSource let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .replyThread(message: ChatReplyThreadMessage( peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false - )), subject: nil, botStart: nil, mode: .standard(previewing: true)) + )), subject: nil, botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) @@ -1363,7 +1363,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let location = location { source = .location(ChatListContextLocationContentSource(controller: strongSelf, location: location)) } else { - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peer.peerId), subject: nil, botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) } @@ -1382,7 +1382,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let source: ContextContentSource let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .replyThread(message: ChatReplyThreadMessage( peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false - )), subject: nil, botStart: nil, mode: .standard(previewing: true)) + )), subject: nil, botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) @@ -1432,7 +1432,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if case let .search(messageId) = source, let id = messageId { subject = .message(id: .id(id), highlight: nil, timecode: nil) } - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peer.id), subject: subject, botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peer.id), subject: subject, botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) contextContentSource = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) } @@ -3399,7 +3399,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } - let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false)) + let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(.default)) if let sourceController = sourceController as? ChatListControllerImpl, case .forum(peerId) = sourceController.location { navigationController.replaceController(sourceController, with: chatController, animated: false) @@ -6541,7 +6541,7 @@ private final class ChatListLocationContext { if let channel = peerView.peers[peerView.peerId] as? TelegramChannel, !channel.flags.contains(.isForum) { if let parentController = self.parentController, let navigationController = parentController.navigationController as? NavigationController { - let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false)) + let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(.default)) navigationController.replaceController(parentController, with: chatController, animated: true) } } else { diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index a92fee78ca..c1ea6a899a 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -1137,7 +1137,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { return } - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(.default)) (controller.navigationController as? NavigationController)?.replaceController(controller, with: chatController, animated: false) } diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift index fcf487666f..86d48faeb5 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift @@ -1559,7 +1559,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset initi }) }, peerContextAction: { peer, node, gesture, location in - let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) + let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) let presentationData = context.sharedContext.currentPresentationData.with { $0 } diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 3bc5fbf73c..86107ee921 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -1507,7 +1507,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo proceed(chatController) }) } else { - proceed(strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) + proceed(strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(.default))) } strongSelf.updateState { state in diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 19e8bf09e5..06ed143139 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -1228,6 +1228,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { enableRecentlySearched = true } } + if case .savedMessagesChats = location { + enableRecentlySearched = false + } if enableRecentlySearched { fixedRecentlySearchedPeers = context.engine.peers.recentlySearchedPeers() @@ -1406,7 +1409,26 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) |> take(1) let foundLocalPeers: Signal<(peers: [EngineRenderedPeer], unread: [EnginePeer.Id: (Int32, Bool)], recentlySearchedPeerIds: Set), NoError> - if let query = query, (key == .chats || key == .topics) { + + if case .savedMessagesChats = location { + if let query { + foundLocalPeers = context.engine.messages.searchLocalSavedMessagesPeers(query: query.lowercased(), indexNameMapping: [ + context.account.peerId: [ + PeerIndexNameRepresentation.title(title: "saved messages", addressNames: []), + PeerIndexNameRepresentation.title(title: presentationData.strings.DialogList_SavedMessages.lowercased(), addressNames: []) + ], + PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2666000)): [ + //TODO:localize + PeerIndexNameRepresentation.title(title: "author hidden", addressNames: []) + ] + ]) + |> map { peers -> (peers: [EngineRenderedPeer], unread: [EnginePeer.Id: (Int32, Bool)], recentlySearchedPeerIds: Set) in + return (peers.map(EngineRenderedPeer.init(peer:)), [:], Set()) + } + } else { + foundLocalPeers = .single(([], [:], Set())) + } + } else if let query = query, (key == .chats || key == .topics) { let fixedOrRemovedRecentlySearchedPeers = context.engine.peers.recentlySearchedPeers() |> map { peers -> [RecentlySearchedPeer] in let allIds = peers.map(\.peer.peerId) @@ -1524,7 +1546,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError> let currentRemotePeersValue: ([FoundPeer], [FoundPeer]) = currentRemotePeers.with { $0 } ?? ([], []) - if let query = query, case .chats = key { + if case .savedMessagesChats = location { + foundRemotePeers = .single(([], [], false)) + } else if let query = query, case .chats = key { foundRemotePeers = ( .single((currentRemotePeersValue.0, currentRemotePeersValue.1, true)) |> then( @@ -1579,7 +1603,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } let foundRemoteMessages: Signal<([FoundRemoteMessages], Bool), NoError> - if peersFilter.contains(.doNotSearchMessages) { + if case .savedMessagesChats = location { + foundRemoteMessages = .single(([FoundRemoteMessages(messages: [], readCounters: [:], threadsData: [:], totalCount: 0)], false)) + } else if peersFilter.contains(.doNotSearchMessages) { foundRemoteMessages = .single(([FoundRemoteMessages(messages: [], readCounters: [:], threadsData: [:], totalCount: 0)], false)) } else { if !finalQuery.isEmpty { @@ -1676,18 +1702,23 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { ) } - let resolvedMessage = .single(nil) - |> then(context.sharedContext.resolveUrl(context: context, peerId: nil, url: finalQuery, skipUrlAuth: true) - |> mapToSignal { resolvedUrl -> Signal in - if case let .channelMessage(_, messageId, _) = resolvedUrl { - return context.engine.messages.downloadMessage(messageId: messageId) - |> map { message -> EngineMessage? in - return message.flatMap(EngineMessage.init) + let resolvedMessage: Signal + if case .savedMessagesChats = location { + resolvedMessage = .single(nil) + } else { + resolvedMessage = .single(nil) + |> then(context.sharedContext.resolveUrl(context: context, peerId: nil, url: finalQuery, skipUrlAuth: true) + |> mapToSignal { resolvedUrl -> Signal in + if case let .channelMessage(_, messageId, _) = resolvedUrl { + return context.engine.messages.downloadMessage(messageId: messageId) + |> map { message -> EngineMessage? in + return message.flatMap(EngineMessage.init) + } + } else { + return .single(nil) } - } else { - return .single(nil) - } - }) + }) + } let foundThreads: Signal<[EngineChatList.Item], NoError> if case let .forum(peerId) = location, (key == .topics || key == .chats) { @@ -2619,6 +2650,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { if peersFilter.contains(.excludeRecent) { recentItems = .single([]) } + if case .savedMessagesChats = location { + recentItems = .single([]) + } if case .chats = key, !peersFilter.contains(.excludeRecent) { self.updatedRecentPeersDisposable.set(context.engine.peers.managedUpdatedRecentPeers().startStrict()) diff --git a/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift index 44c30394ce..f3747fb993 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift @@ -379,9 +379,10 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, UIGestureRecognizerD self.currentParams = (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) - if case .forum = self.location { + switch self.location { + case .forum, .savedMessagesChats: self.backgroundColor = .clear - } else { + default: self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor } let paneFrame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height)) diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 895adda249..bc6907c832 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -1394,6 +1394,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { var overrideImage: AvatarNodeImageOverride? if peer.id.isReplies { overrideImage = .repliesIcon + } else if peer.id.isAnonymousSavedMessages { + overrideImage = .anonymousSavedMessagesIcon } else if peer.id == item.context.account.peerId && !displayAsMessage { overrideImage = .savedMessagesIcon } else if peer.isDeleted { @@ -2294,6 +2296,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: titleFont, textColor: theme.titleColor) } else if let id = itemPeer.chatMainPeer?.id, id.isReplies { titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Replies, font: titleFont, textColor: theme.titleColor) + } else if let id = itemPeer.chatMainPeer?.id, id.isAnonymousSavedMessages { + //TODO:localize + titleAttributedString = NSAttributedString(string: "Author Hidden", font: titleFont, textColor: theme.titleColor) } else if let displayTitle = itemPeer.chatMainPeer?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) { let textColor: UIColor if case let .chatList(index) = item.index, index.messageIndex.id.peerId.namespace == Namespaces.Peer.SecretChat { diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift index a5c144fe88..d84bfb41ce 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift @@ -312,166 +312,68 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat return ChatListNodeViewUpdate(list: list, type: type, scrollPosition: nil) } case .savedMessagesChats: - if "".isEmpty { - let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: account.peerId) - - var isFirst = true - return account.postbox.combinedView(keys: [viewKey]) - |> map { views -> ChatListNodeViewUpdate in - guard let view = views.views[viewKey] as? MessageHistorySavedMessagesIndexView else { - preconditionFailure() - } - - var items: [EngineChatList.Item] = [] - for item in view.items { - guard let sourcePeer = item.peer else { - continue - } - - let sourceId = PeerId(item.id) - - var messages: [EngineMessage] = [] - if let topMessage = item.topMessage { - messages.append(EngineMessage(topMessage)) - } - - let mappedMessageIndex = MessageIndex(id: MessageId(peerId: sourceId, namespace: item.index.id.namespace, id: item.index.id.id), timestamp: item.index.timestamp) - - items.append(EngineChatList.Item( - id: .chatList(sourceId), - index: .chatList(ChatListIndex(pinningIndex: item.pinnedIndex.flatMap(UInt16.init), messageIndex: mappedMessageIndex)), - messages: messages, - readCounters: nil, - isMuted: false, - draft: nil, - threadData: nil, - renderedPeer: EngineRenderedPeer(peer: EnginePeer(sourcePeer)), - presence: nil, - hasUnseenMentions: false, - hasUnseenReactions: false, - forumTopicData: nil, - topForumTopicItems: [], - hasFailed: false, - isContact: false, - autoremoveTimeout: nil, - storyStats: nil - )) - } - - let list = EngineChatList( - items: items.reversed(), - groupItems: [], - additionalItems: [], - hasEarlier: false, - hasLater: false, - isLoading: view.isLoading - ) - - let type: ViewUpdateType - if isFirst { - type = .Initial - } else { - type = .Generic - } - isFirst = false - return ChatListNodeViewUpdate(list: list, type: type, scrollPosition: nil) + let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: account.peerId) + + var isFirst = true + return account.postbox.combinedView(keys: [viewKey]) + |> map { views -> ChatListNodeViewUpdate in + guard let view = views.views[viewKey] as? MessageHistorySavedMessagesIndexView else { + preconditionFailure() } - } else { - var isFirst = true - return account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: account.peerId, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 1000, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Set([Namespaces.Message.ScheduledCloud, Namespaces.Message.ScheduledLocal])), orderStatistics: []) - |> map { view, _, _ -> ChatListNodeViewUpdate in - let isLoading = view.isLoading - - var items: [EngineChatList.Item] = [] - - var topMessageByPeerId: [EnginePeer.Id: Message] = [:] - if !isLoading { - for entry in view.entries { - guard let threadId = entry.message.threadId else { - continue - } - let sourcePeerId = PeerId(threadId) - - if let currentTopMessage = topMessageByPeerId[sourcePeerId] { - if currentTopMessage.index < entry.index { - topMessageByPeerId[sourcePeerId] = entry.message - } - } else { - topMessageByPeerId[sourcePeerId] = entry.message - } - } - for (_, message) in topMessageByPeerId.sorted(by: { $0.value.index > $1.value.index }) { - guard let threadId = message.threadId else { - continue - } - let sourceId = PeerId(threadId) - var sourcePeer = message.peers[sourceId] - if sourcePeer == nil, let forwardInfo = message.forwardInfo, let authorSignature = forwardInfo.authorSignature { - sourcePeer = TelegramUser( - id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(1)), - accessHash: nil, - firstName: authorSignature, - lastName: nil, - username: nil, - phone: nil, - photo: [], - botInfo: nil, - restrictionInfo: nil, - flags: [], - emojiStatus: nil, - usernames: [], - storiesHidden: nil, - nameColor: nil, - backgroundEmojiId: nil, - profileColor: nil, - profileBackgroundEmojiId: nil - ) - } - guard let sourcePeer else { - continue - } - let mappedMessageIndex = MessageIndex(id: MessageId(peerId: sourceId, namespace: message.index.id.namespace, id: message.index.id.id), timestamp: message.index.timestamp) - items.append(EngineChatList.Item( - id: .chatList(sourceId), - index: .chatList(ChatListIndex(pinningIndex: nil, messageIndex: mappedMessageIndex)), - messages: [EngineMessage(message)], - readCounters: nil, - isMuted: false, - draft: nil, - threadData: nil, - renderedPeer: EngineRenderedPeer(peer: EnginePeer(sourcePeer)), - presence: nil, - hasUnseenMentions: false, - hasUnseenReactions: false, - forumTopicData: nil, - topForumTopicItems: [], - hasFailed: false, - isContact: false, - autoremoveTimeout: nil, - storyStats: nil - )) - } + var items: [EngineChatList.Item] = [] + for item in view.items { + guard let sourcePeer = item.peer else { + continue } - let list = EngineChatList( - items: items.reversed(), - groupItems: [], - additionalItems: [], - hasEarlier: false, - hasLater: false, - isLoading: isLoading - ) + let sourceId = PeerId(item.id) - let type: ViewUpdateType - if isFirst { - type = .Initial - } else { - type = .Generic + var messages: [EngineMessage] = [] + if let topMessage = item.topMessage { + messages.append(EngineMessage(topMessage)) } - isFirst = false - return ChatListNodeViewUpdate(list: list, type: type, scrollPosition: nil) + + let mappedMessageIndex = MessageIndex(id: MessageId(peerId: sourceId, namespace: item.index.id.namespace, id: item.index.id.id), timestamp: item.index.timestamp) + + items.append(EngineChatList.Item( + id: .chatList(sourceId), + index: .chatList(ChatListIndex(pinningIndex: item.pinnedIndex.flatMap(UInt16.init), messageIndex: mappedMessageIndex)), + messages: messages, + readCounters: nil, + isMuted: false, + draft: nil, + threadData: nil, + renderedPeer: EngineRenderedPeer(peer: EnginePeer(sourcePeer)), + presence: nil, + hasUnseenMentions: false, + hasUnseenReactions: false, + forumTopicData: nil, + topForumTopicItems: [], + hasFailed: false, + isContact: false, + autoremoveTimeout: nil, + storyStats: nil + )) } + + let list = EngineChatList( + items: items.reversed(), + groupItems: [], + additionalItems: [], + hasEarlier: false, + hasLater: false, + isLoading: view.isLoading + ) + + let type: ViewUpdateType + if isFirst { + type = .Initial + } else { + type = .Generic + } + isFirst = false + return ChatListNodeViewUpdate(list: list, type: type, scrollPosition: nil) } } } diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index 5be7b54cec..3a991bbbe0 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -439,7 +439,7 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { let controller = ContextController(presentationData: self.presentationData, source: .extracted(ContactContextExtractedContentSource(sourceNode: node, shouldBeDismissed: .single(false))), items: items, recognizer: nil, gesture: gesture) contactsController.presentInGlobalOverlay(controller) } else { - let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) + let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) let contextController = ContextController(presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: items, gesture: gesture) contactsController.presentInGlobalOverlay(contextController) diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index 8045723169..9e842373af 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -783,6 +783,9 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: titleBoldFont, textColor: textColor) } else if peer.id.isReplies { titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Replies, font: titleBoldFont, textColor: textColor) + } else if peer.id.isAnonymousSavedMessages { + //TODO:localize + titleAttributedString = NSAttributedString(string: "Author Hidden", font: titleBoldFont, textColor: textColor) } else if let firstName = user.firstName, let lastName = user.lastName, !firstName.isEmpty, !lastName.isEmpty { let string = NSMutableAttributedString() switch item.displayOrder { @@ -1030,6 +1033,8 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { overrideImage = .savedMessagesIcon } else if peer.id.isReplies, case .generalSearch = item.peerMode { overrideImage = .repliesIcon + } else if peer.id.isAnonymousSavedMessages, case .generalSearch = item.peerMode { + overrideImage = .anonymousSavedMessagesIcon } else if peer.isDeleted { overrideImage = .deletedIcon } diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index eddd2abc22..0e4d17c88d 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -217,10 +217,10 @@ public func galleryItemForEntry( if file.isVideo { let content: UniversalVideoContent if file.isAnimated { - content = NativeVideoContent(id: .message(message.stableId, file.fileId), userLocation: .peer(message.id.peerId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), loopVideo: true, enableSound: false, tempFilePath: tempFilePath, captureProtected: message.isCopyProtected(), storeAfterDownload: generateStoreAfterDownload?(message, file)) + content = NativeVideoContent(id: .message(message.stableId, file.fileId), userLocation: .peer(message.id.peerId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), loopVideo: true, enableSound: false, tempFilePath: tempFilePath, captureProtected: message.isCopyProtected() || message.containsSecretMedia, storeAfterDownload: generateStoreAfterDownload?(message, file)) } else { if true || (file.mimeType == "video/mpeg4" || file.mimeType == "video/mov" || file.mimeType == "video/mp4") { - content = NativeVideoContent(id: .message(message.stableId, file.fileId), userLocation: .peer(message.id.peerId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: .conservative, loopVideo: loopVideos, tempFilePath: tempFilePath, captureProtected: message.isCopyProtected(), storeAfterDownload: generateStoreAfterDownload?(message, file)) + content = NativeVideoContent(id: .message(message.stableId, file.fileId), userLocation: .peer(message.id.peerId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: .conservative, loopVideo: loopVideos, tempFilePath: tempFilePath, captureProtected: message.isCopyProtected() || message.containsSecretMedia, storeAfterDownload: generateStoreAfterDownload?(message, file)) } else { content = PlatformVideoContent(id: .message(message.id, message.stableId, file.fileId), userLocation: .peer(message.id.peerId), content: .file(.message(message: MessageReference(message), media: file)), streamVideo: streamVideos, loopVideo: loopVideos) } @@ -323,11 +323,11 @@ public func galleryItemForEntry( var content: UniversalVideoContent? switch websiteType(of: webpageContent.websiteName) { case .instagram where webpageContent.file != nil && webpageContent.image != nil && webpageContent.file!.isVideo: - content = NativeVideoContent(id: .message(message.stableId, webpageContent.file?.id ?? webpage.webpageId), userLocation: .peer(message.id.peerId), fileReference: .message(message: MessageReference(message), media: webpageContent.file!), imageReference: webpageContent.image.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: .conservative, enableSound: true, captureProtected: message.isCopyProtected(), storeAfterDownload: nil) + content = NativeVideoContent(id: .message(message.stableId, webpageContent.file?.id ?? webpage.webpageId), userLocation: .peer(message.id.peerId), fileReference: .message(message: MessageReference(message), media: webpageContent.file!), imageReference: webpageContent.image.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: .conservative, enableSound: true, captureProtected: message.isCopyProtected() || message.containsSecretMedia, storeAfterDownload: nil) default: if let embedUrl = webpageContent.embedUrl, let image = webpageContent.image { if let file = webpageContent.file, file.isVideo { - content = NativeVideoContent(id: .message(message.stableId, file.fileId), userLocation: .peer(message.id.peerId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: .conservative, loopVideo: loopVideos, tempFilePath: tempFilePath, captureProtected: message.isCopyProtected(), storeAfterDownload: generateStoreAfterDownload?(message, file)) + content = NativeVideoContent(id: .message(message.stableId, file.fileId), userLocation: .peer(message.id.peerId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: .conservative, loopVideo: loopVideos, tempFilePath: tempFilePath, captureProtected: message.isCopyProtected() || message.containsSecretMedia, storeAfterDownload: generateStoreAfterDownload?(message, file)) } else if URL(string: embedUrl)?.pathExtension == "mp4" { content = SystemVideoContent(userLocation: .peer(message.id.peerId), url: embedUrl, imageReference: .webPage(webPage: WebpageReference(webpage), media: image), dimensions: webpageContent.embedSize?.cgSize ?? CGSize(width: 640.0, height: 640.0), duration: webpageContent.duration.flatMap(Double.init) ?? 0.0) } diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index 7a17a43cf3..7ed9596605 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -1041,6 +1041,9 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: currentBoldFont, textColor: titleColor) } else if item.peer.id.isReplies { titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Replies, font: currentBoldFont, textColor: titleColor) + } else if item.peer.id.isAnonymousSavedMessages { + //TODO:localize + titleAttributedString = NSAttributedString(string: "Author Hidden", font: currentBoldFont, textColor: titleColor) } else if case let .user(user) = item.peer { if let firstName = user.firstName, let lastName = user.lastName, !firstName.isEmpty, !lastName.isEmpty { let string = NSMutableAttributedString() diff --git a/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift b/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift index 23be5e376b..3fe9f80400 100644 --- a/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift +++ b/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift @@ -818,7 +818,7 @@ public final class ListMessageFileItemNode: ListMessageNode { descriptionText = NSAttributedString(string: " ", font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor) } - if let _ = item.message?.threadId, let threadInfo = item.message?.associatedThreadInfo { + if let _ = item.message?.threadId, item.message?.id.peerId.namespace == Namespaces.Peer.CloudChannel, let threadInfo = item.message?.associatedThreadInfo { if isInstantVideo || isVoice { titleExtraData = (NSAttributedString(string: threadInfo.title, font: titleFont, textColor: item.presentationData.theme.theme.list.itemPrimaryTextColor), true, threadInfo.icon, threadInfo.iconColor) } else { diff --git a/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift b/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift index 9196c1b089..a640ceb8d1 100644 --- a/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift +++ b/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift @@ -509,7 +509,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode { var forumThreadTitle: (title: NSAttributedString, showIcon: Bool, iconId: Int64?, iconColor: Int32)? = nil var authorString = "" - if let message = item.message, let _ = message.threadId, let threadInfo = message.associatedThreadInfo { + if let message = item.message, let _ = message.threadId, item.message?.id.peerId.namespace == Namespaces.Peer.CloudChannel, let threadInfo = message.associatedThreadInfo { let fullAuthorString = stringForFullAuthorName(message: EngineMessage(message), strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, accountPeerId: item.context.account.peerId) authorString = fullAuthorString.first ?? "" forumThreadTitle = (NSAttributedString(string: threadInfo.title, font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor), true, threadInfo.icon, threadInfo.iconColor) diff --git a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift index 1fd43edf48..9e6ae6af5e 100644 --- a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift @@ -2327,7 +2327,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta }) } else { if let navigationController = controller.navigationController as? NavigationController { - navigationController.replaceAllButRootController(context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false)), animated: true) + navigationController.replaceAllButRootController(context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(.default)), animated: true) } } } diff --git a/submodules/PeerInfoUI/Sources/ConvertToSupergroupController.swift b/submodules/PeerInfoUI/Sources/ConvertToSupergroupController.swift index e5c9a37338..5b13bed75d 100644 --- a/submodules/PeerInfoUI/Sources/ConvertToSupergroupController.swift +++ b/submodules/PeerInfoUI/Sources/ConvertToSupergroupController.swift @@ -149,7 +149,7 @@ public func convertToSupergroupController(context: AccountContext, peerId: Engin if !alreadyConverting { convertDisposable.set((context.engine.peers.convertGroupToSupergroup(peerId: peerId) |> deliverOnMainQueue).start(next: { createdPeerId in - replaceControllerImpl?(context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: createdPeerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) + replaceControllerImpl?(context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: createdPeerId), subject: nil, botStart: nil, mode: .standard(.default))) })) } })]), nil) diff --git a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift index 990fb86e35..0a87d44f1d 100644 --- a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift +++ b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift @@ -486,7 +486,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController { })) }, contextAction: { peer, node, gesture in let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) + let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: peerNearbyContextMenuItems(context: context, peerId: peer.id, present: { c in presentControllerImpl?(c, nil) diff --git a/submodules/Postbox/Sources/MessageHistorySavedMessagesStatsView.swift b/submodules/Postbox/Sources/MessageHistorySavedMessagesStatsView.swift new file mode 100644 index 0000000000..014e0018f4 --- /dev/null +++ b/submodules/Postbox/Sources/MessageHistorySavedMessagesStatsView.swift @@ -0,0 +1,55 @@ +import Foundation + +final class MutableMessageHistorySavedMessagesStatsView: MutablePostboxView { + fileprivate let peerId: PeerId + fileprivate var count: Int = 0 + fileprivate var isLoading: Bool = false + + init(postbox: PostboxImpl, peerId: PeerId) { + self.peerId = peerId + + self.reload(postbox: postbox) + } + + private func reload(postbox: PostboxImpl) { + let validIndexBoundary = postbox.peerThreadCombinedStateTable.get(peerId: peerId)?.validIndexBoundary + self.isLoading = validIndexBoundary == nil + + if !self.isLoading { + self.count = postbox.messageHistoryThreadIndexTable.getCount(peerId: self.peerId) + } else { + self.count = 0 + } + } + + func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool { + var updated = false + + if transaction.updatedMessageThreadPeerIds.contains(self.peerId) { + self.reload(postbox: postbox) + updated = true + } + + return updated + } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + self.reload(postbox: postbox) + + return true + } + + func immutableView() -> PostboxView { + return MessageHistorySavedMessagesStatsView(self) + } +} + +public final class MessageHistorySavedMessagesStatsView: PostboxView { + public let isLoading: Bool + public let count: Int + + init(_ view: MutableMessageHistorySavedMessagesStatsView) { + self.isLoading = view.isLoading + self.count = view.count + } +} diff --git a/submodules/Postbox/Sources/MessageHistoryTable.swift b/submodules/Postbox/Sources/MessageHistoryTable.swift index 73a9659ef4..3a69f7c5f7 100644 --- a/submodules/Postbox/Sources/MessageHistoryTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTable.swift @@ -1554,7 +1554,7 @@ final class MessageHistoryTable: Table { let updatedGroupInfo = self.updateMovingGroupInfoInNamespace(index: updatedIndex, updatedIndex: updatedIndex, groupingKey: message.groupingKey, previousInfo: previousMessage.groupInfo, updatedGroupInfos: &updatedGroupInfos) - if previousMessage.tags != message.tags || index != updatedIndex { + if previousMessage.tags != message.tags || previousMessage.threadId != message.threadId || index != updatedIndex { if !previousMessage.tags.isEmpty { self.tagsTable.remove(tags: previousMessage.tags, index: index, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) if let threadId = previousMessage.threadId { diff --git a/submodules/Postbox/Sources/MessageThreadIndexTable.swift b/submodules/Postbox/Sources/MessageThreadIndexTable.swift index 3f9dcc5e91..36cf7be63f 100644 --- a/submodules/Postbox/Sources/MessageThreadIndexTable.swift +++ b/submodules/Postbox/Sources/MessageThreadIndexTable.swift @@ -300,6 +300,10 @@ class MessageHistoryThreadIndexTable: Table { return result } + func getCount(peerId: PeerId) -> Int { + return self.valueBox.count(self.table, start: self.lowerBound(peerId: peerId), end: self.upperBound(peerId: peerId)) + } + override func beforeCommit() { super.beforeCommit() diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 8e078ad6f0..9931b697a7 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -1074,6 +1074,11 @@ public final class Transaction { assert(!self.disposed) self.postbox?.scanMessages(peerId: peerId, namespace: namespace, tag: tag, f) } + + public func scanMessages(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, tag: MessageTags, _ f: (Message) -> Bool) { + assert(!self.disposed) + self.postbox?.scanMessages(peerId: peerId, threadId: threadId, namespace: namespace, tag: tag, f) + } public func scanTopMessages(peerId: PeerId, namespace: MessageId.Namespace, limit: Int, _ f: (Message) -> Bool) { assert(!self.disposed) @@ -1336,6 +1341,26 @@ public final class Transaction { public func getPeerStoryStats(peerId: PeerId) -> PeerStoryStats? { return fetchPeerStoryStats(postbox: self.postbox!, peerId: peerId) } + + public func searchSubPeers(peerId: PeerId, query: String, indexNameMapping: [PeerId: [PeerIndexNameRepresentation]]) -> [Peer] { + let allThreads = self.postbox!.messageHistoryThreadIndexTable.getAll(peerId: peerId) + var matchingPeers: [(Peer, MessageIndex)] = [] + for (threadId, index, _) in allThreads { + if let peer = self.postbox!.peerTable.get(PeerId(threadId)) { + if let mappings = indexNameMapping[peer.id] { + inner: for mapping in mappings { + if mapping.matchesByTokens(query) { + matchingPeers.append((peer, index)) + break inner + } + } + } else if peer.indexName.matchesByTokens(query) { + matchingPeers.append((peer, index)) + } + } + } + return matchingPeers.sorted(by: { $0.1 > $1.1 }).map(\.0) + } } public enum PostboxResult { @@ -3937,6 +3962,28 @@ final class PostboxImpl { } } } + + fileprivate func scanMessages(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, tag: MessageTags, _ f: (Message) -> Bool) { + var index = MessageIndex.lowerBound(peerId: peerId, namespace: namespace) + while true { + let indices = self.messageHistoryThreadTagsTable.laterIndices(tag: tag, threadId: threadId, peerId: peerId, namespace: namespace, index: index, includeFrom: false, count: 10) + for index in indices { + if let message = self.messageHistoryTable.getMessage(index) { + if !f(self.renderIntermediateMessage(message)) { + break + } + } else { + assertionFailure() + break + } + } + if let last = indices.last { + index = last + } else { + break + } + } + } fileprivate func scanTopMessages(peerId: PeerId, namespace: MessageId.Namespace, limit: Int, _ f: (Message) -> Bool) { let lowerBound = MessageIndex.lowerBound(peerId: peerId, namespace: namespace) diff --git a/submodules/Postbox/Sources/Views.swift b/submodules/Postbox/Sources/Views.swift index f90a0a7d9b..67ccf5d34c 100644 --- a/submodules/Postbox/Sources/Views.swift +++ b/submodules/Postbox/Sources/Views.swift @@ -47,6 +47,7 @@ public enum PostboxViewKey: Hashable { case peerStoryStats(peerIds: Set) case story(id: StoryId) case savedMessagesIndex(peerId: PeerId) + case savedMessagesStats(peerId: PeerId) public func hash(into hasher: inout Hasher) { switch self { @@ -156,6 +157,8 @@ public enum PostboxViewKey: Hashable { hasher.combine(id) case let .savedMessagesIndex(peerId): hasher.combine(peerId) + case let .savedMessagesStats(peerId): + hasher.combine(peerId) } } @@ -437,6 +440,12 @@ public enum PostboxViewKey: Hashable { } else { return false } + case let .savedMessagesStats(peerId): + if case .savedMessagesStats(peerId) = rhs { + return true + } else { + return false + } } } } @@ -535,5 +544,7 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost return MutableStoryView(postbox: postbox, id: id) case let .savedMessagesIndex(peerId): return MutableMessageHistorySavedMessagesIndexView(postbox: postbox, peerId: peerId) + case let .savedMessagesStats(peerId): + return MutableMessageHistorySavedMessagesStatsView(postbox: postbox, peerId: peerId) } } diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift index 39af380686..8d4f0180be 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift @@ -997,7 +997,7 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList allItems.append(contentsOf: profileItems) let savedMessages = SettingsSearchableItem(id: .savedMessages(0), title: strings.Settings_SavedMessages, alternate: synonyms(strings.SettingsSearch_Synonyms_SavedMessages), icon: .savedMessages, breadcrumbs: [], present: { context, _, present in - present(.push, context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: context.account.peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) + present(.push, context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: context.account.peerId), subject: nil, botStart: nil, mode: .standard(.default))) }) allItems.append(savedMessages) @@ -1057,7 +1057,7 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList let _ = (context.engine.peers.supportPeerId() |> deliverOnMainQueue).start(next: { peerId in if let peerId = peerId { - present(.push, context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) + present(.push, context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(.default))) } }) }) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 44034adce3..d272aeb803 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -6,6 +6,7 @@ public enum Api { public enum channels {} public enum chatlists {} public enum contacts {} + public enum feed {} public enum help {} public enum messages {} public enum payments {} @@ -26,6 +27,7 @@ public enum Api { public enum channels {} public enum chatlists {} public enum contacts {} + public enum feed {} public enum folders {} public enum help {} public enum langpack {} @@ -252,6 +254,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1103040667] = { return Api.ExportedContactToken.parse_exportedContactToken($0) } dict[1571494644] = { return Api.ExportedMessageLink.parse_exportedMessageLink($0) } dict[1070138683] = { return Api.ExportedStoryLink.parse_exportedStoryLink($0) } + dict[1348066419] = { return Api.FeedPosition.parse_feedPosition($0) } dict[-207944868] = { return Api.FileHash.parse_fileHash($0) } dict[-11252123] = { return Api.Folder.parse_folder($0) } dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) } @@ -539,7 +542,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1859134776] = { return Api.MessageEntity.parse_messageEntityUrl($0) } dict[-297296796] = { return Api.MessageExtendedMedia.parse_messageExtendedMedia($0) } dict[-1386050360] = { return Api.MessageExtendedMedia.parse_messageExtendedMediaPreview($0) } - dict[1601666510] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) } + dict[1313731771] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) } dict[1882335561] = { return Api.MessageMedia.parse_messageMediaContact($0) } dict[1065280907] = { return Api.MessageMedia.parse_messageMediaDice($0) } dict[1291114285] = { return Api.MessageMedia.parse_messageMediaDocument($0) } @@ -949,6 +952,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1218471511] = { return Api.Update.parse_updateReadChannelOutbox($0) } dict[-78886548] = { return Api.Update.parse_updateReadFeaturedEmojiStickers($0) } dict[1461528386] = { return Api.Update.parse_updateReadFeaturedStickers($0) } + dict[1951948721] = { return Api.Update.parse_updateReadFeed($0) } dict[-1667805217] = { return Api.Update.parse_updateReadHistoryInbox($0) } dict[791617983] = { return Api.Update.parse_updateReadHistoryOutbox($0) } dict[-131960447] = { return Api.Update.parse_updateReadMessagesContents($0) } @@ -1088,6 +1092,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1891070632] = { return Api.contacts.TopPeers.parse_topPeers($0) } dict[-1255369827] = { return Api.contacts.TopPeers.parse_topPeersDisabled($0) } dict[-567906571] = { return Api.contacts.TopPeers.parse_topPeersNotModified($0) } + dict[-587770695] = { return Api.feed.FeedMessages.parse_feedMessages($0) } + dict[-619039485] = { return Api.feed.FeedMessages.parse_feedMessagesNotModified($0) } dict[-585598930] = { return Api.help.AppConfig.parse_appConfig($0) } dict[2094949405] = { return Api.help.AppConfig.parse_appConfigNotModified($0) } dict[-860107216] = { return Api.help.AppUpdate.parse_appUpdate($0) } @@ -1463,6 +1469,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.ExportedStoryLink: _1.serialize(buffer, boxed) + case let _1 as Api.FeedPosition: + _1.serialize(buffer, boxed) case let _1 as Api.FileHash: _1.serialize(buffer, boxed) case let _1 as Api.Folder: @@ -1959,6 +1967,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.contacts.TopPeers: _1.serialize(buffer, boxed) + case let _1 as Api.feed.FeedMessages: + _1.serialize(buffer, boxed) case let _1 as Api.help.AppConfig: _1.serialize(buffer, boxed) case let _1 as Api.help.AppUpdate: diff --git a/submodules/TelegramApi/Sources/Api13.swift b/submodules/TelegramApi/Sources/Api13.swift index faeac515b0..f82c179069 100644 --- a/submodules/TelegramApi/Sources/Api13.swift +++ b/submodules/TelegramApi/Sources/Api13.swift @@ -618,13 +618,13 @@ public extension Api { } public extension Api { enum MessageFwdHeader: TypeConstructorDescription { - case messageFwdHeader(flags: Int32, fromId: Api.Peer?, fromName: String?, date: Int32, channelPost: Int32?, postAuthor: String?, savedFromPeer: Api.Peer?, savedFromMsgId: Int32?, psaType: String?) + case messageFwdHeader(flags: Int32, fromId: Api.Peer?, fromName: String?, date: Int32, channelPost: Int32?, postAuthor: String?, savedFromPeer: Api.Peer?, savedFromMsgId: Int32?, savedFromId: Api.Peer?, savedFromName: String?, savedDate: Int32?, psaType: String?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .messageFwdHeader(let flags, let fromId, let fromName, let date, let channelPost, let postAuthor, let savedFromPeer, let savedFromMsgId, let psaType): + case .messageFwdHeader(let flags, let fromId, let fromName, let date, let channelPost, let postAuthor, let savedFromPeer, let savedFromMsgId, let savedFromId, let savedFromName, let savedDate, let psaType): if boxed { - buffer.appendInt32(1601666510) + buffer.appendInt32(1313731771) } serializeInt32(flags, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {fromId!.serialize(buffer, true)} @@ -634,6 +634,9 @@ public extension Api { if Int(flags) & Int(1 << 3) != 0 {serializeString(postAuthor!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 4) != 0 {savedFromPeer!.serialize(buffer, true)} if Int(flags) & Int(1 << 4) != 0 {serializeInt32(savedFromMsgId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 8) != 0 {savedFromId!.serialize(buffer, true)} + if Int(flags) & Int(1 << 9) != 0 {serializeString(savedFromName!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 10) != 0 {serializeInt32(savedDate!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 6) != 0 {serializeString(psaType!, buffer: buffer, boxed: false)} break } @@ -641,8 +644,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .messageFwdHeader(let flags, let fromId, let fromName, let date, let channelPost, let postAuthor, let savedFromPeer, let savedFromMsgId, let psaType): - return ("messageFwdHeader", [("flags", flags as Any), ("fromId", fromId as Any), ("fromName", fromName as Any), ("date", date as Any), ("channelPost", channelPost as Any), ("postAuthor", postAuthor as Any), ("savedFromPeer", savedFromPeer as Any), ("savedFromMsgId", savedFromMsgId as Any), ("psaType", psaType as Any)]) + case .messageFwdHeader(let flags, let fromId, let fromName, let date, let channelPost, let postAuthor, let savedFromPeer, let savedFromMsgId, let savedFromId, let savedFromName, let savedDate, let psaType): + return ("messageFwdHeader", [("flags", flags as Any), ("fromId", fromId as Any), ("fromName", fromName as Any), ("date", date as Any), ("channelPost", channelPost as Any), ("postAuthor", postAuthor as Any), ("savedFromPeer", savedFromPeer as Any), ("savedFromMsgId", savedFromMsgId as Any), ("savedFromId", savedFromId as Any), ("savedFromName", savedFromName as Any), ("savedDate", savedDate as Any), ("psaType", psaType as Any)]) } } @@ -667,8 +670,16 @@ public extension Api { } } var _8: Int32? if Int(_1!) & Int(1 << 4) != 0 {_8 = reader.readInt32() } - var _9: String? - if Int(_1!) & Int(1 << 6) != 0 {_9 = parseString(reader) } + var _9: Api.Peer? + if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.Peer + } } + var _10: String? + if Int(_1!) & Int(1 << 9) != 0 {_10 = parseString(reader) } + var _11: Int32? + if Int(_1!) & Int(1 << 10) != 0 {_11 = reader.readInt32() } + var _12: String? + if Int(_1!) & Int(1 << 6) != 0 {_12 = parseString(reader) } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 5) == 0) || _3 != nil @@ -677,9 +688,12 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.MessageFwdHeader.messageFwdHeader(flags: _1!, fromId: _2, fromName: _3, date: _4!, channelPost: _5, postAuthor: _6, savedFromPeer: _7, savedFromMsgId: _8, psaType: _9) + let _c9 = (Int(_1!) & Int(1 << 8) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 9) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 10) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 6) == 0) || _12 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { + return Api.MessageFwdHeader.messageFwdHeader(flags: _1!, fromId: _2, fromName: _3, date: _4!, channelPost: _5, postAuthor: _6, savedFromPeer: _7, savedFromMsgId: _8, savedFromId: _9, savedFromName: _10, savedDate: _11, psaType: _12) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api22.swift b/submodules/TelegramApi/Sources/Api22.swift index bbc72d8b97..1242ceb8a0 100644 --- a/submodules/TelegramApi/Sources/Api22.swift +++ b/submodules/TelegramApi/Sources/Api22.swift @@ -559,6 +559,7 @@ public extension Api { case updateReadChannelOutbox(channelId: Int64, maxId: Int32) case updateReadFeaturedEmojiStickers case updateReadFeaturedStickers + case updateReadFeed(flags: Int32, filterId: Int32, maxPosition: Api.FeedPosition, unreadCount: Int32?, unreadMutedCount: Int32?) case updateReadHistoryInbox(flags: Int32, folderId: Int32?, peer: Api.Peer, maxId: Int32, stillUnreadCount: Int32, pts: Int32, ptsCount: Int32) case updateReadHistoryOutbox(peer: Api.Peer, maxId: Int32, pts: Int32, ptsCount: Int32) case updateReadMessagesContents(flags: Int32, messages: [Int32], pts: Int32, ptsCount: Int32, date: Int32?) @@ -1450,6 +1451,16 @@ public extension Api { buffer.appendInt32(1461528386) } + break + case .updateReadFeed(let flags, let filterId, let maxPosition, let unreadCount, let unreadMutedCount): + if boxed { + buffer.appendInt32(1951948721) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(filterId, buffer: buffer, boxed: false) + maxPosition.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(unreadCount!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(unreadMutedCount!, buffer: buffer, boxed: false)} break case .updateReadHistoryInbox(let flags, let folderId, let peer, let maxId, let stillUnreadCount, let pts, let ptsCount): if boxed { @@ -1866,6 +1877,8 @@ public extension Api { return ("updateReadFeaturedEmojiStickers", []) case .updateReadFeaturedStickers: return ("updateReadFeaturedStickers", []) + case .updateReadFeed(let flags, let filterId, let maxPosition, let unreadCount, let unreadMutedCount): + return ("updateReadFeed", [("flags", flags as Any), ("filterId", filterId as Any), ("maxPosition", maxPosition as Any), ("unreadCount", unreadCount as Any), ("unreadMutedCount", unreadMutedCount as Any)]) case .updateReadHistoryInbox(let flags, let folderId, let peer, let maxId, let stillUnreadCount, let pts, let ptsCount): return ("updateReadHistoryInbox", [("flags", flags as Any), ("folderId", folderId as Any), ("peer", peer as Any), ("maxId", maxId as Any), ("stillUnreadCount", stillUnreadCount as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) case .updateReadHistoryOutbox(let peer, let maxId, let pts, let ptsCount): @@ -3688,6 +3701,31 @@ public extension Api { public static func parse_updateReadFeaturedStickers(_ reader: BufferReader) -> Update? { return Api.Update.updateReadFeaturedStickers } + public static func parse_updateReadFeed(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Api.FeedPosition? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.FeedPosition + } + var _4: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt32() } + var _5: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.Update.updateReadFeed(flags: _1!, filterId: _2!, maxPosition: _3!, unreadCount: _4, unreadMutedCount: _5) + } + else { + return nil + } + } public static func parse_updateReadHistoryInbox(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() diff --git a/submodules/TelegramApi/Sources/Api26.swift b/submodules/TelegramApi/Sources/Api26.swift index 60f1674a87..2e926c8b0f 100644 --- a/submodules/TelegramApi/Sources/Api26.swift +++ b/submodules/TelegramApi/Sources/Api26.swift @@ -634,6 +634,102 @@ public extension Api.contacts { } } +public extension Api.feed { + enum FeedMessages: TypeConstructorDescription { + case feedMessages(flags: Int32, maxPosition: Api.FeedPosition?, minPosition: Api.FeedPosition?, readMaxPosition: Api.FeedPosition?, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + case feedMessagesNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .feedMessages(let flags, let maxPosition, let minPosition, let readMaxPosition, let messages, let chats, let users): + if boxed { + buffer.appendInt32(-587770695) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {maxPosition!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {minPosition!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {readMaxPosition!.serialize(buffer, true)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .feedMessagesNotModified: + if boxed { + buffer.appendInt32(-619039485) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .feedMessages(let flags, let maxPosition, let minPosition, let readMaxPosition, let messages, let chats, let users): + return ("feedMessages", [("flags", flags as Any), ("maxPosition", maxPosition as Any), ("minPosition", minPosition as Any), ("readMaxPosition", readMaxPosition as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + case .feedMessagesNotModified: + return ("feedMessagesNotModified", []) + } + } + + public static func parse_feedMessages(_ reader: BufferReader) -> FeedMessages? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.FeedPosition? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.FeedPosition + } } + var _3: Api.FeedPosition? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.FeedPosition + } } + var _4: Api.FeedPosition? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.FeedPosition + } } + var _5: [Api.Message]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _6: [Api.Chat]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _7: [Api.User]? + if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.feed.FeedMessages.feedMessages(flags: _1!, maxPosition: _2, minPosition: _3, readMaxPosition: _4, messages: _5!, chats: _6!, users: _7!) + } + else { + return nil + } + } + public static func parse_feedMessagesNotModified(_ reader: BufferReader) -> FeedMessages? { + return Api.feed.FeedMessages.feedMessagesNotModified + } + + } +} public extension Api.help { enum AppConfig: TypeConstructorDescription { case appConfig(hash: Int32, config: Api.JSONValue) @@ -1304,89 +1400,3 @@ public extension Api.help { } } -public extension Api.help { - enum PremiumPromo: TypeConstructorDescription { - case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], periodOptions: [Api.PremiumSubscriptionOption], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users): - if boxed { - buffer.appendInt32(1395946908) - } - serializeString(statusText, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(statusEntities.count)) - for item in statusEntities { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(videoSections.count)) - for item in videoSections { - serializeString(item, buffer: buffer, boxed: false) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(videos.count)) - for item in videos { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(periodOptions.count)) - for item in periodOptions { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users): - return ("premiumPromo", [("statusText", statusText as Any), ("statusEntities", statusEntities as Any), ("videoSections", videoSections as Any), ("videos", videos as Any), ("periodOptions", periodOptions as Any), ("users", users as Any)]) - } - } - - public static func parse_premiumPromo(_ reader: BufferReader) -> PremiumPromo? { - var _1: String? - _1 = parseString(reader) - var _2: [Api.MessageEntity]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) - } - var _3: [String]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) - } - var _4: [Api.Document]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) - } - var _5: [Api.PremiumSubscriptionOption]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PremiumSubscriptionOption.self) - } - var _6: [Api.User]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, periodOptions: _5!, users: _6!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api27.swift b/submodules/TelegramApi/Sources/Api27.swift index 90373cb39c..badbd70145 100644 --- a/submodules/TelegramApi/Sources/Api27.swift +++ b/submodules/TelegramApi/Sources/Api27.swift @@ -1,3 +1,89 @@ +public extension Api.help { + enum PremiumPromo: TypeConstructorDescription { + case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], periodOptions: [Api.PremiumSubscriptionOption], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users): + if boxed { + buffer.appendInt32(1395946908) + } + serializeString(statusText, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(statusEntities.count)) + for item in statusEntities { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(videoSections.count)) + for item in videoSections { + serializeString(item, buffer: buffer, boxed: false) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(videos.count)) + for item in videos { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(periodOptions.count)) + for item in periodOptions { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users): + return ("premiumPromo", [("statusText", statusText as Any), ("statusEntities", statusEntities as Any), ("videoSections", videoSections as Any), ("videos", videos as Any), ("periodOptions", periodOptions as Any), ("users", users as Any)]) + } + } + + public static func parse_premiumPromo(_ reader: BufferReader) -> PremiumPromo? { + var _1: String? + _1 = parseString(reader) + var _2: [Api.MessageEntity]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + } + var _3: [String]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) + } + var _4: [Api.Document]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + var _5: [Api.PremiumSubscriptionOption]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PremiumSubscriptionOption.self) + } + var _6: [Api.User]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, periodOptions: _5!, users: _6!) + } + else { + return nil + } + } + + } +} public extension Api.help { enum PromoData: TypeConstructorDescription { case promoData(flags: Int32, expires: Int32, peer: Api.Peer, chats: [Api.Chat], users: [Api.User], psaType: String?, psaMessage: String?) diff --git a/submodules/TelegramApi/Sources/Api32.swift b/submodules/TelegramApi/Sources/Api32.swift index f4b884aef4..ed20259b19 100644 --- a/submodules/TelegramApi/Sources/Api32.swift +++ b/submodules/TelegramApi/Sources/Api32.swift @@ -3770,6 +3770,44 @@ public extension Api.functions.contacts { }) } } +public extension Api.functions.feed { + static func getFeed(flags: Int32, filterId: Int32, offsetPosition: Api.FeedPosition?, addOffset: Int32, limit: Int32, maxPosition: Api.FeedPosition?, minPosition: Api.FeedPosition?, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2121717715) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(filterId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {offsetPosition!.serialize(buffer, true)} + serializeInt32(addOffset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {maxPosition!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {minPosition!.serialize(buffer, true)} + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "feed.getFeed", parameters: [("flags", String(describing: flags)), ("filterId", String(describing: filterId)), ("offsetPosition", String(describing: offsetPosition)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxPosition", String(describing: maxPosition)), ("minPosition", String(describing: minPosition)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.feed.FeedMessages? in + let reader = BufferReader(buffer) + var result: Api.feed.FeedMessages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.feed.FeedMessages + } + return result + }) + } +} +public extension Api.functions.feed { + static func readFeed(filterId: Int32, maxPosition: Api.FeedPosition) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1271479809) + serializeInt32(filterId, buffer: buffer, boxed: false) + maxPosition.serialize(buffer, true) + return (FunctionDescription(name: "feed.readFeed", parameters: [("filterId", String(describing: filterId)), ("maxPosition", String(describing: maxPosition))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} public extension Api.functions.folders { static func editPeerFolders(folderPeers: [Api.InputFolderPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramApi/Sources/Api6.swift b/submodules/TelegramApi/Sources/Api6.swift index 60cf7f4290..ecfcd55b20 100644 --- a/submodules/TelegramApi/Sources/Api6.swift +++ b/submodules/TelegramApi/Sources/Api6.swift @@ -74,6 +74,52 @@ public extension Api { } } +public extension Api { + enum FeedPosition: TypeConstructorDescription { + case feedPosition(date: Int32, peer: Api.Peer, id: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .feedPosition(let date, let peer, let id): + if boxed { + buffer.appendInt32(1348066419) + } + serializeInt32(date, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .feedPosition(let date, let peer, let id): + return ("feedPosition", [("date", date as Any), ("peer", peer as Any), ("id", id as Any)]) + } + } + + public static func parse_feedPosition(_ reader: BufferReader) -> FeedPosition? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.FeedPosition.feedPosition(date: _1!, peer: _2!, id: _3!) + } + else { + return nil + } + } + + } +} public extension Api { enum FileHash: TypeConstructorDescription { case fileHash(offset: Int64, limit: Int32, hash: Buffer) diff --git a/submodules/TelegramCore/Sources/Account/AccountManager.swift b/submodules/TelegramCore/Sources/Account/AccountManager.swift index 548368101b..035ffaa88e 100644 --- a/submodules/TelegramCore/Sources/Account/AccountManager.swift +++ b/submodules/TelegramCore/Sources/Account/AccountManager.swift @@ -116,6 +116,7 @@ private var declaredEncodables: Void = { declareEncodable(OutgoingMessageInfoAttribute.self, f: { OutgoingMessageInfoAttribute(decoder: $0) }) declareEncodable(ForwardSourceInfoAttribute.self, f: { ForwardSourceInfoAttribute(decoder: $0) }) declareEncodable(SourceReferenceMessageAttribute.self, f: { SourceReferenceMessageAttribute(decoder: $0) }) + declareEncodable(SourceAuthorInfoMessageAttribute.self, f: { SourceAuthorInfoMessageAttribute(decoder: $0) }) declareEncodable(EditedMessageAttribute.self, f: { EditedMessageAttribute(decoder: $0) }) declareEncodable(ReplyMarkupMessageAttribute.self, f: { ReplyMarkupMessageAttribute(decoder: $0) }) declareEncodable(OutgoingChatContextResultMessageAttribute.self, f: { OutgoingChatContextResultMessageAttribute(decoder: $0) }) diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index 940503e49a..94136f437f 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -155,13 +155,16 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { if let fwdHeader = fwdHeader { switch fwdHeader { - case let .messageFwdHeader(_, fromId, _, _, _, _, savedFromPeer, _, _): + case let .messageFwdHeader(_, fromId, _, _, _, _, savedFromPeer, _, savedFromId, _, _, _): if let fromId = fromId { result.append(fromId.peerId) } if let savedFromPeer = savedFromPeer { result.append(savedFromPeer.peerId) } + if let savedFromId = savedFromId { + result.append(savedFromId.peerId) + } } } @@ -669,7 +672,7 @@ extension StoreMessage { var forwardInfo: StoreMessageForwardInfo? if let fwdFrom = fwdFrom { switch fwdFrom { - case let .messageFwdHeader(flags, fromId, fromName, date, channelPost, postAuthor, savedFromPeer, savedFromMsgId, psaType): + case let .messageFwdHeader(flags, fromId, fromName, date, channelPost, postAuthor, savedFromPeer, savedFromMsgId, savedFromId, savedFromName, savedDate, psaType): var forwardInfoFlags: MessageForwardInfo.Flags = [] let isImported = (flags & (1 << 7)) != 0 if isImported { @@ -693,15 +696,20 @@ extension StoreMessage { authorId = fromId.peerId } } + + let originalOutgoing = (flags & (1 << 11)) != 0 if let savedFromPeer = savedFromPeer, let savedFromMsgId = savedFromMsgId { let peerId: PeerId = savedFromPeer.peerId let messageId: MessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: savedFromMsgId) attributes.append(SourceReferenceMessageAttribute(messageId: messageId)) } + if savedFromId != nil || savedFromName != nil || savedDate != nil || originalOutgoing { + attributes.append(SourceAuthorInfoMessageAttribute(originalAuthor: savedFromId?.peerId, originalAuthorName: savedFromName, orignalDate: savedDate, originalOutgoing: originalOutgoing)) + } if let authorId = authorId { - forwardInfo = StoreMessageForwardInfo(authorId: authorId, sourceId: sourceId, sourceMessageId: sourceMessageId, date: date, authorSignature: postAuthor, psaType: psaType, flags: forwardInfoFlags) + forwardInfo = StoreMessageForwardInfo(authorId: authorId, sourceId: sourceId, sourceMessageId: sourceMessageId, date: date, authorSignature: postAuthor, psaType: psaType, flags: forwardInfoFlags) } else if let sourceId = sourceId { forwardInfo = StoreMessageForwardInfo(authorId: sourceId, sourceId: sourceId, sourceMessageId: sourceMessageId, date: date, authorSignature: postAuthor, psaType: psaType, flags: forwardInfoFlags) } else if let postAuthor = postAuthor ?? fromName { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMessageAttribute.swift index d4d99db781..a0cad0ee13 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMessageAttribute.swift @@ -109,7 +109,7 @@ public class QuotedReplyMessageAttribute: MessageAttribute { extension QuotedReplyMessageAttribute { convenience init(apiHeader: Api.MessageFwdHeader, quote: EngineMessageReplyQuote?, isQuote: Bool) { switch apiHeader { - case let .messageFwdHeader(_, fromId, fromName, _, _, _, _, _, _): + case let .messageFwdHeader(_, fromId, fromName, _, _, _, _, _, _, _, _, _): self.init(peerId: fromId?.peerId, authorName: fromName, quote: quote, isQuote: isQuote) } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SourceReferenceMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SourceReferenceMessageAttribute.swift index 5865f83a21..5b7d48e096 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SourceReferenceMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SourceReferenceMessageAttribute.swift @@ -3,17 +3,20 @@ import Postbox public class SourceReferenceMessageAttribute: MessageAttribute { public let messageId: MessageId + public let associatedMessageIds: [MessageId] = [] public let associatedPeerIds: [PeerId] public init(messageId: MessageId) { self.messageId = messageId + self.associatedPeerIds = [messageId.peerId] } required public init(decoder: PostboxDecoder) { let namespaceAndId: Int64 = decoder.decodeInt64ForKey("i", orElse: 0) self.messageId = MessageId(peerId: PeerId(decoder.decodeInt64ForKey("p", orElse: 0)), namespace: Int32(namespaceAndId & 0xffffffff), id: Int32((namespaceAndId >> 32) & 0xffffffff)) + self.associatedPeerIds = [self.messageId.peerId] } @@ -24,4 +27,57 @@ public class SourceReferenceMessageAttribute: MessageAttribute { } } - +public class SourceAuthorInfoMessageAttribute: MessageAttribute { + public let originalAuthor: PeerId? + public let originalAuthorName: String? + public let orignalDate: Int32? + public let originalOutgoing: Bool + + public let associatedMessageIds: [MessageId] = [] + public let associatedPeerIds: [PeerId] + + public init(originalAuthor: PeerId?, originalAuthorName: String?, orignalDate: Int32?, originalOutgoing: Bool) { + self.originalAuthor = originalAuthor + self.originalAuthorName = originalAuthorName + self.orignalDate = orignalDate + self.originalOutgoing = originalOutgoing + + if let originalAuthor = self.originalAuthor { + self.associatedPeerIds = [originalAuthor] + } else { + self.associatedPeerIds = [] + } + } + + required public init(decoder: PostboxDecoder) { + self.originalAuthor = decoder.decodeOptionalInt64ForKey("oa").flatMap(PeerId.init) + self.originalAuthorName = decoder.decodeOptionalStringForKey("oan") + self.orignalDate = decoder.decodeOptionalInt32ForKey("od") + self.originalOutgoing = decoder.decodeBoolForKey("oout", orElse: false) + + if let originalAuthor = self.originalAuthor { + self.associatedPeerIds = [originalAuthor] + } else { + self.associatedPeerIds = [] + } + } + + public func encode(_ encoder: PostboxEncoder) { + if let originalAuthor = self.originalAuthor { + encoder.encodeInt64(originalAuthor.toInt64(), forKey: "oa") + } else { + encoder.encodeNil(forKey: "oa") + } + if let originalAuthorName = self.originalAuthorName { + encoder.encodeString(originalAuthorName, forKey: "oan") + } else { + encoder.encodeNil(forKey: "oan") + } + if let orignalDate = self.orignalDate { + encoder.encodeInt32(orignalDate, forKey: "od") + } else { + encoder.encodeNil(forKey: "od") + } + encoder.encodeBool(self.originalOutgoing, forKey: "oout") + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 2c37eb7f79..8db4bf2727 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -471,7 +471,18 @@ public extension TelegramEngine { for i in 0 ..< tags.count { let (count, maxId) = counts[i] if let count = count { - transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: tags[i], namespace: Namespaces.Message.Cloud, count: count, maxId: maxId ?? 1) + if count == 0, peerId == account.peerId, let threadId { + var localCount = 0 + var maxId: Int32 = 1 + transaction.scanMessages(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, tag: tags[i], { message in + localCount += 1 + maxId = max(maxId, message.id.id) + return true + }) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: tags[i], namespace: Namespaces.Message.Cloud, count: Int32(localCount), maxId: maxId) + } else { + transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: tags[i], namespace: Namespaces.Message.Cloud, count: count, maxId: maxId ?? 1) + } } } } @@ -1300,7 +1311,25 @@ public extension TelegramEngine { } public func synchronouslyIsMessageDeletedInteractively(ids: [EngineMessage.Id]) -> [EngineMessage.Id] { - return account.stateManager.synchronouslyIsMessageDeletedInteractively(ids: ids) + return self.account.stateManager.synchronouslyIsMessageDeletedInteractively(ids: ids) + } + + public func savedMessagesPeersStats() -> Signal { + return self.account.postbox.combinedView(keys: [.savedMessagesStats(peerId: self.account.peerId)]) + |> map { views -> Int? in + guard let view = views.views[.savedMessagesStats(peerId: self.account.peerId)] as? MessageHistorySavedMessagesStatsView else { + return nil + } + if view.isLoading { + return nil + } else { + return view.count + } + } + } + + public func searchLocalSavedMessagesPeers(query: String, indexNameMapping: [EnginePeer.Id: [PeerIndexNameRepresentation]]) -> Signal<[EnginePeer], NoError> { + return _internal_searchLocalSavedMessagesPeers(account: self.account, query: query, indexNameMapping: indexNameMapping) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift index ccdb358a1f..74f69e52fa 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift @@ -83,3 +83,8 @@ public func _internal_searchPeers(accountPeerId: PeerId, postbox: Postbox, netwo return processedSearchResult } +func _internal_searchLocalSavedMessagesPeers(account: Account, query: String, indexNameMapping: [EnginePeer.Id: [PeerIndexNameRepresentation]]) -> Signal<[EnginePeer], NoError> { + return account.postbox.transaction { transaction -> [EnginePeer] in + return transaction.searchSubPeers(peerId: account.peerId, query: query, indexNameMapping: indexNameMapping).map(EnginePeer.init) + } +} diff --git a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift index 371dcbb67a..071119fa71 100644 --- a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift @@ -114,7 +114,21 @@ public extension Message { return nil } + var sourceAuthorInfo: SourceAuthorInfoMessageAttribute? { + for attribute in self.attributes { + if let attribute = attribute as? SourceAuthorInfoMessageAttribute { + return attribute + } + } + return nil + } + var effectiveAuthor: Peer? { + if let sourceAuthorInfo = self.sourceAuthorInfo { + if let sourceAuthorId = sourceAuthorInfo.originalAuthor, let peer = self.peers[sourceAuthorId] { + return peer + } + } if let forwardInfo = self.forwardInfo, let sourceReference = self.sourceReference, forwardInfo.author?.id == sourceReference.messageId.peerId { if let peer = self.peers[sourceReference.messageId.peerId] { return peer diff --git a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift index 9a07ce5ac0..5abb9df028 100644 --- a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift @@ -424,4 +424,13 @@ public extension PeerId { } return false } + + var isAnonymousSavedMessages: Bool { + if self.namespace == Namespaces.Peer.CloudUser { + if self.id._internalGetInt64Value() == 2666000 { + return true + } + } + return false + } } diff --git a/submodules/TelegramStringFormatting/Sources/PeerDisplayName.swift b/submodules/TelegramStringFormatting/Sources/PeerDisplayName.swift index a7603c3d65..a6dc72b985 100644 --- a/submodules/TelegramStringFormatting/Sources/PeerDisplayName.swift +++ b/submodules/TelegramStringFormatting/Sources/PeerDisplayName.swift @@ -25,6 +25,9 @@ public func stringForFullAuthorName(message: EngineMessage, strings: Presentatio } else { if message.id.peerId == accountPeerId { authorString = [strings.DialogList_SavedMessages] + } else if message.id.peerId.isAnonymousSavedMessages { + //TODO:localize + authorString = ["Author Hidden"] } else if message.flags.contains(.Incoming) { authorString = [peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder)] } else { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 135bb3d145..aae6ebc963 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -1331,6 +1331,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI break } } + let sourceAuthorInfo = item.content.firstMessage.sourceAuthorInfo var isCrosspostFromChannel = false if let _ = sourceReference { @@ -1357,10 +1358,17 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI displayAuthorInfo = false } else if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { if let forwardInfo = item.content.firstMessage.forwardInfo { - ignoreForward = true effectiveAuthor = forwardInfo.author - if effectiveAuthor == nil, let authorSignature = forwardInfo.authorSignature { - effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) + + if let sourceAuthorInfo, let originalAuthorId = sourceAuthorInfo.originalAuthor, let peer = item.message.peers[originalAuthorId] { + effectiveAuthor = peer + } else if let sourceAuthorInfo, let originalAuthorName = sourceAuthorInfo.originalAuthorName { + effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(originalAuthorName.persistentHashValue % 32))), accessHash: nil, firstName: originalAuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) + } else { + ignoreForward = true + if effectiveAuthor == nil, let authorSignature = forwardInfo.authorSignature { + effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) + } } } displayAuthorInfo = !mergedTop.merged && incoming && effectiveAuthor != nil diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/StringForMessageTimestampStatus.swift b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/StringForMessageTimestampStatus.swift index e1de4b5dc9..d400478e3c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/StringForMessageTimestampStatus.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/StringForMessageTimestampStatus.swift @@ -71,22 +71,28 @@ public func stringForMessageTimestampStatus(accountPeerId: PeerId, message: Mess } } - let timestamp: Int32 + var timestamp: Int32 if let scheduleTime = message.scheduleTime { timestamp = scheduleTime } else { timestamp = message.timestamp } - var dateText = stringForMessageTimestamp(timestamp: timestamp, dateTimeFormat: dateTimeFormat) - if timestamp == scheduleWhenOnlineTimestamp { - dateText = " " - } var displayFullDate = false if case .full = format, timestamp > 100000 { displayFullDate = true - } else if let _ = message.forwardInfo, message.id.peerId == accountPeerId { + } else if let forwardInfo = message.forwardInfo, message.id.peerId == accountPeerId { displayFullDate = true + timestamp = forwardInfo.date + } + + if let sourceAuthorInfo = message.sourceAuthorInfo, let orignalDate = sourceAuthorInfo.orignalDate { + timestamp = orignalDate + } + + var dateText = stringForMessageTimestamp(timestamp: timestamp, dateTimeFormat: dateTimeFormat) + if timestamp == scheduleWhenOnlineTimestamp { + dateText = " " } if displayFullDate { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift index e77784a7c6..f3e50cf870 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift @@ -56,12 +56,24 @@ private func messagesShouldBeMerged(accountPeerId: PeerId, _ lhs: Message, _ rhs break } } + let lhsSourceAuthorInfo = lhs.sourceAuthorInfo + if let sourceAuthorInfo = lhsSourceAuthorInfo { + if let originalAuthor = sourceAuthorInfo.originalAuthor { + lhsEffectiveAuthor = lhs.peers[originalAuthor] + } + } for attribute in rhs.attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { rhsEffectiveAuthor = rhs.peers[attribute.messageId.peerId] break } } + let rhsSourceAuthorInfo = rhs.sourceAuthorInfo + if let sourceAuthorInfo = rhsSourceAuthorInfo { + if let originalAuthor = sourceAuthorInfo.originalAuthor { + rhsEffectiveAuthor = rhs.peers[originalAuthor] + } + } var sameThread = true if let lhsPeer = lhs.peers[lhs.id.peerId], let rhsPeer = rhs.peers[rhs.id.peerId], arePeersEqual(lhsPeer, rhsPeer), let channel = lhsPeer as? TelegramChannel, channel.flags.contains(.isForum), lhs.threadId != rhs.threadId { @@ -73,6 +85,16 @@ private func messagesShouldBeMerged(accountPeerId: PeerId, _ lhs: Message, _ rhs sameAuthor = true } + if let lhsSourceAuthorInfo, let rhsSourceAuthorInfo { + if lhsSourceAuthorInfo.originalAuthor != rhsSourceAuthorInfo.originalAuthor { + sameAuthor = false + } else if lhsSourceAuthorInfo.originalAuthorName != rhsSourceAuthorInfo.originalAuthorName { + sameAuthor = false + } + } else if (lhsSourceAuthorInfo == nil) != (rhsSourceAuthorInfo == nil) { + sameAuthor = false + } + var lhsEffectiveTimestamp = lhs.timestamp var rhsEffectiveTimestamp = rhs.timestamp @@ -257,6 +279,13 @@ public final class ChatMessageItemImpl: ChatMessageItem, CustomStringConvertible effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) } } + if let sourceAuthorInfo = content.firstMessage.sourceAuthorInfo { + if let originalAuthor = sourceAuthorInfo.originalAuthor, let peer = content.firstMessage.peers[originalAuthor] { + effectiveAuthor = peer + } else if let authorSignature = sourceAuthorInfo.originalAuthorName { + effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) + } + } displayAuthorInfo = incoming && effectiveAuthor != nil } else { effectiveAuthor = content.firstMessage.author diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index 22d2cd0136..5209f3fdf0 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -2429,7 +2429,7 @@ public final class EntityInputView: UIInputView, AttachmentTextInputPanelInputVi fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, - mode: .standard(previewing: false), + mode: .standard(.default), chatLocation: .peer(id: self.context.account.peerId), subject: nil, peerNearbyData: nil, diff --git a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift index d2f3799ef6..3e9ceb5090 100644 --- a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift @@ -247,6 +247,9 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { segments = [.text(0, NSAttributedString(string: customTitle, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))] } else if peerView.peerId == self.context.account.peerId { segments = [.text(0, NSAttributedString(string: self.strings.Conversation_SavedMessages, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))] + } else if peerView.peerId.isAnonymousSavedMessages { + //TODO:localize + segments = [.text(0, NSAttributedString(string: "Author Hidden", font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))] } else { if !peerView.isContact, let user = peer as? TelegramUser, !user.flags.contains(.isSupport), user.botInfo == nil, let phone = user.phone, !phone.isEmpty { segments = [.text(0, NSAttributedString(string: formatPhoneNumber(context: self.context, number: phone), font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))] diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index c39dc2da94..68b8d42edf 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -1015,7 +1015,7 @@ final class MediaEditorScreenComponent: Component { fontSize: presentationData.chatFontSize, bubbleCorners: presentationData.chatBubbleCorners, accountPeerId: component.context.account.peerId, - mode: .standard(previewing: false), + mode: .standard(.default), chatLocation: .peer(id: component.context.account.peerId), subject: nil, peerNearbyData: nil, diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift index 8b3ae7e569..4b7ba3d59a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift @@ -52,7 +52,9 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, UI public init(context: AccountContext, navigationController: @escaping () -> NavigationController?) { self.context = context self.navigationController = navigationController - self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + self.presentationData = presentationData + let strings = presentationData.strings self.chatListNode = ChatListNode( context: self.context, @@ -90,6 +92,15 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, UI self.ready.set(self.chatListNode.ready) + self.statusPromise.set(self.context.engine.messages.savedMessagesPeersStats() + |> map { count in + if let count { + return PeerInfoStatusData(text: strings.Notifications_Exceptions(Int32(count)), isActivity: false, key: .savedMessagesChats) + } else { + return PeerInfoStatusData(text: strings.Channel_NotificationLoading.lowercased(), isActivity: false, key: .savedMessagesChats) + } + }) + self.chatListNode.peerSelected = { [weak self] peer, _, _, _, _ in guard let self, let navigationController = self.navigationController() else { return @@ -229,7 +240,7 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, UI public func updateSelectedMessages(animated: Bool) { } - public func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + public func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { self.currentParams = (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) transition.updateFrame(node: self.chatListNode, frame: CGRect(origin: CGPoint(), size: size)) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/BUILD new file mode 100644 index 0000000000..6ff7241b51 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/BUILD @@ -0,0 +1,27 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "PeerInfoChatPaneNode", + module_name = "PeerInfoChatPaneNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/AsyncDisplayKit", + "//submodules/TelegramCore", + "//submodules/Postbox", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/TelegramStringFormatting", + "//submodules/ComponentFlow", + "//submodules/AppBundle", + "//submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift new file mode 100644 index 0000000000..2f17905d31 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift @@ -0,0 +1,156 @@ +import AsyncDisplayKit +import Display +import TelegramCore +import SwiftSignalKit +import Postbox +import TelegramPresentationData +import AccountContext +import TelegramStringFormatting +import ComponentFlow +import TelegramUIPreferences +import AppBundle +import PeerInfoPaneNode + +public final class PeerInfoChatPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate, UIGestureRecognizerDelegate { + private let context: AccountContext + private let peerId: EnginePeer.Id + private let navigationController: () -> NavigationController? + + private let chatController: ChatController + + public weak var parentController: ViewController? { + didSet { + if self.parentController !== oldValue { + if let parentController = self.parentController { + self.chatController.willMove(toParent: parentController) + parentController.addChild(self.chatController) + self.chatController.didMove(toParent: parentController) + } else { + self.chatController.willMove(toParent: nil) + self.chatController.removeFromParent() + self.chatController.didMove(toParent: nil) + } + } + } + } + + private var currentParams: (size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData)? + + private let ready = Promise() + private var didSetReady: Bool = false + public var isReady: Signal { + return self.ready.get() + } + + private let statusPromise = Promise(nil) + public var status: Signal { + self.statusPromise.get() + } + + public var tabBarOffsetUpdated: ((ContainedViewLayoutTransition) -> Void)? + public var tabBarOffset: CGFloat { + return 0.0 + } + + private var presentationData: PresentationData + private var presentationDataDisposable: Disposable? + + public init(context: AccountContext, peerId: EnginePeer.Id, navigationController: @escaping () -> NavigationController?) { + self.context = context + self.peerId = peerId + self.navigationController = navigationController + self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + + self.chatController = context.sharedContext.makeChatController(context: context, chatLocation: .replyThread(message: ChatReplyThreadMessage(peerId: context.account.peerId, threadId: peerId.toInt64(), channelMessageId: nil, isChannelPost: false, isForumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)), subject: nil, botStart: nil, mode: .standard(.embedded)) + + super.init() + + self.presentationDataDisposable = (self.context.sharedContext.presentationData + |> deliverOnMainQueue).start(next: { [weak self] presentationData in + guard let self else { + return + } + self.presentationData = presentationData + }) + + self.ready.set(self.chatController.ready.get()) + + self.addSubnode(self.chatController.displayNode) + self.chatController.displayNode.clipsToBounds = true + } + + deinit { + self.presentationDataDisposable?.dispose() + } + + public func ensureMessageIsVisible(id: MessageId) { + } + + public func scrollToTop() -> Bool { + return false + } + + public func hitTestResultForScrolling() -> UIView? { + return nil + } + + public func brieflyDisableTouchActions() { + } + + public func findLoadedMessage(id: MessageId) -> Message? { + return nil + } + + public func updateHiddenMedia() { + } + + public func transferVelocity(_ velocity: CGFloat) { + } + + public func cancelPreviewGestures() { + } + + public func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? { + return nil + } + + public func addToTransitionSurface(view: UIView) { + } + + override public func didLoad() { + super.didLoad() + } + + + override public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } + + public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + if gestureRecognizer.state != .failed, let otherGestureRecognizer = otherGestureRecognizer as? UIPanGestureRecognizer { + let _ = otherGestureRecognizer + return true + } else { + return false + } + } + + public func updateSelectedMessages(animated: Bool) { + } + + public func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + self.currentParams = (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) + let chatFrame = CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: size.width, height: size.height - topInset)) + + let combinedBottomInset = max(0.0, size.height - visibleHeight) + bottomInset + transition.updateFrame(node: self.chatController.displayNode, frame: chatFrame) + self.chatController.containerLayoutUpdated(ContainerViewLayout(size: chatFrame.size, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact, orientation: nil), deviceMetrics: deviceMetrics, intrinsicInsets: UIEdgeInsets(top: 0.0, left: sideInset, bottom: combinedBottomInset, right: sideInset), safeInsets: UIEdgeInsets(top: 0.0, left: sideInset, bottom: combinedBottomInset, right: sideInset), additionalInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: transition) + } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + guard let result = super.hitTest(point, with: event) else { + return nil + } + return result + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift index 406286f904..8e79a99875 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift @@ -17,6 +17,7 @@ public enum PeerInfoPaneKey: Int32 { case groupsInCommon case recommended case savedMessagesChats + case savedMessages } public struct PeerInfoStatusData: Equatable { @@ -44,7 +45,7 @@ public protocol PeerInfoPaneNode: ASDisplayNode { var tabBarOffsetUpdated: ((ContainedViewLayoutTransition) -> Void)? { get set } var tabBarOffset: CGFloat { get } - func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) + func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) func scrollToTop() -> Bool func transferVelocity(_ velocity: CGFloat) func cancelPreviewGestures() diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD index 1c31e02af7..f78e733852 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD @@ -133,6 +133,7 @@ swift_library( "//submodules/SolidRoundedButtonNode", "//submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode", "//submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode", + "//submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode", "//submodules/MediaPickerUI", "//submodules/AttachmentUI", "//submodules/TelegramUI/Components/Settings/BoostLevelIconComponent", diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGifPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGifPaneNode.swift index 9788c29606..340d6800ac 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGifPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGifPaneNode.swift @@ -786,7 +786,7 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDe return self._itemInteraction! } - private var currentParams: (size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData)? + private var currentParams: (size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData)? private let ready = Promise() private var didSetReady: Bool = false @@ -959,8 +959,8 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDe let wasFirstHistoryView = self.isFirstHistoryView self.isFirstHistoryView = false - if let (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = self.currentParams { - self.update(size: size, topInset: topInset, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: wasFirstHistoryView, transition: .immediate) + if let (size, topInset, sideInset, bottomInset, deviceMetrics, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = self.currentParams { + self.update(size: size, topInset: topInset, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: wasFirstHistoryView, transition: .immediate) if !self.didSetReady { self.didSetReady = true self.ready.set(.single(true)) @@ -1066,9 +1066,9 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDe } } - func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { let previousParams = self.currentParams - self.currentParams = (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) + self.currentParams = (size, topInset, sideInset, bottomInset, deviceMetrics, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: size.width, height: size.height - topInset))) @@ -1110,7 +1110,7 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDe private var previousDidScrollTimestamp: Double = 0.0 func scrollViewDidScroll(_ scrollView: UIScrollView) { - if let (size, _, sideInset, bottomInset, visibleHeight, _, _, presentationData) = self.currentParams { + if let (size, _, sideInset, bottomInset, _, visibleHeight, _, _, presentationData) = self.currentParams { self.updateVisibleItems(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, theme: presentationData.theme, strings: presentationData.strings, synchronousLoad: false) if scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.bounds.height * 2.0, let currentView = self.currentView, currentView.earlierId != nil { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift index 7ce1f98397..1ffa847f01 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift @@ -155,7 +155,7 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode { } } - func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { let isFirstLayout = self.currentParams == nil self.currentParams = (size, isScrollingLockedAtTop, presentationData) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoListPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoListPaneNode.swift index 7aa0d6f3e6..0cddeb8276 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoListPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoListPaneNode.swift @@ -31,7 +31,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { private let listNode: ChatHistoryListNode - private var currentParams: (size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData)? + private var currentParams: (size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData)? private let ready = Promise() private var didSetReady: Bool = false @@ -146,8 +146,8 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { strongSelf.playlistLocation = nil } - if let (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = strongSelf.currentParams { - strongSelf.update(size: size, topInset: topInset, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: true, transition: .animated(duration: 0.4, curve: .spring)) + if let (size, topInset, sideInset, bottomInset, deviceMetrics, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = strongSelf.currentParams { + strongSelf.update(size: size, topInset: topInset, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: true, transition: .animated(duration: 0.4, curve: .spring)) } } }) @@ -200,8 +200,8 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { } } - func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { - self.currentParams = (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) + func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + self.currentParams = (size, topInset, sideInset, bottomInset, deviceMetrics, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) var topPanelHeight: CGFloat = 0.0 if let (item, previousItem, nextItem, order, type, _) = self.playlistStateAndType { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift index 28750cf74c..712656f0e0 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift @@ -238,7 +238,7 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode { } } - func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { let isFirstLayout = self.currentParams == nil self.currentParams = (size, isScrollingLockedAtTop) self.presentationDataPromise.set(.single(presentationData)) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedChannelsPane.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedChannelsPane.swift index 3720a1935c..089c3bea7f 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedChannelsPane.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedChannelsPane.swift @@ -195,7 +195,7 @@ final class PeerInfoRecommendedChannelsPaneNode: ASDisplayNode, PeerInfoPaneNode } } - func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { let isFirstLayout = self.currentParams == nil self.currentParams = (size, sideInset, bottomInset, isScrollingLockedAtTop, presentationData) self.presentationDataPromise.set(.single(presentationData)) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index 54efd21ca2..cab6ac3ea0 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -806,6 +806,21 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen savedMessagesPeer = .single(nil) } + let hasSavedMessages: Signal + if case .peer = chatLocation { + hasSavedMessages = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: context.account.peerId, threadId: peerId.toInt64(), tag: MessageTags())) + |> map { count -> Bool in + if let count, count != 0 { + return true + } else { + return false + } + } + |> distinctUntilChanged + } else { + hasSavedMessages = .single(false) + } + return combineLatest( context.account.viewTracker.peerView(peerId, updateData: true), peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder), @@ -814,9 +829,10 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen status, hasStories, accountIsPremium, - savedMessagesPeer + savedMessagesPeer, + hasSavedMessages ) - |> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories, accountIsPremium, savedMessagesPeer -> PeerInfoScreenData in + |> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories, accountIsPremium, savedMessagesPeer, hasSavedMessages -> PeerInfoScreenData in var availablePanes = availablePanes if let hasStories { @@ -830,8 +846,19 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen } } - if peerId == context.account.peerId, case .peer = chatLocation { - availablePanes?.insert(.savedMessagesChats, at: 0) + if case .peer = chatLocation { + if peerId == context.account.peerId { + availablePanes?.insert(.savedMessagesChats, at: 0) + } else if hasSavedMessages { + if var availablePanesValue = availablePanes { + if let index = availablePanesValue.firstIndex(of: .media) { + availablePanesValue.insert(.savedMessages, at: index + 1) + } else { + availablePanesValue.insert(.savedMessages, at: 0) + } + availablePanes = availablePanesValue + } + } } } else { availablePanes = nil @@ -923,6 +950,17 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen if let recommendedChannels, !recommendedChannels.channels.isEmpty { availablePanes?.append(.recommended) } + + if case .peer = chatLocation { + if var availablePanesValue = availablePanes { + if let index = availablePanesValue.firstIndex(of: .media) { + availablePanesValue.insert(.savedMessages, at: index + 1) + } else { + availablePanesValue.insert(.savedMessages, at: 0) + } + availablePanes = availablePanesValue + } + } } else { availablePanes = nil } @@ -1129,6 +1167,17 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen } } + if case .peer = chatLocation { + if var availablePanesValue = availablePanes { + if let index = availablePanesValue.firstIndex(of: .media) { + availablePanesValue.insert(.savedMessages, at: index + 1) + } else { + availablePanesValue.insert(.savedMessages, at: 0) + } + availablePanes = availablePanesValue + } + } + var canManageInvitations = false if let group = peerViewMainPeer(peerView) as? TelegramGroup { let previousValue = wasUpgradedGroup.swap(group.migrationReference != nil) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index f9372267e0..5adcdd9802 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -968,6 +968,9 @@ final class PeerInfoHeaderNode: ASDisplayNode { var title: String if peer.id == self.context.account.peerId && !self.isSettings { title = presentationData.strings.Conversation_SavedMessages + } else if peer.id.isAnonymousSavedMessages { + //TODO:localize + title = "Author Hidden" } else if let threadData = threadData { title = threadData.info.title } else { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift index 198fc3cfba..81271aef90 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift @@ -12,26 +12,27 @@ import ChatControllerInteraction import PeerInfoVisualMediaPaneNode import PeerInfoPaneNode import PeerInfoChatListPaneNode +import PeerInfoChatPaneNode final class PeerInfoPaneWrapper { let key: PeerInfoPaneKey let node: PeerInfoPaneNode var isAnimatingOut: Bool = false - private var appliedParams: (CGSize, CGFloat, CGFloat, CGFloat, CGFloat, Bool, CGFloat, PresentationData)? + private var appliedParams: (CGSize, CGFloat, CGFloat, CGFloat, DeviceMetrics, CGFloat, Bool, CGFloat, PresentationData)? init(key: PeerInfoPaneKey, node: PeerInfoPaneNode) { self.key = key self.node = node } - func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { - if let (currentSize, currentTopInset, currentSideInset, currentBottomInset, _, currentIsScrollingLockedAtTop, currentExpandProgress, currentPresentationData) = self.appliedParams { - if currentSize == size && currentTopInset == topInset, currentSideInset == sideInset && currentBottomInset == bottomInset, currentIsScrollingLockedAtTop == isScrollingLockedAtTop && currentExpandProgress == expandProgress && currentPresentationData === presentationData { + func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + if let (currentSize, currentTopInset, currentSideInset, currentBottomInset, _, currentVisibleHeight, currentIsScrollingLockedAtTop, currentExpandProgress, currentPresentationData) = self.appliedParams { + if currentSize == size && currentTopInset == topInset, currentSideInset == sideInset && currentBottomInset == bottomInset && currentVisibleHeight == visibleHeight && currentIsScrollingLockedAtTop == isScrollingLockedAtTop && currentExpandProgress == expandProgress && currentPresentationData === presentationData { return } } - self.appliedParams = (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) - self.node.update(size: size, topInset: topInset, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: synchronous, transition: transition) + self.appliedParams = (size, topInset, sideInset, bottomInset, deviceMetrics, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) + self.node.update(size: size, topInset: topInset, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: synchronous, transition: transition) } } @@ -422,6 +423,8 @@ private final class PeerInfoPendingPane { paneNode = PeerInfoRecommendedChannelsPaneNode(context: context, peerId: peerId, chatControllerInteraction: chatControllerInteraction, openPeerContextAction: openPeerContextAction) case .savedMessagesChats: paneNode = PeerInfoChatListPaneNode(context: context, navigationController: chatControllerInteraction.navigationController) + case .savedMessages: + paneNode = PeerInfoChatPaneNode(context: context, peerId: peerId, navigationController: chatControllerInteraction.navigationController) } paneNode.parentController = parentController self.pane = PeerInfoPaneWrapper(key: key, node: paneNode) @@ -456,7 +459,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat let isReady = Promise() var didSetIsReady = false - private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?)? + private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?)? private(set) var currentPaneKey: PeerInfoPaneKey? var pendingSwitchToPaneKey: PeerInfoPaneKey? @@ -548,8 +551,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat if strongSelf.currentPanes[key] != nil { strongSelf.currentPaneKey = key - if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams { - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.4, curve: .spring)) + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.4, curve: .spring)) strongSelf.currentPaneUpdated?(true) @@ -561,8 +564,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat strongSelf.pendingSwitchToPaneKey = key strongSelf.expandOnSwitch = true - if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams { - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.4, curve: .spring)) + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.4, curve: .spring)) } } } @@ -621,7 +624,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat cancelContextGestures(view: self.view) case .changed: - if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { let translation = recognizer.translation(in: self.view) var transitionFraction = translation.x / size.width if currentIndex <= 0 { @@ -636,11 +639,11 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat // print(transitionFraction) self.paneTransitionPromise.set(transitionFraction) - self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .immediate) + self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .immediate) self.currentPaneUpdated?(false) } case .cancelled, .ended: - if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { let translation = recognizer.translation(in: self.view) let velocity = recognizer.velocity(in: self.view) var directionIsToRight: Bool? @@ -664,7 +667,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat } } self.transitionFraction = 0.0 - self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.35, curve: .spring)) + self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.35, curve: .spring)) self.currentPaneUpdated?(false) self.currentPaneStatusPromise.set(self.currentPane?.node.status ?? .single(nil)) @@ -703,7 +706,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat } } - func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?, transition: ContainedViewLayoutTransition) { + func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?, transition: ContainedViewLayoutTransition) { let previousAvailablePanes = self.currentAvailablePanes let availablePanes = data?.availablePanes ?? [] self.currentAvailablePanes = data?.availablePanes @@ -747,7 +750,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat currentIndex = nil } - self.currentParams = (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) + self.currentParams = (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) transition.updateAlpha(node: self.coveringBackgroundNode, alpha: expansionFraction) @@ -817,12 +820,12 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat guard let strongSelf = self else { return } - if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams { + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams { var transition: ContainedViewLayoutTransition = .immediate if strongSelf.pendingSwitchToPaneKey == key && strongSelf.currentPaneKey != nil { transition = .animated(duration: 0.4, curve: .spring) } - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: transition) + strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: transition) } } if leftScope { @@ -845,14 +848,14 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat ) self.pendingPanes[key] = pane pane.pane.node.frame = paneFrame - pane.pane.update(size: paneFrame.size, topInset: tabsHeight, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: true, transition: .immediate) + pane.pane.update(size: paneFrame.size, topInset: tabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: true, transition: .immediate) let paneNode = pane.pane.node pane.pane.node.tabBarOffsetUpdated = { [weak self, weak paneNode] transition in guard let strongSelf = self, let paneNode = paneNode, let currentPane = strongSelf.currentPane, paneNode === currentPane.node else { return } - if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams { - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: transition) + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: transition) } } leftScope = true @@ -861,7 +864,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat for (key, pane) in self.pendingPanes { pane.pane.node.frame = paneFrame - pane.pane.update(size: paneFrame.size, topInset: tabsHeight, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: self.currentPaneKey == nil, transition: .immediate) + pane.pane.update(size: paneFrame.size, topInset: tabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: self.currentPaneKey == nil, transition: .immediate) if pane.isReady { self.pendingPanes.removeValue(forKey: key) @@ -922,7 +925,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat return } pane.isAnimatingOut = false - if let (_, _, _, _, _, _, data) = strongSelf.currentParams { + if let (_, _, _, _, _, _, _, data) = strongSelf.currentParams { if let availablePanes = data?.availablePanes, let currentPaneKey = strongSelf.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey), let paneIndex = availablePanes.firstIndex(of: key), abs(paneIndex - currentIndex) <= 1 { } else { if let pane = strongSelf.currentPanes.removeValue(forKey: key) { @@ -953,7 +956,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat paneCompletion() }) } - pane.update(size: paneFrame.size, topInset: tabsHeight, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition) + pane.update(size: paneFrame.size, topInset: tabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition) } } @@ -1002,6 +1005,9 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat title = presentationData.strings.PeerInfo_PaneRecommended case .savedMessagesChats: title = presentationData.strings.DialogList_TabTitle + case .savedMessages: + //TODO:localize + title = "Saved" } return PeerInfoPaneSpecifier(key: key, title: title) }, selectedPane: self.currentPaneKey, transitionFraction: self.transitionFraction, transition: transition) @@ -1009,7 +1015,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat for (_, pane) in self.pendingPanes { let paneTransition: ContainedViewLayoutTransition = .immediate paneTransition.updateFrame(node: pane.pane.node, frame: paneFrame) - pane.pane.update(size: paneFrame.size, topInset: tabsHeight, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: true, transition: paneTransition) + pane.pane.update(size: paneFrame.size, topInset: tabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: true, transition: paneTransition) } var removeKeys: [PeerInfoPaneKey] = [] diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 3e36375104..9f01c2a7ce 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -429,7 +429,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { self.backgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) self.separatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor - let interfaceState = ChatPresentationInterfaceState(chatWallpaper: .color(0), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: .regular, bubbleCorners: PresentationChatBubbleCorners(mainRadius: 16.0, auxiliaryRadius: 8.0, mergeBubbleCorners: true), accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(id: self.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil) + let interfaceState = ChatPresentationInterfaceState(chatWallpaper: .color(0), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: .regular, bubbleCorners: PresentationChatBubbleCorners(mainRadius: 16.0, auxiliaryRadius: 8.0, mergeBubbleCorners: true), accountPeerId: self.context.account.peerId, mode: .standard(.default), chatLocation: .peer(id: self.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil) let panelHeight = self.selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: UIEdgeInsets(), maxHeight: 0.0, isSecondary: false, transition: transition, interfaceState: interfaceState, metrics: layout.metrics, isMediaInputExpanded: false) transition.updateFrame(node: self.selectionPanel, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: panelHeight))) @@ -3060,7 +3060,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return } let presentationData = strongSelf.presentationData - let chatController = strongSelf.context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) let items: [ContextMenuItem] if recommended { @@ -5484,7 +5484,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } if !foundController { - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: strongSelf.peerId), subject: nil, botStart: nil, mode: .standard(previewing: false)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: strongSelf.peerId), subject: nil, botStart: nil, mode: .standard(.default)) chatController.hintPlayNextOutgoingGift() controllers.append(chatController) } @@ -8709,7 +8709,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } self.supportPeerDisposable.set((supportPeer.get() |> take(1) |> deliverOnMainQueue).startStrict(next: { [weak self] peerId in if let strongSelf = self, let peerId = peerId { - push(strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) + push(strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(.default))) } })) })]), in: .window(.root)) @@ -9171,7 +9171,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro proceed(chatController) }) } else { - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .none, botStart: nil, mode: .standard(previewing: false)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .none, botStart: nil, mode: .standard(.default)) proceed(chatController) } } @@ -9230,6 +9230,84 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }), cancel: { [weak self] in self?.deactivateSearch() }) + } else if let currentPaneKey = self.paneContainerNode.currentPaneKey, case .savedMessagesChats = currentPaneKey { + let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer, filter: [.removeSearchHeader], requestPeerType: nil, location: .savedMessagesChats, displaySearchFilters: false, hasDownloads: false, initialFilter: .chats, openPeer: { [weak self] peer, _, _, _ in + guard let self else { + return + } + guard let navigationController = self.controller?.navigationController as? NavigationController else { + return + } + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams( + navigationController: navigationController, + context: self.context, + chatLocation: .replyThread(ChatReplyThreadMessage( + peerId: self.context.account.peerId, + threadId: peer.id.toInt64(), + channelMessageId: nil, + isChannelPost: false, + isForumPost: false, + maxMessage: nil, + maxReadIncomingMessageId: nil, + maxReadOutgoingMessageId: nil, + unreadCount: 0, + initialFilledHoles: IndexSet(), + initialAnchor: .automatic, + isNotAvailable: false + )), + subject: nil, + keepStack: .always + )) + }, openDisabledPeer: { _, _ in + }, openRecentPeerOptions: { _ in + }, openMessage: { [weak self] peer, threadId, messageId, deactivateOnAction in + guard let self else { + return + } + guard let navigationController = self.controller?.navigationController as? NavigationController else { + return + } + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams( + navigationController: navigationController, + context: self.context, + chatLocation: .replyThread(ChatReplyThreadMessage( + peerId: self.context.account.peerId, + threadId: peer.id.toInt64(), + channelMessageId: nil, + isChannelPost: false, + isForumPost: false, + maxMessage: nil, + maxReadIncomingMessageId: nil, + maxReadOutgoingMessageId: nil, + unreadCount: 0, + initialFilledHoles: IndexSet(), + initialAnchor: .automatic, + isNotAvailable: false + )), + subject: nil, + keepStack: .always + )) + }, addContact: { _ in + }, peerContextAction: nil, present: { [weak self] c, a in + guard let self else { + return + } + self.controller?.present(c, in: .window(.root), with: a) + }, presentInGlobalOverlay: { [weak self] c, a in + guard let self else { + return + } + self.controller?.presentInGlobalOverlay(c, with: a) + }, navigationController: self.controller?.navigationController as? NavigationController, parentController: { [weak self] in + guard let self else { + return nil + } + return self.controller + }) + + self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, placeholder: self.presentationData.strings.Common_Search, hasBackground: true, hasSeparator: true, contentNode: contentNode, cancel: { [weak self] in + self?.deactivateSearch() + }) } else { var tagMask: MessageTags = .file if let currentPaneKey = self.paneContainerNode.currentPaneKey { @@ -9549,7 +9627,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro )) }))) - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: strongSelf.peerId), subject: .message(id: .id(index.id), highlight: nil, timecode: nil), botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: strongSelf.peerId), subject: .message(id: .id(index.id), highlight: nil, timecode: nil), botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) strongSelf.controller?.presentInGlobalOverlay(contextController) @@ -10063,7 +10141,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } let navigationBarHeight: CGFloat = !self.isSettings && layout.isModalOverlay ? 56.0 : 44.0 - self.paneContainerNode.update(size: self.paneContainerNode.bounds.size, sideInset: layout.safeInsets.left, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: effectiveAreaExpansionFraction, presentationData: self.presentationData, data: self.data, transition: transition) + self.paneContainerNode.update(size: self.paneContainerNode.bounds.size, sideInset: layout.safeInsets.left, bottomInset: bottomInset, deviceMetrics: layout.deviceMetrics, visibleHeight: visibleHeight, expansionFraction: effectiveAreaExpansionFraction, presentationData: self.presentationData, data: self.data, transition: transition) transition.updateFrame(node: self.headerNode.navigationButtonContainer, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left, y: layout.statusBarHeight ?? 0.0), size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: navigationBarHeight))) @@ -10088,7 +10166,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if self.state.selectedMessageIds == nil { if let currentPaneKey = self.paneContainerNode.currentPaneKey { switch currentPaneKey { - case .files, .music, .links, .members: + case .files, .music, .links, .members, .savedMessagesChats: rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true)) case .media: rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .more, isForExpandedView: true)) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift index b1b19c9605..cb020bae7e 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift @@ -482,6 +482,7 @@ final class PeerInfoStoryGridScreenComponent: Component { topInset: environment.navigationHeight, sideInset: environment.safeInsets.left, bottomInset: bottomInset, + deviceMetrics: environment.deviceMetrics, visibleHeight: availableSize.height, isScrollingLockedAtTop: false, expandProgress: 1.0, diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift index 79821d3274..7491954a5d 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift @@ -950,7 +950,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr public private(set) var isSelectionModeActive: Bool - private var currentParams: (size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData)? + private var currentParams: (size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData)? private let ready = Promise() private var didSetReady: Bool = false @@ -1730,12 +1730,12 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr private func updateHistory(items: SparseItemGrid.Items, synchronous: Bool, reloadAtTop: Bool) { self.items = items - if let (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = self.currentParams { + if let (size, topInset, sideInset, bottomInset, deviceMetrics, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = self.currentParams { var gridSnapshot: UIView? if reloadAtTop { gridSnapshot = self.itemGrid.view.snapshotView(afterScreenUpdates: false) } - self.update(size: size, topInset: topInset, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: false, transition: .immediate) + self.update(size: size, topInset: topInset, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: false, transition: .immediate) self.updateSelectedItems(animated: false) if let gridSnapshot = gridSnapshot { self.view.addSubview(gridSnapshot) @@ -2006,8 +2006,8 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr } } - public func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { - self.currentParams = (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) + public func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + self.currentParams = (size, topInset, sideInset, bottomInset, deviceMetrics, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) transition.updateFrame(node: self.contextGestureContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift index b068285ea5..eb6d94202c 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift @@ -1101,7 +1101,7 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, return self._itemInteraction! } - private var currentParams: (size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData)? + private var currentParams: (size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData)? private let ready = Promise() private var didSetReady: Bool = false @@ -1605,7 +1605,7 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, self.presentationDataDisposable = (self.context.sharedContext.presentationData |> deliverOnMainQueue).start(next: { [weak self] presentationData in - guard let strongSelf = self, let (size, topInset, sideInset, bottomInset, _, _, _, _) = strongSelf.currentParams else { + guard let strongSelf = self, let (size, topInset, sideInset, bottomInset, _, _, _, _, _) = strongSelf.currentParams else { return } strongSelf.itemGridBinding.updatePresentationData(presentationData: presentationData) @@ -1742,12 +1742,12 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, private func updateHistory(items: SparseItemGrid.Items, synchronous: Bool, reloadAtTop: Bool) { self.items = items - if let (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = self.currentParams { + if let (size, topInset, sideInset, bottomInset, deviceMetrics, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = self.currentParams { var gridSnapshot: UIView? if reloadAtTop { gridSnapshot = self.itemGrid.view.snapshotView(afterScreenUpdates: false) } - self.update(size: size, topInset: topInset, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: false, transition: .immediate) + self.update(size: size, topInset: topInset, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: false, transition: .immediate) if let gridSnapshot = gridSnapshot { self.view.addSubview(gridSnapshot) gridSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak gridSnapshot] _ in @@ -2036,7 +2036,7 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, switch self.contentType { case .files, .music, .voiceAndVideoMessages: self.itemGrid.forEachVisibleItem { item in - guard let itemView = item.view as? ItemView, let (size, topInset, sideInset, bottomInset, _, _, _, _) = self.currentParams else { + guard let itemView = item.view as? ItemView, let (size, topInset, sideInset, bottomInset, _, _, _, _, _) = self.currentParams else { return } if let item = itemView.item { @@ -2093,8 +2093,8 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, } } - public func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { - self.currentParams = (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) + public func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + self.currentParams = (size, topInset, sideInset, bottomInset, deviceMetrics, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) transition.updateFrame(node: self.contextGestureContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift index 5bc0c12a51..079cf25593 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift @@ -124,7 +124,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { self.animationCache = context.animationCache self.animationRenderer = context.animationRenderer - self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(id: PeerId(0)), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(.default), chatLocation: .peer(id: PeerId(0)), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil) self.presentationInterfaceState = self.presentationInterfaceState.updatedInterfaceState { $0.withUpdatedForwardMessageIds(forwardedMessageIds) } self.presentationInterfaceStatePromise.set(self.presentationInterfaceState) @@ -371,7 +371,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { chatLocation: .peer(id: strongSelf.context.account.peerId), subject: .messageOptions(peerIds: peerIds, ids: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], info: .forward(ChatControllerSubject.MessageOptionsInfo.Forward(options: forwardOptions))), botStart: nil, - mode: .standard(previewing: true) + mode: .standard(.previewing) ) chatController.canReadHistory.set(false) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index cc37c3835e..f9af36cf21 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -259,7 +259,7 @@ final class StoryItemSetContainerSendMessage { fontSize: presentationData.chatFontSize, bubbleCorners: presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, - mode: .standard(previewing: false), + mode: .standard(.default), chatLocation: .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift index d250a02135..739d3823d4 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift @@ -135,6 +135,8 @@ extension ChatControllerImpl { navigateToLocation = .peer(peer) } self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: navigateToLocation, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil), keepStack: .always)) + + completion?() }) } 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)) { let _ = (self.context.engine.data.get( @@ -158,6 +160,8 @@ extension ChatControllerImpl { self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: chatLocation, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: quote), timecode: nil), keepStack: .always)) } + + completion?() }) } else if forceInCurrentChat { if let _ = fromId, let fromIndex = fromIndex, rememberInStack { @@ -308,17 +312,19 @@ extension ChatControllerImpl { |> deliverOnMainQueue).startStrict(next: { [weak self] index in if let strongSelf = self, let index = index.0 { strongSelf.chatDisplayNode.historyNode.scrollToMessage(from: scrollFromIndex, to: index, animated: animated, quote: quote, scrollPosition: scrollPosition) - completion?() } else if index.1 { if !progressStarted { progressStarted = true progressDisposable.set(progressSignal.start()) } + } else if let strongSelf = self { + strongSelf.controllerInteraction?.displayUndo(.info(title: nil, text: strongSelf.presentationData.strings.Conversation_MessageDoesntExist, timeout: nil, customUndoText: nil)) } }, completed: { [weak self] in if let strongSelf = self { strongSelf.loadingMessage.set(.single(nil)) } + completion?() })) cancelImpl = { [weak self] in if let strongSelf = self { diff --git a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift index c82be4bbb9..1db5fab7f0 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift @@ -100,7 +100,7 @@ private func chatForwardOptions(selfController: ChatControllerImpl, sourceNode: } |> distinctUntilChanged - let chatController = selfController.context.sharedContext.makeChatController(context: selfController.context, chatLocation: .peer(id: peerId), subject: .messageOptions(peerIds: [peerId], ids: selfController.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], info: .forward(ChatControllerSubject.MessageOptionsInfo.Forward(options: forwardOptions))), botStart: nil, mode: .standard(previewing: true)) + let chatController = selfController.context.sharedContext.makeChatController(context: selfController.context, chatLocation: .peer(id: peerId), subject: .messageOptions(peerIds: [peerId], ids: selfController.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], info: .forward(ChatControllerSubject.MessageOptionsInfo.Forward(options: forwardOptions))), botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) let messageIds = selfController.presentationInterfaceState.interfaceState.forwardMessageIds ?? [] @@ -508,7 +508,7 @@ private func chatReplyOptions(selfController: ChatControllerImpl, sourceNode: AS } |> distinctUntilChanged) - guard let chatController = selfController.context.sharedContext.makeChatController(context: selfController.context, chatLocation: .peer(id: peerId), subject: .messageOptions(peerIds: [replySubject.messageId.peerId], ids: [replySubject.messageId], info: .reply(ChatControllerSubject.MessageOptionsInfo.Reply(quote: replyQuote, selectionState: selectionState))), botStart: nil, mode: .standard(previewing: true)) as? ChatControllerImpl else { + guard let chatController = selfController.context.sharedContext.makeChatController(context: selfController.context, chatLocation: .peer(id: peerId), subject: .messageOptions(peerIds: [replySubject.messageId.peerId], ids: [replySubject.messageId], info: .reply(ChatControllerSubject.MessageOptionsInfo.Reply(quote: replyQuote, selectionState: selectionState))), botStart: nil, mode: .standard(.previewing)) as? ChatControllerImpl else { return nil } chatController.canReadHistory.set(false) @@ -736,7 +736,7 @@ private func chatLinkOptions(selfController: ChatControllerImpl, sourceNode: ASD } |> distinctUntilChanged - guard let chatController = selfController.context.sharedContext.makeChatController(context: selfController.context, chatLocation: .peer(id: peerId), subject: .messageOptions(peerIds: [peerId], ids: selfController.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], info: .link(ChatControllerSubject.MessageOptionsInfo.Link(options: linkOptions))), botStart: nil, mode: .standard(previewing: true)) as? ChatControllerImpl else { + guard let chatController = selfController.context.sharedContext.makeChatController(context: selfController.context, chatLocation: .peer(id: peerId), subject: .messageOptions(peerIds: [peerId], ids: selfController.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], info: .link(ChatControllerSubject.MessageOptionsInfo.Link(options: linkOptions))), botStart: nil, mode: .standard(.previewing)) as? ChatControllerImpl else { return nil } chatController.canReadHistory.set(false) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 812035d3ec..411470fb32 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -565,7 +565,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var performTextSelectionAction: ((Message?, Bool, NSAttributedString, TextSelectionAction) -> Void)? var performOpenURL: ((Message?, String, Promise?) -> Void)? - public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic = Atomic(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, botAppStart: ChatControllerInitialBotAppStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [ChatNavigationStackItem] = []) { + public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic = Atomic(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, botAppStart: ChatControllerInitialBotAppStart? = nil, mode: ChatControllerPresentationMode = .standard(.default), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [ChatNavigationStackItem] = []) { let _ = ChatControllerCount.modify { value in return value + 1 } @@ -583,7 +583,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var useSharedAnimationPhase = false switch mode { - case .standard(false): + case .standard(.default): useSharedAnimationPhase = true default: break @@ -643,10 +643,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let navigationBarPresentationData: NavigationBarPresentationData? switch mode { - case .inline: - navigationBarPresentationData = nil - default: - navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, hideBackground: self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding ? true : false, hideBadge: false) + case .inline, .standard(.embedded): + navigationBarPresentationData = nil + default: + navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, hideBackground: self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding ? true : false, hideBadge: false) } self.moreBarButton = MoreHeaderButton(color: self.presentationData.theme.rootController.navigationBar.buttonColor) @@ -4603,7 +4603,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } - let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) + let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) var items: [ContextMenuItem] = [ @@ -5270,6 +5270,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G imageOverride = .savedMessagesIcon } else if peer.id.isReplies { imageOverride = .repliesIcon + } else if peer.id.isAnonymousSavedMessages { + imageOverride = .anonymousSavedMessagesIcon } else if peer.isDeleted { imageOverride = .deletedIcon } else { @@ -5629,7 +5631,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) - if case .standard(previewing: false) = mode, let channel = renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info { + if case .standard(.default) = mode, let channel = renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info { var isRegularChat = false if let subject = subject { if case .message = subject { @@ -5903,6 +5905,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.peerView = peerView + let imageOverride: AvatarNodeImageOverride? + if strongSelf.context.account.peerId == savedMessagesPeerId { + imageOverride = .savedMessagesIcon + } else if savedMessagesPeerId.isReplies { + imageOverride = .repliesIcon + } else if savedMessagesPeerId.isAnonymousSavedMessages { + imageOverride = .anonymousSavedMessagesIcon + } else if let peer = savedMessagesPeer?.peer, peer.isDeleted { + imageOverride = .deletedIcon + } else { + imageOverride = nil + } + if strongSelf.isNodeLoaded { strongSelf.chatDisplayNode.overlayTitle = strongSelf.overlayTitle } @@ -5914,7 +5929,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }.updatedSavedMessagesTopicPeer(savedMessagesPeer?.peer) }) - (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.setPeer(context: strongSelf.context, theme: strongSelf.presentationData.theme, peer: savedMessagesPeer?.peer) + (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.setPeer(context: strongSelf.context, theme: strongSelf.presentationData.theme, peer: savedMessagesPeer?.peer, overrideImage: imageOverride) (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.contextActionIsEnabled = false strongSelf.chatInfoNavigationButton?.buttonItem.accessibilityLabel = strongSelf.presentationData.strings.Conversation_ContextMenuOpenProfile } else { @@ -6625,7 +6640,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) self.networkStateDisposable = (context.account.networkState |> deliverOnMainQueue).startStrict(next: { [weak self] state in - if let strongSelf = self, case .standard(previewing: false) = strongSelf.presentationInterfaceState.mode { + if let strongSelf = self, case .standard(.default) = strongSelf.presentationInterfaceState.mode { strongSelf.chatTitleView?.networkState = state } }) @@ -6752,9 +6767,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G func themeAndStringsUpdated() { self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) switch self.presentationInterfaceState.mode { - case .standard: - self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.deferScreenEdgeGestures = [] + case let .standard(standardMode): + switch standardMode { + case .embedded: + self.statusBar.statusBarStyle = .Ignore + default: + self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style + self.deferScreenEdgeGestures = [] + } case .overlay: self.statusBar.statusBarStyle = .Hide self.deferScreenEdgeGestures = [.top] @@ -7186,7 +7206,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if closeOnEmpty { - self.chatDisplayNode.historyNode.setLoadStateUpdated({ [weak self] state, _ in + self.chatDisplayNode.historyNode.addSetLoadStateUpdated({ [weak self] state, _ in guard let strongSelf = self else { return } @@ -7540,7 +7560,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G threadData = .single(nil) } - if case .standard(previewing: true) = self.presentationInterfaceState.mode { + if case .standard(.previewing) = self.presentationInterfaceState.mode { } else if peerId.namespace != Namespaces.Peer.SecretChat && peerId != context.account.peerId && self.subject != .scheduledMessages { self.premiumGiftSuggestionDisposable = (ApplicationSpecificNotice.dismissedPremiumGiftSuggestion(accountManager: self.context.sharedContext.accountManager, peerId: peerId) @@ -10582,7 +10602,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G chatLocation = .peer(id: peerId) } - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: chatLocation, subject: .pinnedMessages(id: pinnedMessage.message.id), botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: chatLocation, subject: .pinnedMessages(id: pinnedMessage.message.id), botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() @@ -10973,7 +10993,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.chatUnreadMentionCountDisposable = (self.context.account.viewTracker.unseenPersonalMessagesAndReactionCount(peerId: peerId, threadId: nil) |> deliverOnMainQueue).startStrict(next: { [weak self] mentionCount, reactionCount in if let strongSelf = self { - if case let .standard(previewing) = strongSelf.presentationInterfaceState.mode, previewing { + if case .standard(.previewing) = strongSelf.presentationInterfaceState.mode { strongSelf.chatDisplayNode.navigateButtons.mentionCount = 0 strongSelf.chatDisplayNode.navigateButtons.reactionsCount = 0 } else { @@ -10985,7 +11005,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else if let peerId = self.chatLocation.peerId, let threadId = self.chatLocation.threadId { self.chatUnreadMentionCountDisposable = (self.context.account.viewTracker.unseenPersonalMessagesAndReactionCount(peerId: peerId, threadId: threadId) |> deliverOnMainQueue).startStrict(next: { [weak self] mentionCount, reactionCount in if let strongSelf = self { - if case let .standard(previewing) = strongSelf.presentationInterfaceState.mode, previewing { + if case .standard(.previewing) = strongSelf.presentationInterfaceState.mode { strongSelf.chatDisplayNode.navigateButtons.mentionCount = 0 strongSelf.chatDisplayNode.navigateButtons.reactionsCount = 0 } else { @@ -11393,7 +11413,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } - if case .standard(false) = self.presentationInterfaceState.mode, self.raiseToListen == nil { + if case .standard(.default) = self.presentationInterfaceState.mode, self.raiseToListen == nil { self.raiseToListen = RaiseToListenManager(shouldActivate: { [weak self] in if let strongSelf = self, strongSelf.isNodeLoaded && strongSelf.canReadHistoryValue, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil, strongSelf.playlistStateAndType == nil { if !strongSelf.context.sharedContext.currentMediaInputSettings.with({ $0.enableRaiseToSpeak }) { @@ -16050,7 +16070,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }))) } - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .message(id: .timestamp(timestamp), highlight: nil, timecode: nil), botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .message(id: .timestamp(timestamp), highlight: nil, timecode: nil), botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 4fd10e2b54..ebefee58ca 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -263,7 +263,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { private var isLoadingValue: Bool = false private var isLoadingEarlier: Bool = false private func updateIsLoading(isLoading: Bool, earlier: Bool, animated: Bool) { - let useLoadingPlaceholder = self.chatLocation.peerId?.namespace != Namespaces.Peer.CloudUser + var useLoadingPlaceholder = self.chatLocation.peerId?.namespace != Namespaces.Peer.CloudUser + if case let .replyThread(message) = self.chatLocation, message.peerId == self.context.account.peerId { + useLoadingPlaceholder = true + } let updated = isLoading != self.isLoadingValue || (isLoading && earlier && !self.isLoadingEarlier) @@ -1342,7 +1345,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } let previewing: Bool - if case .standard(true) = self.chatPresentationInterfaceState.mode { + if case .standard(.previewing) = self.chatPresentationInterfaceState.mode { previewing = true } else { previewing = false @@ -1366,9 +1369,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { insets = layout.insets(options: [.input]) } - if case .overlay = self.chatPresentationInterfaceState.mode { + switch self.chatPresentationInterfaceState.mode { + case .standard(.embedded): + break + case .overlay: insets.top = 44.0 - } else { + default: insets.top += navigationBarHeight } @@ -1993,6 +1999,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { inputPanelUpdateTransition = .immediate } + if case .standard(.embedded) = self.chatPresentationInterfaceState.mode { + self.inputPanelBackgroundNode.isHidden = true + self.inputPanelBackgroundSeparatorNode.isHidden = true + self.inputPanelBottomBackgroundSeparatorNode.isHidden = true + } self.inputPanelBackgroundNode.update(size: CGSize(width: intrinsicInputPanelBackgroundNodeSize.width, height: intrinsicInputPanelBackgroundNodeSize.height + inputPanelBackgroundExtension), transition: inputPanelUpdateTransition, beginWithCurrentState: true) self.inputPanelBottomBackgroundSeparatorBaseOffset = intrinsicInputPanelBackgroundNodeSize.height inputPanelUpdateTransition.updateFrame(node: self.inputPanelBottomBackgroundSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: intrinsicInputPanelBackgroundNodeSize.height + inputPanelBackgroundExtension), size: CGSize(width: intrinsicInputPanelBackgroundNodeSize.width, height: UIScreenPixel)), beginWithCurrentState: true) @@ -3066,7 +3077,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { switch self.chatPresentationInterfaceState.mode { - case .standard(previewing: true): + case .standard(.previewing): if let subject = self.controller?.subject, case let .messageOptions(_, _, info) = subject, case .reply = info { if let controller = self.controller { if let result = controller.presentationContext.hitTest(view: self.view, point: point, with: event) { diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 0b90e15cc2..26fda0d0e0 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -616,6 +616,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto public private(set) var loadState: ChatHistoryNodeLoadState? private var loadStateUpdated: ((ChatHistoryNodeLoadState, Bool) -> Void)? + private var additionalLoadStateUpdated: [(ChatHistoryNodeLoadState, Bool) -> Void] = [] public private(set) var hasPlentyOfMessages: Bool = false public var hasPlentyOfMessagesUpdated: ((Bool) -> Void)? @@ -1507,6 +1508,9 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto if strongSelf.loadState != loadState { strongSelf.loadState = loadState strongSelf.loadStateUpdated?(loadState, false) + for f in strongSelf.additionalLoadStateUpdated { + f(loadState, false) + } } let historyState: ChatHistoryNodeHistoryState = .loading @@ -1948,6 +1952,10 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto public func setLoadStateUpdated(_ f: @escaping (ChatHistoryNodeLoadState, Bool) -> Void) { self.loadStateUpdated = f } + + public func addSetLoadStateUpdated(_ f: @escaping (ChatHistoryNodeLoadState, Bool) -> Void) { + self.additionalLoadStateUpdated.append(f) + } private func maybeUpdateOverscrollAction(offset: CGFloat?) { if self.freezeOverscrollControl { @@ -2966,6 +2974,9 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto if self.loadState != loadState { self.loadState = loadState self.loadStateUpdated?(loadState, transition.options.contains(.AnimateInsertion)) + for f in self.additionalLoadStateUpdated { + f(loadState, transition.options.contains(.AnimateInsertion)) + } } let isEmpty = transition.historyView.originalView.entries.isEmpty || loadState == .empty(.botInfo) @@ -3291,7 +3302,11 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } } strongSelf.loadState = loadState - strongSelf.loadStateUpdated?(loadState, animated || transition.animateIn || animateIn) + let isAnimated = animated || transition.animateIn || animateIn + strongSelf.loadStateUpdated?(loadState, isAnimated) + for f in strongSelf.additionalLoadStateUpdated { + f(loadState, isAnimated) + } } var hasPlentyOfMessages = false diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift index eac1111677..fe7d320454 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift @@ -10,7 +10,7 @@ import ForwardAccessoryPanelNode import ReplyAccessoryPanelNode func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: AccessoryPanelNode?, chatControllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> AccessoryPanelNode? { - if case .standard(previewing: true) = chatPresentationInterfaceState.mode { + if case .standard(.previewing) = chatPresentationInterfaceState.mode { return nil } if let _ = chatPresentationInterfaceState.interfaceState.selectionState { diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 691b724fe8..8e8dfec57b 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -267,6 +267,8 @@ func canReplyInChat(_ chatPresentationInterfaceState: ChatPresentationInterfaceS switch chatPresentationInterfaceState.mode { case .inline: return false + case .standard(.embedded): + return false default: break } @@ -424,6 +426,11 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState return .single(ContextController.Items(content: .list([]))) } + var isEmbeddedMode = false + if case .standard(.embedded) = chatPresentationInterfaceState.mode { + isEmbeddedMode = true + } + var hasExpandedAudioTranscription = false if let messageNode = messageNode as? ChatMessageBubbleItemNode { hasExpandedAudioTranscription = messageNode.hasExpandedAudioTranscription() @@ -509,7 +516,6 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState actions.append(.separator) if chatPresentationInterfaceState.copyProtectionEnabled { - } else { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuCopy, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.actionSheet.primaryTextColor) @@ -769,7 +775,20 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState loggingSettings = LoggingSettings.defaultSettings } - return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia, infoSummaryData, appConfig, isMessageRead, messageViewsPrivacyTips, availableReactions, translationSettings, loggingSettings, notificationSoundList, accountPeer) + return (MessageContextMenuData( + starStatus: stickerSaveStatus, + canReply: canReply && !isEmbeddedMode, + canPin: canPin && !isEmbeddedMode, + canEdit: canEdit && !isEmbeddedMode, + canSelect: canSelect && !isEmbeddedMode, + resourceStatus: resourceStatus, + messageActions: isEmbeddedMode ? ChatAvailableMessageActions( + options: [], + banAuthor: nil, + disableDelete: true, + isCopyProtected: messageActions.isCopyProtected + ) : messageActions + ), updatingMessageMedia, infoSummaryData, appConfig, isMessageRead, messageViewsPrivacyTips, availableReactions, translationSettings, loggingSettings, notificationSoundList, accountPeer) } return dataSignal diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift index c85ae97d29..575b66e084 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift @@ -10,6 +10,10 @@ import ChatChannelSubscriberInputPanelNode import ChatMessageSelectionInputPanelNode func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatInputPanelNode?, currentSecondaryPanel: ChatInputPanelNode?, textInputPanelNode: ChatTextInputPanelNode?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> (primary: ChatInputPanelNode?, secondary: ChatInputPanelNode?) { + if case .standard(.embedded) = chatPresentationInterfaceState.mode { + return (nil, nil) + } + if let renderedPeer = chatPresentationInterfaceState.renderedPeer, renderedPeer.peer?.restrictionText(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) != nil { return (nil, nil) } @@ -118,13 +122,24 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState } if case let .replyThread(message) = chatPresentationInterfaceState.chatLocation, message.peerId == context.account.peerId { - if let currentPanel = (currentPanel as? ChatChannelSubscriberInputPanelNode) ?? (currentSecondaryPanel as? ChatChannelSubscriberInputPanelNode) { - return (currentPanel, nil) + if EnginePeer.Id(message.threadId).isAnonymousSavedMessages { + if let currentPanel = (currentPanel as? ChatRestrictedInputPanelNode) ?? (currentSecondaryPanel as? ChatRestrictedInputPanelNode) { + return (currentPanel, nil) + } else { + let panel = ChatRestrictedInputPanelNode() + panel.context = context + panel.interfaceInteraction = interfaceInteraction + return (panel, nil) + } } else { - let panel = ChatChannelSubscriberInputPanelNode() - panel.interfaceInteraction = interfaceInteraction - panel.context = context - return (panel, nil) + if let currentPanel = (currentPanel as? ChatChannelSubscriberInputPanelNode) ?? (currentSecondaryPanel as? ChatChannelSubscriberInputPanelNode) { + return (currentPanel, nil) + } else { + let panel = ChatChannelSubscriberInputPanelNode() + panel.interfaceInteraction = interfaceInteraction + panel.context = context + return (panel, nil) + } } } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift index 69647525d7..8cf582a26d 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift @@ -142,7 +142,7 @@ func rightNavigationButtonForChatInterfaceState(context: AccountContext, present return chatInfoNavigationButton } - if case .standard(true) = presentationInterfaceState.mode { + if case .standard(.previewing) = presentationInterfaceState.mode { return chatInfoNavigationButton } else if let peer = presentationInterfaceState.renderedPeer?.peer { if presentationInterfaceState.accountPeerId == peer.id { diff --git a/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift index 3bc3af51e1..bea80a0cc2 100644 --- a/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift @@ -46,7 +46,10 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode { var iconImage: UIImage? - if let threadData = interfaceState.threadData, threadData.isClosed { + if case let .replyThread(message) = interfaceState.chatLocation, message.peerId == self.context?.account.peerId { + //TODO:localize + self.textNode.attributedText = NSAttributedString(string: "Senders of these messages restricted to link\ntheir name when forwarding.", font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) + } else if let threadData = interfaceState.threadData, threadData.isClosed { iconImage = PresentationResourcesChat.chatPanelLockIcon(interfaceState.theme) self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelTopicClosedText, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } else if let channel = interfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum), case .peer = interfaceState.chatLocation { diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index 5b124d6689..a99bd19f41 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -251,7 +251,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe switch item.content { case let .peer(peerData): if let message = peerData.messages.first { - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerData.peer.peerId), subject: .message(id: .id(message.id), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil), botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerData.peer.peerId), subject: .message(id: .id(message.id), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil), botStart: nil, mode: .standard(.previewing)) chatController.canReadHistory.set(false) let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list([]))), gesture: gesture) presentInGlobalOverlay(contextController) diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 1632fed615..cabf003a6c 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -38,7 +38,7 @@ private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceh init(context: AccountContext) { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: .standard(.default), chatLocation: .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil) self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: true) self.emptyNode = ChatEmptyNode(context: context, interaction: nil) @@ -51,7 +51,7 @@ private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceh func updatePresentationData(_ presentationData: PresentationData) { self.presentationData = presentationData - self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.presentationInterfaceState.limitsConfiguration, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.presentationInterfaceState.accountPeerId, mode: .standard(previewing: false), chatLocation: self.presentationInterfaceState.chatLocation, subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.presentationInterfaceState.limitsConfiguration, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.presentationInterfaceState.accountPeerId, mode: .standard(.default), chatLocation: self.presentationInterfaceState.chatLocation, subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil) self.wallpaperBackgroundNode.update(wallpaper: presentationData.chatWallpaper, animated: false) } diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index 6b73742cdc..564c632f93 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit 6b73742cdc140c46a1ab1b8e3390354a9738e429 +Subproject commit 564c632f9368409870631d3cef75a7fc4070d45b