From 90725b9db9eaaf826bb39a2b4b35f7dd95430eab Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 29 Jul 2021 18:20:52 +0300 Subject: [PATCH 01/18] Various Fixes (cherry picked from commit cf4f852043c87db05e9ee5f82daad7e1dff69987) --- .../Sources/PeerSelectionController.swift | 2 +- .../Sources/ChatListControllerNode.swift | 4 +- .../Sources/ChatListSearchContainerNode.swift | 64 +++++++++++-------- .../Sources/ChatListSearchListPaneNode.swift | 18 ++++-- .../Sources/Node/ChatListItem.swift | 4 +- .../Sources/Node/ChatListNode.swift | 28 +++++--- .../Sources/Node/ChatListNodeEntries.swift | 13 ++-- .../Sources/HashtagSearchController.swift | 2 +- .../Sources/SemanticStatusNode.swift | 1 + .../TextSizeSelectionController.swift | 2 +- .../ThemeAccentColorControllerNode.swift | 2 +- .../Themes/ThemePreviewControllerNode.swift | 2 +- .../TelegramUI/Sources/ChatController.swift | 53 ++++++++------- .../ChatSearchResultsContollerNode.swift | 2 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 53 ++++++++------- .../Sources/PeerSelectionController.swift | 6 +- .../Sources/PeerSelectionControllerNode.swift | 17 +++-- 17 files changed, 163 insertions(+), 110 deletions(-) diff --git a/submodules/AccountContext/Sources/PeerSelectionController.swift b/submodules/AccountContext/Sources/PeerSelectionController.swift index 21d396b434..34a9371976 100644 --- a/submodules/AccountContext/Sources/PeerSelectionController.swift +++ b/submodules/AccountContext/Sources/PeerSelectionController.swift @@ -63,7 +63,7 @@ public enum PeerSelectionControllerSendMode { public protocol PeerSelectionController: ViewController { var peerSelected: ((Peer) -> Void)? { get set } - var multiplePeersSelected: (([Peer], NSAttributedString, PeerSelectionControllerSendMode) -> Void)? { get set } + var multiplePeersSelected: (([Peer], [PeerId: Peer], NSAttributedString, PeerSelectionControllerSendMode) -> Void)? { get set } var inProgress: Bool { get set } var customDismiss: (() -> Void)? { get set } } diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 89e035f20e..db5afda01a 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -181,7 +181,7 @@ private final class ChatListShimmerNode: ASDisplayNode { let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt32Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) let timestamp1: Int32 = 100000 let peers = SimpleDictionary() - let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in + let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() }, present: { _ in }) @@ -1170,7 +1170,7 @@ final class ChatListControllerNode: ASDisplayNode { filter.insert(.excludeRecent) } - let contentNode = ChatListSearchContainerNode(context: self.context, filter: filter, groupId: self.groupId, displaySearchFilters: displaySearchFilters, initialFilter: initialFilter, openPeer: { [weak self] peer, dismissSearch in + let contentNode = ChatListSearchContainerNode(context: self.context, filter: filter, groupId: self.groupId, displaySearchFilters: displaySearchFilters, initialFilter: initialFilter, openPeer: { [weak self] peer, _, dismissSearch in self?.requestOpenPeerFromSearch?(peer, dismissSearch) }, openDisabledPeer: { _ in }, openRecentPeerOptions: { [weak self] peer in diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index c7dd83d666..96e6e0f078 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -38,7 +38,7 @@ private enum ChatListTokenId: Int32 { } final class ChatListSearchInteraction { - let openPeer: (Peer, Bool) -> Void + let openPeer: (Peer, Peer?, Bool) -> Void let openDisabledPeer: (Peer) -> Void let openMessage: (Peer, MessageId, Bool) -> Void let openUrl: (String) -> Void @@ -52,7 +52,7 @@ final class ChatListSearchInteraction { let dismissInput: () -> Void let getSelectedMessageIds: () -> Set? - init(openPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openMessage: @escaping (Peer, MessageId, Bool) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (MessageId, Bool) -> Void, messageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), mediaMessageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, getSelectedMessageIds: @escaping () -> Set?) { + init(openPeer: @escaping (Peer, Peer?, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openMessage: @escaping (Peer, MessageId, Bool) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (MessageId, Bool) -> Void, messageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), mediaMessageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, getSelectedMessageIds: @escaping () -> Set?) { self.openPeer = openPeer self.openDisabledPeer = openDisabledPeer self.openMessage = openMessage @@ -124,7 +124,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo private var validLayout: (ContainerViewLayout, CGFloat)? - public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, displaySearchFilters: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) { + public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, displaySearchFilters: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (Peer, Peer?, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) { self.context = context self.peersFilter = filter self.groupId = groupId @@ -149,8 +149,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo self.addSubnode(self.paneContainerNode) - let interaction = ChatListSearchInteraction(openPeer: { peer, value in - originalOpenPeer(peer, value) + let interaction = ChatListSearchInteraction(openPeer: { peer, chatPeer, value in + originalOpenPeer(peer, chatPeer, value) if peer.id.namespace != Namespaces.Peer.SecretChat { addAppLogEvent(postbox: context.account.postbox, type: "search_global_open_peer", peerId: peer.id) } @@ -901,7 +901,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo }).start() let peerSelectionController = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.onlyWriteable, .excludeDisabled], multipleSelection: true)) - peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, messageText, mode in + peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, peerMap, messageText, mode in guard let strongSelf = self, let strongController = peerSelectionController else { return } @@ -926,6 +926,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo return .forward(source: messageId, grouping: .auto, attributes: [], correlationId: nil) }) + var displayPeers: [Peer] = [] for peer in peers { let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: result) |> deliverOnMainQueue).start(next: { messageIds in @@ -951,31 +952,38 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo |> deliverOnMainQueue).start()) } }) - - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let text: String - var savedMessages = false - if peers.count == 1, let peerId = peers.first?.id, peerId == strongSelf.context.account.peerId { - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many - savedMessages = true - } else { - if peers.count == 1, let peer = peers.first { - let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string - } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last { - let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string - } else if let peer = peers.first { - let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(peers.count - 1)").string - } else { - text = "" + if let secretPeer = peer as? TelegramSecretChat { + if let peer = peerMap[secretPeer.regularPeerId] { + displayPeers.append(peer) } + } else { + displayPeers.append(peer) } - - (strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) } + + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + let text: String + var savedMessages = false + if displayPeers.count == 1, let peerId = displayPeers.first?.id, peerId == strongSelf.context.account.peerId { + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many + savedMessages = true + } else { + if displayPeers.count == 1, let peer = displayPeers.first { + let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string + } else if displayPeers.count == 2, let firstPeer = displayPeers.first, let secondPeer = displayPeers.last { + let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string + } else if let peer = displayPeers.first { + let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(displayPeers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(displayPeers.count - 1)").string + } else { + text = "" + } + } + + (strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) } peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer in let peerId = peer.id diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 1709c9d6e7..b227158f6a 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -440,8 +440,12 @@ public enum ChatListSearchEntry: Comparable, Identifiable { }) } - return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .generalSearch, peer: .peer(peer: primaryPeer, chatPeer: chatPeer), status: .none, badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in - interaction.peerSelected(peer, nil) + return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .generalSearch, peer: .peer(peer: primaryPeer, chatPeer: chatPeer), status: .none, badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { contactPeer in + if case let .peer(maybePeer, maybeChatPeer) = contactPeer, let peer = maybePeer, let chatPeer = maybeChatPeer { + interaction.peerSelected(chatPeer, peer, nil) + } else { + interaction.peerSelected(peer, nil, nil) + } }, contextAction: peerContextAction.flatMap { peerContextAction in return { node, gesture in if let chatPeer = chatPeer, chatPeer.id.namespace != Namespaces.Peer.SecretChat { @@ -504,7 +508,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { } return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .generalSearch, peer: .peer(peer: peer.peer, chatPeer: peer.peer), status: .addressName(suffixString), badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in - interaction.peerSelected(peer.peer, nil) + interaction.peerSelected(peer.peer, nil, nil) }, contextAction: peerContextAction.flatMap { peerContextAction in return { node, gesture in peerContextAction(peer.peer, .search(nil), node, gesture) @@ -1209,9 +1213,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } let chatListInteraction = ChatListNodeInteraction(activateSearch: { - }, peerSelected: { [weak self] peer, _ in + }, peerSelected: { [weak self] peer, chatPeer, _ in interaction.dismissInput() - interaction.openPeer(peer, false) + interaction.openPeer(peer, chatPeer, false) let _ = context.engine.peers.addRecentlySearchedPeer(peerId: peer.id).start() self?.listNode.clearHighlightAnimated(true) }, disabledPeerSelected: { _ in @@ -1478,7 +1482,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let firstTime = previousEntries == nil let transition = chatListSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries, context: context, presentationData: presentationData, filter: peersFilter, peerSelected: { peer in - interaction.openPeer(peer, true) + interaction.openPeer(peer, nil, true) let _ = context.engine.peers.addRecentlySearchedPeer(peerId: peer.id).start() self?.recentListNode.clearHighlightAnimated(true) }, disabledPeerSelected: { peer in @@ -2324,7 +2328,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode { let timestamp1: Int32 = 100000 var peers = SimpleDictionary() peers[peer1.id] = peer1 - let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in + let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() }, present: { _ in }) diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index dcf12008f5..137bf423b7 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -127,9 +127,9 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour { if let message = messages.last, let peer = peer.peer { self.interaction.messageSelected(peer, message, promoInfo) } else if let peer = peer.peer { - self.interaction.peerSelected(peer, promoInfo) + self.interaction.peerSelected(peer, nil, promoInfo) } else if let peer = peer.peers[peer.peerId] { - self.interaction.peerSelected(peer, promoInfo) + self.interaction.peerSelected(peer, nil, promoInfo) } case let .groupReference(groupId, _, _, _, _): self.interaction.groupSelected(groupId) diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 226005a3cd..0435d3078f 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -53,7 +53,7 @@ public final class ChatListNodeInteraction { } let activateSearch: () -> Void - let peerSelected: (Peer, ChatListNodeEntryPromoInfo?) -> Void + let peerSelected: (Peer, Peer?, ChatListNodeEntryPromoInfo?) -> Void let disabledPeerSelected: (Peer) -> Void let togglePeerSelected: (Peer) -> Void let togglePeersSelection: ([PeerEntry], Bool) -> Void @@ -75,7 +75,7 @@ public final class ChatListNodeInteraction { public var searchTextHighightState: String? var highlightedChatLocation: ChatListHighlightedLocation? - public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (Peer) -> Void, togglePeersSelection: @escaping ([PeerEntry], Bool) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (Peer, Message, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId, Bool) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (PeerId) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) { + public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer, Peer?, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (Peer) -> Void, togglePeersSelection: @escaping ([PeerEntry], Bool) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (Peer, Message, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId, Bool) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (PeerId) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) { self.activateSearch = activateSearch self.peerSelected = peerSelected self.disabledPeerSelected = disabledPeerSelected @@ -106,6 +106,18 @@ public final class ChatListNodePeerInputActivities { } } +private func areFoundPeerArraysEqual(_ lhs: [(Peer, Peer?)], _ rhs: [(Peer, Peer?)]) -> Bool { + if lhs.count != rhs.count { + return false + } + for i in 0 ..< lhs.count { + if !arePeersEqual(lhs[i].0, rhs[i].0) || !arePeersEqual(lhs[i].1, rhs[i].1) { + return false + } + } + return true +} + public struct ChatListNodeState: Equatable { public var presentationData: ChatListPresentationData public var editing: Bool @@ -117,10 +129,10 @@ public struct ChatListNodeState: Equatable { public var archiveShouldBeTemporaryRevealed: Bool public var selectedAdditionalCategoryIds: Set public var hiddenPsaPeerId: PeerId? - public var foundPeers: [Peer] + public var foundPeers: [(Peer, Peer?)] public var selectedPeerMap: [PeerId: Peer] - public init(presentationData: ChatListPresentationData, editing: Bool, peerIdWithRevealedOptions: PeerId?, selectedPeerIds: Set, foundPeers: [Peer], selectedPeerMap: [PeerId: Peer], selectedAdditionalCategoryIds: Set, peerInputActivities: ChatListNodePeerInputActivities?, pendingRemovalPeerIds: Set, pendingClearHistoryPeerIds: Set, archiveShouldBeTemporaryRevealed: Bool, hiddenPsaPeerId: PeerId?) { + public init(presentationData: ChatListPresentationData, editing: Bool, peerIdWithRevealedOptions: PeerId?, selectedPeerIds: Set, foundPeers: [(Peer, Peer?)], selectedPeerMap: [PeerId: Peer], selectedAdditionalCategoryIds: Set, peerInputActivities: ChatListNodePeerInputActivities?, pendingRemovalPeerIds: Set, pendingClearHistoryPeerIds: Set, archiveShouldBeTemporaryRevealed: Bool, hiddenPsaPeerId: PeerId?) { self.presentationData = presentationData self.editing = editing self.peerIdWithRevealedOptions = peerIdWithRevealedOptions @@ -148,7 +160,7 @@ public struct ChatListNodeState: Equatable { if lhs.selectedPeerIds != rhs.selectedPeerIds { return false } - if arePeerArraysEqual(lhs.foundPeers, rhs.foundPeers) { + if areFoundPeerArraysEqual(lhs.foundPeers, rhs.foundPeers) { return false } if arePeerDictionariesEqual(lhs.selectedPeerMap, rhs.selectedPeerMap) { @@ -302,7 +314,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL if editing { nodeInteraction.togglePeerSelected(chatPeer) } else { - nodeInteraction.peerSelected(chatPeer, nil) + nodeInteraction.peerSelected(chatPeer, nil, nil) } } }, disabledAction: { _ in @@ -383,7 +395,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL if editing { nodeInteraction.togglePeerSelected(chatPeer) } else { - nodeInteraction.peerSelected(chatPeer, nil) + nodeInteraction.peerSelected(chatPeer, nil, nil) } } }, disabledAction: { _ in @@ -602,7 +614,7 @@ public final class ChatListNode: ListView { if let strongSelf = self, let activateSearch = strongSelf.activateSearch { activateSearch() } - }, peerSelected: { [weak self] peer, promoInfo in + }, peerSelected: { [weak self] peer, _, promoInfo in if let strongSelf = self, let peerSelected = strongSelf.peerSelected { peerSelected(peer, true, true, promoInfo) } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift index ac87a13490..7b00ff204d 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift @@ -281,7 +281,7 @@ private func offsetPinnedIndex(_ index: ChatListIndex, offset: UInt16) -> ChatLi } } -func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState, savedMessagesPeer: Peer?, foundPeers: [Peer], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, mode: ChatListNodeMode) -> (entries: [ChatListNodeEntry], loading: Bool) { +func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState, savedMessagesPeer: Peer?, foundPeers: [(Peer, Peer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, mode: ChatListNodeMode) -> (entries: [ChatListNodeEntry], loading: Bool) { var result: [ChatListNodeEntry] = [] var pinnedIndexOffset: UInt16 = 0 @@ -300,7 +300,7 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState, var foundPeerIds = Set() for peer in foundPeers { - foundPeerIds.insert(peer.id) + foundPeerIds.insert(peer.0.id) } if view.laterIndex == nil && savedMessagesPeer == nil { @@ -338,8 +338,13 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState, if !foundPeers.isEmpty { var foundPinningIndex: UInt16 = UInt16(foundPeers.count) for peer in foundPeers.reversed() { - let messageIndex = MessageIndex(id: MessageId(peerId: peer.id, namespace: 0, id: 0), timestamp: 1) - result.append(.PeerEntry(index: ChatListIndex(pinningIndex: foundPinningIndex, messageIndex: messageIndex), presentationData: state.presentationData, messages: [], readState: nil, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: nil, peer: RenderedPeer(peerId: peer.id, peers: SimpleDictionary([peer.id: peer])), presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), editing: state.editing, hasActiveRevealControls: false, selected: state.selectedPeerIds.contains(peer.id), inputActivities: nil, promoInfo: nil, hasFailedMessages: false, isContact: false)) + var peers: [PeerId: Peer] = [peer.0.id: peer.0] + if let chatPeer = peer.1 { + peers[chatPeer.id] = chatPeer + } + + let messageIndex = MessageIndex(id: MessageId(peerId: peer.0.id, namespace: 0, id: 0), timestamp: 1) + result.append(.PeerEntry(index: ChatListIndex(pinningIndex: foundPinningIndex, messageIndex: messageIndex), presentationData: state.presentationData, messages: [], readState: nil, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: nil, peer: RenderedPeer(peerId: peer.0.id, peers: SimpleDictionary(peers)), presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), editing: state.editing, hasActiveRevealControls: false, selected: state.selectedPeerIds.contains(peer.0.id), inputActivities: nil, promoInfo: nil, hasFailedMessages: false, isContact: false)) if foundPinningIndex != 0 { foundPinningIndex -= 1 } diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index 2976f02ba7..1e80d08b49 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -48,7 +48,7 @@ public final class HashtagSearchController: TelegramBaseController { return result.messages.map({ .message($0, RenderedPeer(message: $0), result.readStates[$0.id.peerId], chatListPresentationData, result.totalCount, nil, false) }) } let interaction = ChatListNodeInteraction(activateSearch: { - }, peerSelected: { _, _ in + }, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in diff --git a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift index 970de65856..6ddd16dc37 100644 --- a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift +++ b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift @@ -273,6 +273,7 @@ private final class SemanticStatusNodeIconContext: SemanticStatusNodeStateContex strongSelf.requestUpdate() } } + self.animationNode?.enqueueState(self.icon == .play ? .play : .pause, animated: false) self.iconImage = self.animationNode?.image self.iconOffset = 1.5 } diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index b2a4ed06b3..5fd07b038f 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -210,7 +210,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) { var items: [ChatListItem] = [] - let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in + let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 7e3eb52b5d..bd1850236f 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -778,7 +778,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) { var items: [ChatListItem] = [] - let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in + let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 3a88403acc..c07a304420 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -355,7 +355,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) { var items: [ChatListItem] = [] - let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in + let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index cfda44c958..5c6ac8f1c9 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -10925,7 +10925,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } controller.present(textAlertController(context: context, title: nil, text: presentationData.strings.Forward_ErrorDisabledForChat, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } - controller.multiplePeersSelected = { [weak self, weak controller] peers, messageText, mode in + controller.multiplePeersSelected = { [weak self, weak controller] peers, peerMap, messageText, mode in guard let strongSelf = self, let strongController = controller else { return } @@ -10951,6 +10951,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) let commit: ([EnqueueMessage]) -> Void = { result in + var displayPeers: [Peer] = [] for peer in peers { let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: result) |> deliverOnMainQueue).start(next: { messageIds in @@ -10977,30 +10978,38 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let text: String - var savedMessages = false - if peers.count == 1, let peerId = peers.first?.id, peerId == strongSelf.context.account.peerId { - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many - savedMessages = true - } else { - if peers.count == 1, let peer = peers.first { - let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string - } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last { - let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string - } else if let peer = peers.first { - let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(peers.count - 1)").string - } else { - text = "" + if let secretPeer = peer as? TelegramSecretChat { + if let peer = peerMap[secretPeer.regularPeerId] { + displayPeers.append(peer) } + } else { + displayPeers.append(peer) } - - strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) } + + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + let text: String + var savedMessages = false + if displayPeers.count == 1, let peerId = displayPeers.first?.id, peerId == strongSelf.context.account.peerId { + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many + savedMessages = true + } else { + if displayPeers.count == 1, let peer = displayPeers.first { + let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string + } else if displayPeers.count == 2, let firstPeer = displayPeers.first, let secondPeer = displayPeers.last { + let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string + } else if let peer = displayPeers.first { + let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(displayPeers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(displayPeers.count - 1)").string + } else { + text = "" + } + } + + strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) } switch mode { diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index ba76debd4d..901788194a 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -171,7 +171,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe } let interaction = ChatListNodeInteraction(activateSearch: { - }, peerSelected: { _, _ in + }, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 724a704546..abce9e6d90 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -5649,7 +5649,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD func forwardMessages(messageIds: Set?) { if let messageIds = messageIds ?? self.state.selectedMessageIds, !messageIds.isEmpty { let peerSelectionController = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.onlyWriteable, .excludeDisabled], multipleSelection: true)) - peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, messageText, mode in + peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, peerMap, messageText, mode in guard let strongSelf = self, let strongController = peerSelectionController else { return } @@ -5674,6 +5674,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD return .forward(source: messageId, grouping: .auto, attributes: [], correlationId: nil) }) + var displayPeers: [Peer] = [] for peer in peers { let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: result) |> deliverOnMainQueue).start(next: { messageIds in @@ -5700,30 +5701,38 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } }) - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let text: String - var savedMessages = false - if peers.count == 1, let peerId = peers.first?.id, peerId == strongSelf.context.account.peerId { - text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many - savedMessages = true - } else { - if peers.count == 1, let peer = peers.first { - let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string - } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last { - let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string - } else if let peer = peers.first { - let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(peers.count - 1)").string - } else { - text = "" + if let secretPeer = peer as? TelegramSecretChat { + if let peer = peerMap[secretPeer.regularPeerId] { + displayPeers.append(peer) } + } else { + displayPeers.append(peer) } - - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) } + + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + let text: String + var savedMessages = false + if displayPeers.count == 1, let peerId = displayPeers.first?.id, peerId == strongSelf.context.account.peerId { + text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many + savedMessages = true + } else { + if displayPeers.count == 1, let peer = displayPeers.first { + let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string + } else if displayPeers.count == 2, let firstPeer = displayPeers.first, let secondPeer = displayPeers.last { + let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string + } else if let peer = displayPeers.first { + let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(displayPeers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(displayPeers.count - 1)").string + } else { + text = "" + } + } + + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) } peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer in let peerId = peer.id diff --git a/submodules/TelegramUI/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Sources/PeerSelectionController.swift index 9e8240e79c..04b95dfe60 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionController.swift @@ -19,7 +19,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon private var customTitle: String? public var peerSelected: ((Peer) -> Void)? - public var multiplePeersSelected: (([Peer], NSAttributedString, PeerSelectionControllerSendMode) -> Void)? + public var multiplePeersSelected: (([Peer], [PeerId: Peer], NSAttributedString, PeerSelectionControllerSendMode) -> Void)? private let filter: ChatListNodePeersFilter private let attemptSelection: ((Peer) -> Void)? @@ -158,8 +158,8 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon self.peerSelectionNode.navigationBar = self.navigationBar - self.peerSelectionNode.requestSend = { [weak self] peers, text, mode in - self?.multiplePeersSelected?(peers, text, mode) + self.peerSelectionNode.requestSend = { [weak self] peers, peerMap, text, mode in + self?.multiplePeersSelected?(peers, peerMap, text, mode) } self.peerSelectionNode.requestDeactivateSearch = { [weak self] in diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index 75dfeaf130..9d6eb2af3d 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -56,7 +56,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { var requestOpenDisabledPeer: ((Peer) -> Void)? var requestOpenPeerFromSearch: ((Peer) -> Void)? var requestOpenMessageFromSearch: ((Peer, MessageId) -> Void)? - var requestSend: (([Peer], NSAttributedString, PeerSelectionControllerSendMode) -> Void)? + var requestSend: (([Peer], [PeerId: Peer], NSAttributedString, PeerSelectionControllerSendMode) -> Void)? private var presentationData: PresentationData private var presentationDataDisposable: Disposable? @@ -348,13 +348,15 @@ final class PeerSelectionControllerNode: ASDisplayNode { let selectedContactPeers = strongSelf.contactListNode?.selectedPeers ?? [] let effectiveInputText = strongSelf.presentationInterfaceState.interfaceState.composeInputState.inputText var selectedPeers: [Peer] = [] + var selectedPeerMap: [PeerId: Peer] = [:] for contactPeer in selectedContactPeers { if case let .peer(peer, _, _) = contactPeer { selectedPeers.append(peer) + selectedPeerMap[peer.id] = peer } } if !selectedPeers.isEmpty { - strongSelf.requestSend?(selectedPeers, effectiveInputText, mode) + strongSelf.requestSend?(selectedPeers, selectedPeerMap, effectiveInputText, mode) } } else { var selectedPeerIds: [PeerId] = [] @@ -372,7 +374,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { selectedPeers.append(peer) } } - strongSelf.requestSend?(selectedPeers, effectiveInputText, mode) + strongSelf.requestSend?(selectedPeers, selectedPeerMap, effectiveInputText, mode) } } } @@ -492,7 +494,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { } if self.chatListNode.supernode != nil { - self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ChatListSearchContainerNode(context: self.context, filter: self.filter, groupId: .root, displaySearchFilters: false, openPeer: { [weak self] peer, _ in + self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ChatListSearchContainerNode(context: self.context, filter: self.filter, groupId: .root, displaySearchFilters: false, openPeer: { [weak self] peer, chatPeer, _ in guard let strongSelf = self else { return } @@ -505,15 +507,18 @@ final class PeerSelectionControllerNode: ASDisplayNode { var foundPeers = state.foundPeers var selectedPeerMap = state.selectedPeerMap selectedPeerMap[peer.id] = peer + if peer is TelegramSecretChat, let chatPeer = chatPeer { + selectedPeerMap[chatPeer.id] = chatPeer + } var exists = false for foundPeer in foundPeers { - if peer.id == foundPeer.id { + if peer.id == foundPeer.0.id { exists = true break } } if !exists { - foundPeers.insert(peer, at: 0) + foundPeers.insert((peer, chatPeer), at: 0) } if state.selectedPeerIds.contains(peer.id) { state.selectedPeerIds.remove(peer.id) From 51b1f7a836dcf5753b225e899d823a405a51170b Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 31 Jul 2021 02:33:15 +0300 Subject: [PATCH 02/18] Fix attached content file layout (cherry picked from commit 7712ad0a27fbaf27a22254af77a18961ff0b0a6f) --- .../TelegramUI/Sources/ChatMessageAttachedContentNode.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift index d1b73fc1a4..841078d66d 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift @@ -786,6 +786,8 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { var imageHeightAddition = size.height + 6.0 if textFrame.size.height > CGFloat.ulpOfOne { imageHeightAddition += 6.0 + } else { + imageHeightAddition += 7.0 } adjustedBoundingSize.height += imageHeightAddition + 5.0 @@ -931,7 +933,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { if let (_, flags) = mediaAndFlags, flags.contains(.preferMediaBeforeText) { contentFileNode.frame = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: contentFileSize) } else { - contentFileNode.frame = CGRect(origin: CGPoint(x: insets.left, y: textFrame.maxY + (textFrame.size.height > CGFloat.ulpOfOne ? 8.0 : 0.0)), size: contentFileSize) + contentFileNode.frame = CGRect(origin: CGPoint(x: insets.left, y: textFrame.maxY + (textFrame.size.height > CGFloat.ulpOfOne ? 8.0 : 7.0)), size: contentFileSize) } } else if let contentFileNode = strongSelf.contentFileNode { contentFileNode.removeFromSupernode() From 40b7feac423cb60596818692f1d27bfaa4dc78cf Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 31 Jul 2021 00:27:42 +0300 Subject: [PATCH 03/18] Video Messages Improvements (cherry picked from commit e614343888ebdb7208399b785815b5dbc3f8ad26) --- .../LegacyComponents/TGVideoMessageRingView.h | 7 + .../Sources/TGVideoMessageCaptureController.m | 7 + .../Sources/TGVideoMessageRingView.m | 162 ++++++++++++++++++ .../MediaPlayer/Sources/MediaPlayer.swift | 1 - .../InstantVideoRadialStatusNode.swift | 8 +- .../Sources/ChatTextInputAttributes.swift | 20 ++- 6 files changed, 193 insertions(+), 12 deletions(-) diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoMessageRingView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoMessageRingView.h index b9c7004ee7..55253039fe 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoMessageRingView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoMessageRingView.h @@ -1,5 +1,12 @@ #import +@interface TGVideoMessageShimmerView : UIView + +- (void)updateAbsoluteRect:(CGRect)absoluteRect containerSize:(CGSize)containerSize; + +@end + + @interface TGVideoMessageRingView : UIView @property (nonatomic, strong) UIColor *accentColor; diff --git a/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m b/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m index 371c802c22..28d75d6496 100644 --- a/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m +++ b/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m @@ -96,6 +96,7 @@ typedef enum UIView *_separatorView; UIImageView *_placeholderView; + TGVideoMessageShimmerView *_shimmerView; bool _automaticDismiss; NSTimeInterval _startTimestamp; @@ -329,6 +330,10 @@ typedef enum _placeholderView.image = [TGVideoMessageCaptureController startImage]; [_circleView addSubview:_placeholderView]; + _shimmerView = [[TGVideoMessageShimmerView alloc] initWithFrame:_circleView.bounds]; + [_shimmerView updateAbsoluteRect:_circleView.bounds containerSize:_circleView.bounds.size]; + [_circleView addSubview:_shimmerView]; + if (iosMajorVersion() >= 11) { _shadowView.accessibilityIgnoresInvertColors = true; @@ -1182,9 +1187,11 @@ typedef enum [UIView animateWithDuration:0.3 delay:delay options:kNilOptions animations:^ { _placeholderView.alpha = 0.0f; + _shimmerView.alpha = 0.0f; _switchButton.alpha = 1.0f; } completion:^(__unused BOOL finished) { + _shimmerView.hidden = true; _placeholderView.hidden = true; _placeholderView.alpha = 1.0f; }]; diff --git a/submodules/LegacyComponents/Sources/TGVideoMessageRingView.m b/submodules/LegacyComponents/Sources/TGVideoMessageRingView.m index e2e84b919a..e623de4111 100644 --- a/submodules/LegacyComponents/Sources/TGVideoMessageRingView.m +++ b/submodules/LegacyComponents/Sources/TGVideoMessageRingView.m @@ -2,6 +2,168 @@ #import "TGColor.h" +#import "LegacyComponentsInternal.h" + +@interface TGVideoMessageShimmerEffectForegroundView : UIView +{ + UIView *_imageContainerView; + UIView *_imageView; + + CGFloat _size; + bool _hasContainerSize; + CGRect _absoluteRect; + CGSize _containerSize; +} + +- (instancetype)initWithSize:(CGFloat)size alpha:(CGFloat)alpha; + +@end + +@implementation TGVideoMessageShimmerEffectForegroundView + +- (instancetype)initWithSize:(CGFloat)size alpha:(CGFloat)alpha { + self = [super initWithFrame:CGRectZero]; + if (self != nil) { + _size = size; + + _imageContainerView = [[UIView alloc] init]; + _imageView = [[UIView alloc] init]; + + self.clipsToBounds = true; + + [_imageContainerView addSubview:_imageView]; + [self addSubview:_imageContainerView]; + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(size, 16), false, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGRect bounds = CGRectMake(0, 0, size, 16); + CGContextClearRect(context, bounds); + CGContextClipToRect(context, bounds); + + UIColor *transparentColor = [UIColor colorWithWhite:1.0 alpha:0.0]; + UIColor *peakColor = [UIColor colorWithWhite:1.0 alpha:alpha]; + + CGColorRef colors[3] = { + CGColorRetain(transparentColor.CGColor), + CGColorRetain(peakColor.CGColor), + CGColorRetain(transparentColor.CGColor) + }; + + CFArrayRef colorsArray = CFArrayCreate(kCFAllocatorDefault, (const void **)&colors, 3, NULL); + CGFloat locations[3] = {0.0f, 0.5, 1.0}; + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, colorsArray, (CGFloat const *)&locations); + + CGContextDrawLinearGradient(context, gradient, CGPointMake(0, 0), CGPointMake(size, 0), kNilOptions); + + CFRelease(colorsArray); + CFRelease(colors[0]); + CFRelease(colors[1]); + + CGColorSpaceRelease(colorSpace); + CFRelease(gradient); + + UIImage *image = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:25 topCapHeight:25]; + UIGraphicsEndImageContext(); + + _imageView.backgroundColor = [UIColor colorWithPatternImage:image]; + } + return self; +} + +- (void)updateAbsoluteRect:(CGRect)absoluteRect containerSize:(CGSize)containerSize { + _hasContainerSize = true; + + CGRect previousAbsoluteRect = _absoluteRect; + CGSize previousContainerSize = _containerSize; + _absoluteRect = absoluteRect; + _containerSize = containerSize; + + if (!CGSizeEqualToSize(previousContainerSize, containerSize)) { + [self setupAnimation]; + } + + if (!CGRectEqualToRect(previousAbsoluteRect, absoluteRect)) { + _imageContainerView.frame = CGRectMake(-absoluteRect.origin.x, -absoluteRect.origin.y, containerSize.width, containerSize.height); + } +} + + +- (void)setupAnimation { + if (!_hasContainerSize) { + return; + } + + CGFloat gradientHeight = _size; + _imageView.frame = CGRectMake(-gradientHeight, 0, gradientHeight, _containerSize.height); + + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.x"]; + animation.fromValue = @(_imageView.center.x); + animation.toValue = @(_imageView.center.x + _containerSize.width + gradientHeight); + animation.duration = 1.3f; + animation.repeatCount = INFINITY; + animation.beginTime = 1.0; + [_imageView.layer addAnimation:animation forKey:@"position"]; +} + +@end + +@interface TGVideoMessageShimmerView () +{ + TGVideoMessageShimmerEffectForegroundView *_effectView; + UIImageView *_imageView; + + UIView *_borderView; + UIView *_borderMaskView; + TGVideoMessageShimmerEffectForegroundView *_borderEffectView; +} +@end + +@implementation TGVideoMessageShimmerView + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self != nil) { + self.clipsToBounds = true; + self.layer.cornerRadius = frame.size.width / 2.0f; + if (iosMajorVersion() >= 13) { + self.layer.cornerCurve = kCACornerCurveCircular; + } + + _effectView = [[TGVideoMessageShimmerEffectForegroundView alloc] initWithSize:320 alpha:0.3]; + _effectView.layer.compositingFilter = @"screenBlendMode"; + _effectView.frame = self.bounds; + + _borderView = [[UIView alloc] initWithFrame:self.bounds]; + _borderMaskView = [[UIView alloc] initWithFrame:self.bounds]; + _borderMaskView.layer.borderWidth = 1.0; + _borderMaskView.layer.borderColor = [UIColor whiteColor].CGColor; + _borderMaskView.layer.cornerRadius = frame.size.width / 2.0f; + if (iosMajorVersion() >= 13) { + _borderMaskView.layer.cornerCurve = kCACornerCurveCircular; + } + _borderView.maskView = _borderMaskView; + + _borderEffectView = [[TGVideoMessageShimmerEffectForegroundView alloc] initWithSize:400 alpha:0.45]; + _borderEffectView.layer.compositingFilter = @"screenBlendMode"; + _borderEffectView.frame = self.bounds; + + [self addSubview:_effectView]; + [self addSubview:_borderView]; + [_borderView addSubview:_borderEffectView]; + } + return self; +} + +- (void)updateAbsoluteRect:(CGRect)absoluteRect containerSize:(CGSize)containerSize { + [_effectView updateAbsoluteRect:absoluteRect containerSize:containerSize]; + [_borderEffectView updateAbsoluteRect:absoluteRect containerSize:containerSize]; +} + +@end + @interface TGVideoMessageRingView () { CGFloat _value; diff --git a/submodules/MediaPlayer/Sources/MediaPlayer.swift b/submodules/MediaPlayer/Sources/MediaPlayer.swift index cea3926c49..c3a4693eb0 100644 --- a/submodules/MediaPlayer/Sources/MediaPlayer.swift +++ b/submodules/MediaPlayer/Sources/MediaPlayer.swift @@ -281,7 +281,6 @@ private final class MediaPlayerContext { CMTimebaseSetRate(loadedState.controlTimebase.timebase, rate: 0.0) } } - let currentTimestamp = CMTimeGetSeconds(CMTimebaseGetTime(loadedState.controlTimebase.timebase)) var duration: Double = 0.0 if let videoTrackFrameBuffer = loadedState.mediaBuffers.videoBuffer { duration = max(duration, CMTimeGetSeconds(videoTrackFrameBuffer.duration)) diff --git a/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift b/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift index 404531adbf..91a1f10153 100644 --- a/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift +++ b/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift @@ -200,8 +200,12 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele self.hapticFeedback.impact(.light) } } - self.seekTo?(min(0.99, fraction), false) - self.seekingProgress = CGFloat(fraction) + let newProgress = min(0.99, fraction) + if let seekingProgress = self.seekingProgress, abs(seekingProgress - CGFloat(newProgress)) < 0.005 { + } else { + self.seekTo?(newProgress, false) + self.seekingProgress = CGFloat(fraction) + } case .ended, .cancelled: self.seeking = false self.seekTo?(min(0.99, fraction), true) diff --git a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift index fd01167f40..ae4ddfbff1 100644 --- a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift +++ b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift @@ -720,21 +720,23 @@ public func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttribu let entity = string.substring(with: match.range(at: 7)) let substring = string.substring(with: match.range(at: 6)) + text + string.substring(with: match.range(at: 9)) + let textInputAttribute: NSAttributedString.Key? switch entity { case "`": - result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.monospace: true as NSNumber])) - offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2)) + textInputAttribute = ChatTextInputAttributes.monospace case "**": - result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.bold: true as NSNumber])) - offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2)) + textInputAttribute = ChatTextInputAttributes.bold case "__": - result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.italic: true as NSNumber])) - offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2)) + textInputAttribute = ChatTextInputAttributes.italic case "~~": - result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.strikethrough: true as NSNumber])) - offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2)) + textInputAttribute = ChatTextInputAttributes.strikethrough default: - break + textInputAttribute = nil + } + + if let textInputAttribute = textInputAttribute { + result.append(NSAttributedString(string: substring, attributes: [textInputAttribute: true as NSNumber])) + offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2)) } stringOffset -= match.range(at: 7).length * 2 From 5d1a535f7b1d4e49d13ab2e958dc1966a66c468e Mon Sep 17 00:00:00 2001 From: Ali <> Date: Wed, 4 Aug 2021 19:41:32 +0200 Subject: [PATCH 04/18] Update tgcalls --- submodules/TgVoipWebrtc/tgcalls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index 818c174b37..6d3df88659 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit 818c174b37c9c9b6d267e401d0198fa7b1dc1dde +Subproject commit 6d3df8865963a56329c69d610e0e5075907cecac From ac52fa7c400b612beae0729db37aeee426f603d5 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 4 Aug 2021 21:32:42 +0300 Subject: [PATCH 05/18] Cherry-pick various improvements --- .../Sources/PeerSelectionController.swift | 4 +- .../Sources/ChatListSearchListPaneNode.swift | 63 +++-- .../Items/UniversalVideoGalleryItem.swift | 9 +- .../LegacyComponents/TGCameraController.h | 2 +- .../CameraPlaceholder.jpg | Bin 0 -> 13897 bytes .../Sources/TGAttachmentCameraView.m | 3 + .../Sources/TGCameraController.m | 46 ++- .../Sources/TGCameraPreviewView.m | 22 +- .../Sources/TGMediaAvatarMenuMixin.m | 2 +- .../Sources/TGPhotoPaintController.m | 3 +- .../TelegramInitializeLegacyComponents.swift | 2 +- submodules/ShareController/BUILD | 1 + .../Sources/ShareControllerPeerGridItem.swift | 78 +++++- .../Sources/ShareSearchContainerNode.swift | 50 ++-- .../Sources/ManagedAudioSession.swift | 10 +- ...ediaNavigationAccessoryContainerNode.swift | 2 +- .../MediaNavigationAccessoryHeaderNode.swift | 265 ++++++++++++++++-- .../MediaNavigationAccessoryPanel.swift | 24 +- .../Sources/TelegramBaseController.swift | 61 ++-- .../Sources/VoiceChatController.swift | 4 +- .../TelegramUI/Sources/ChatController.swift | 22 +- .../ChatMessageAnimatedStickerItemNode.swift | 6 +- .../ChatMessageInstantVideoItemNode.swift | 46 +-- .../Sources/ChatMessageStickerItemNode.swift | 2 +- .../InstantVideoRadialStatusNode.swift | 74 ++++- .../PeerInfo/Panes/PeerInfoListPaneNode.swift | 61 ++-- .../Sources/PeerSelectionController.swift | 4 +- .../Sources/PeerSelectionControllerNode.swift | 20 +- 28 files changed, 678 insertions(+), 208 deletions(-) create mode 100644 submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/CameraPlaceholder.jpg diff --git a/submodules/AccountContext/Sources/PeerSelectionController.swift b/submodules/AccountContext/Sources/PeerSelectionController.swift index 34a9371976..e4f30c083b 100644 --- a/submodules/AccountContext/Sources/PeerSelectionController.swift +++ b/submodules/AccountContext/Sources/PeerSelectionController.swift @@ -40,8 +40,9 @@ public final class PeerSelectionControllerParams { public let createNewGroup: (() -> Void)? public let pretendPresentedInModal: Bool public let multipleSelection: Bool + public let forwardedMessagesCount: Int - public init(context: AccountContext, filter: ChatListNodePeersFilter = [.onlyWriteable], hasChatListSelector: Bool = true, hasContactSelector: Bool = true, hasGlobalSearch: Bool = true, title: String? = nil, attemptSelection: ((Peer) -> Void)? = nil, createNewGroup: (() -> Void)? = nil, pretendPresentedInModal: Bool = false, multipleSelection: Bool = false) { + public init(context: AccountContext, filter: ChatListNodePeersFilter = [.onlyWriteable], hasChatListSelector: Bool = true, hasContactSelector: Bool = true, hasGlobalSearch: Bool = true, title: String? = nil, attemptSelection: ((Peer) -> Void)? = nil, createNewGroup: (() -> Void)? = nil, pretendPresentedInModal: Bool = false, multipleSelection: Bool = false, forwardedMessagesCount: Int = 0) { self.context = context self.filter = filter self.hasChatListSelector = hasChatListSelector @@ -52,6 +53,7 @@ public final class PeerSelectionControllerParams { self.createNewGroup = createNewGroup self.pretendPresentedInModal = pretendPresentedInModal self.multipleSelection = multipleSelection + self.forwardedMessagesCount = forwardedMessagesCount } } diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index b227158f6a..2342d7bce9 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -1727,32 +1727,28 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context, displayBackground: true) mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo + mediaAccessoryPanel.getController = { [weak self] in + return self?.navigationController?.topViewController as? ViewController + } + mediaAccessoryPanel.presentInGlobalOverlay = { [weak self] c in + (self?.navigationController?.topViewController as? ViewController)?.presentInGlobalOverlay(c) + } mediaAccessoryPanel.close = { [weak self] in if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType { strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause)) } } - mediaAccessoryPanel.toggleRate = { - [weak self] in + mediaAccessoryPanel.setRate = { [weak self] rate in guard let strongSelf = self else { return } let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings) as? MusicPlaybackSettings ?? MusicPlaybackSettings.defaultSettings - - let nextRate: AudioPlaybackRate - switch settings.voicePlaybackRate { - case .x1: - nextRate = .x2 - case .x2: - nextRate = .x1 - default: - nextRate = .x1 - } + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in - return settings.withUpdatedVoicePlaybackRate(nextRate) + return settings.withUpdatedVoicePlaybackRate(rate) }) - return nextRate + return rate } |> deliverOnMainQueue).start(next: { baseRate in guard let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType else { @@ -1771,22 +1767,31 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { }) let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let slowdown = baseRate == .x1 - controller.present( - UndoOverlayController( - presentationData: presentationData, - content: .audioRate( - slowdown: slowdown, - text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp + let slowdown: Bool? + if baseRate == .x1 { + slowdown = true + } else if baseRate == .x2 { + slowdown = false + } else { + slowdown = nil + } + if let slowdown = slowdown { + controller.present( + UndoOverlayController( + presentationData: presentationData, + content: .audioRate( + slowdown: slowdown, + text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp + ), + elevatedLayout: false, + animateInAsReplacement: hasTooltip, + action: { action in + return true + } ), - elevatedLayout: false, - animateInAsReplacement: hasTooltip, - action: { action in - return true - } - ), - in: .current - ) + in: .current + ) + } } }) } diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 62880ca214..56578535cd 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -524,6 +524,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { private let isInteractingPromise = ValuePromise(false, ignoreRepeated: true) private let controlsVisiblePromise = ValuePromise(true, ignoreRepeated: true) private let isShowingContextMenuPromise = ValuePromise(false, ignoreRepeated: true) + private let hasExpandedCaptionPromise = ValuePromise(false, ignoreRepeated: true) private var hideControlsDisposable: Disposable? var playbackCompleted: (() -> Void)? @@ -701,12 +702,12 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { self.titleContentView = GalleryTitleView(frame: CGRect()) self._titleView.set(.single(self.titleContentView)) - let shouldHideControlsSignal: Signal = combineLatest(self.isPlayingPromise.get(), self.isInteractingPromise.get(), self.controlsVisiblePromise.get(), self.isShowingContextMenuPromise.get()) - |> mapToSignal { isPlaying, isIntracting, controlsVisible, isShowingContextMenu -> Signal in - if isShowingContextMenu { + let shouldHideControlsSignal: Signal = combineLatest(self.isPlayingPromise.get(), self.isInteractingPromise.get(), self.controlsVisiblePromise.get(), self.isShowingContextMenuPromise.get(), self.hasExpandedCaptionPromise.get()) + |> mapToSignal { isPlaying, isInteracting, controlsVisible, isShowingContextMenu, hasExpandedCaptionPromise -> Signal in + if isShowingContextMenu || hasExpandedCaptionPromise { return .complete() } - if isPlaying && !isIntracting && controlsVisible { + if isPlaying && !isInteracting && controlsVisible { return .single(Void()) |> delay(4.0, queue: Queue.mainQueue()) } else { diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraController.h index da8b0b9bf3..c08deb5ed3 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraController.h @@ -76,6 +76,6 @@ typedef enum { + (UIInterfaceOrientation)_interfaceOrientationForDeviceOrientation:(UIDeviceOrientation)orientation; -+ (bool)useLegacyCamera; ++ (UIImage *)startImage; @end diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/CameraPlaceholder.jpg b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/CameraPlaceholder.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d7d326fa0ecd3db0e318a352497817a4eecd0072 GIT binary patch literal 13897 zcmeHNd0bQ1p1(=h!@jCejgeKz3JHNHDys$)1i~s<$#MgMKtd8KQr=W>DIi+*O<$iP zVimQ5$b;4u0R^S4;DSr7AlTNn7OG6OZ)P&*-YlT?y?Hb5GxN`QlY8&){Lb(E&iD5_ zzjN+6_a60_dK_O8o-YwY5SI%rhakuVvco_S4p12USKxxt&{h`!jYVlB?-z`bh7Vx@ z-wL@xM&Nvbu|{cIK>r8ERx1M(VjQ$ID2-VJ8G?L-YmIT%(+e=3^K{ZsJxhTM0YgV= zdq4+cR_OF&;xQqRfkqD&!i3JzSi8A;Ka}YBn2ZuP9gX`Df(*U`wt`=ejS5{St)@#5Fpg~ntbM$_lEi@{1zc#kiY6#ko z)wK;Yy&pG6H#=$T0ugplgCB$>1hr^l5h0-#oK^;*4Yl-YKn^Yr`p1BSQvc9@7sQtH0Ip!QBL$NZ&C>?tde4 z2?D+_i=a|SKwJ}q{A^gkC-{>Y+1Ug!g-)h}k`QD8YD4-gHVR2;+8^{xgFV0lC?5r7 zfYycZTLFdOL;VeUI)VYv2Il}0RDKSpYj1>i1q-Hb&(C4%}0O79KHoXo5w-E5k?m; zAOhybB17ifSY)_z{#b;?=;F~Z0;`Wxqd;w4Yr!4CiQd&?>YpHUbgbAE%7rlMr;sI@ zf<#*pIrTkgJ7jKRVq$7yZfa_7gEzz5I9QpRTRAMSvvaVsTVP|39@?8G@%j~GfyY}| zT3B0JTH9M%TG}HQOM6Wbn}0-s`T}HQ0_}(P<1j=BYlFerVANj&BhA!(m{kC!AqG7F zVC2Aok>9}3$k@cx43Bv|6T}ftmuU@Q3@|vX0oKsi$ixt5?h7()a0W|lX@(jojf^kK0RVdFvsPvn0GeQ6uyEA%6 z0EmN5qVRa83ZBqI_}d6P<{+r=LgqNMP8(Up!B zgPU!u#ZpJNu$rfyQ3@t@+$eTmfJqNwlGXdv;NcMi#~zy#5f?Y4Rd{T_$Z@Yd;~f%x z$TY<9V@GO@S4ebi+m3QKv*;SP%yFUVfhbU;i_cO|-u46DiFoz(=go00JSWe9!N(jT zo^>E#@WG^0&l;~!ZBl!v1vZL3&mS=Ele*42?1^i7E)H7hyFu`^u(IhW?a3vVKle+; zUa{MYt0FNOTr+k>ybmz6xu(V=s!CGsv3>eu>(1%(>XGLb-|wmjFLnClMQ!ir_qYD) z)pLcS;8#6d&@<$GBAKbJ@^|j%w>Ih><NvLw&U^KA##t6>w+%sksU`Hdnn0& zDCO@e+)zSF?wz7-Zc2?jygKFapKm>j3sMa{@2*|1zHuKeDeeyk;2mOzoZoHU_VKnS zo_p;glY_pa6r=~4ei7_l-|DzD;ED4HFSqXC?aVK-OOBn!r@oVrT1HMTKW)G9aqWrL zm!A)>zJJuY#$xcov5M@y)dfS9J^sf&#xGPE4u#C_Bd`DAVAjSZn})+Cw;Wx3zNz&5 zuzS=KCl{M>XQYY>D4hNX`c!1L_*VT%b zUDh>4cs|n2?!Fgec(kIR{*zkrMD}k(?o)Ahn}6UHkGc<@UUMtn;>g)}g3zt!2ank^ zH!1O}j#k~vDHw9U$-Bvs1{<7L-#X?K>$tqQpLsUUE!|EUv^FRq>&B&O_Ilet4!I3s z1|uC$xY_QHRK%IGT_W9>Q?_xiTkfF;e=e|`vVFdK<3PjMerX6Lni7SHkZ{XZHk%gg zc|LW9`w?%L)t>O6WpPn>m;d1hTU}br9NRun-|n{x%etA+<8UJGszq1RWJFIwR^_6} z-ZrVRn`iLguY_IV$Re*@HSBQ5p5+EDkwu=(x5+VNPGpfnzTS3f$Z&914ZgZKTQPaB zhRyXF>f#RtUs-(w+Y~R^R&z1fkYnEqYWeE%rOFLcsR1^imW>^^iHxCBsU^4FYf{nz zR!&+^{9&~V28RLhu-3MTXWJCRJ!QDo zo*GxgUEaC>Nbz>F2Cp3y^&Rz3RvB}%OJ6b-Vui4|2FMH-f?Qp|qYeCBu>OzgE%7f&H zD#%Q(mj)?RO^8&LqeWyc$dgD_k{nbe$Tvl$L%GT8G~W9Iq5?|-mpO}V=UKd7u9C=d zlpx;;kqniqa&$%jBLxb5A~s#g)h9v~IiYij9MxQcBakZfIZ^4V_wmfA|e zZPivt6K@tM&p@qK_o0#tHED2NjB0&`rcKc+xM0vyV2z0!X4*~y0v45>MY(GsidYPC zq~MCIu#rL<&Vj}zJqMZ9W1tP-s*_=$93=9!98~8EkcN<+1rWaBzo;Uh50(?i1(jWdP`Ge#(V$0?5HzhlO&~VJh1NiuAUfm=`GZqC zG9W_p4sCZrIl8j*vfo??X$)<_oIEKqo*}CWBY`Y0N2GkMp9)oA;i!R)1h+nd0Q0p7 zghJ*b6@dc9&IKZpHd0C@LRgs~AOS)TkAmXS;$ zNtftH$#5Q`eLczpIxP5kD%l!12doTLFtVhA;jF`6{+@Z5)@Q8?a1t>{oJc~C^IP-%6`W+7obDm@A!Do#2K%Pqdm$KsR(^_@RC;s3BV$d0G5RrfL4K1iBfVPX8&JE5Wu963QlBZ zrhTeu=8(qdL=M!V9=E!dp;E~MDU=)~nU8$eAq!>MlzhINLL*Zt(27<0a_|lvRuKd+ zm_-EPMmZk*L%_-+(|rKZ5okg6b3^abRp%gtjt}3L)5gwG zs`xoV7|{mwlO;fth$aw-Sxh0xpDqX>`B8oSNPH%ZPT~vw0{9|-roT|^rz@{n*YTzE zQPIOgBErJM!kA1ZD}olv0;E49!Y?E&oW}GIU_=lJB4MCdrpV@l*q3DU(_u=c98O0@ zg*hX13WEVSB?w)mcGL;sr)w#xWTj?^d)K?WS4f}o)^YAJueeoX_cl^}SwXi@OsTsA}aas!6KrT zDS3mVeFNxzd|#%BB%;AWk{^RbBME3U7D))x`7jOi1R5(qSAH{i8TGr^n%|)`bt`3J z)n>i|4o(Llq8sX?btBpDHV){t3p4mR=`aXh3X$?ZHs>FU8x79?Q*+R^)-)95^)^KF z<~@jX0Rf(F2&sRruGBsL-rnDO;H?MVdf=@G-g@Az2mT*^D|>q70b>Wv4ySQnN5QnKqIkwF)Q?xy+#yuxh!rSid|mxQiyfQ!n=lt;l~T}k2C7`k{fKe;;XG2X&X{FH!kxSw9|(=hdFt^ zJZHcddm?UScK(I=2d4KrCGVZR_ObWJejGbaYRwcLX=*sWO8wQ-g-K6fapCE32bcQ`i&Zp~w$wUCDQv}Ov6tP5Kc~J^%DL>K-N&h#(AM zhK9kQbxt4GxZ^IeW$dZzBleKTqNt5I{&-HVdw_JT+NHF5sl&0cYGz6oak#e8*GAfv z=)>vq{Wx@Yy%~WSv}?(>-Nr-%xGOquObI zpOY`bDnb~WoYq`0d-2&y4!hK}Y^6y=?~88?BVMLeJbo>dO-@ zCpxq~6AztO{^PO!_A@7&>%{|#;j4?nDOU$}UdEM;ZaD>)n>V#?^cD9b``d2(Q20!F zVS;mR%NkorT$lUsZc4fMf@r8o+_$m)+So3aQm;ehT9;LHj{HY5e+K(9b-K87S7QGN z2kH%Hc1uzcrracwv3;X5zyywJ>% zR~y@oxF2LhEPW!LrA=_b=CtpLocuzA4nff~+j|m+FBhfQd|Q)Zc$^G?cid@rlsYzT z_^IR+{JFsVj`KRW{mcwg$}fz!b?vw17%W-bAu0+-P(P|Siv_5Ayu;nE0~@N%g!P85 z-TC!9e3NOx?oP+L_t)f)PrPd{JF=UewkdHD-0RxcZz~#DDEF#g01xI8wr^@{U~fIU zsj!{w9KNitd$tN{tL%#1n%380!)#TdHF}UM< zcb`ui;GRWGXocpkSHG?FicjxroGE{?ccnqkJ!|o`FH1g8OZ1(9XO4|k7RIN1%wiX< zdu%JrY;f%+2FTk*{UfQ_62_iX`>OGfb;Yr%`Hiuerv%NZKFRIDOyIgTzjcq2akFP< zTBQ}9OC4c=d1<{JeX=Mm#Q)|sw_h);{PdhG%-Gvzg)$#iL35VVt*ay66wGq*UJSB=IlMQ zbT`jp0G%irsK3&Y`X%tZ?*wPep-al%yJ)!dl%Q?b<8Mpq2A=iDtaJCNN`qcB4;EE$ zs#2}1#y7BcwRDpzIJZ)xxj*l?N?x(<=iP^vJo)P*&bb#>HwPLOtJ^(Kk*QaU%+I~J zL{3^XE3S^i2JS7UoHN$tdwd}7|m{gD?Bf{|KY|30cZ1(N}uC)PlW3x7PrO}mSxACb001z z?IsmvUmM%Y6pZb4Nw}Y~AZ1aMUzgYEsHEu^&UHzJhJ=HToMh33r8D8x#A|F9x68kE z8vq~B*4z5t9HKv5&szi!?sRP~@4iGfq(3rmq+X~=uJ^uq;a^%4+p5wc{cm2Km0Pvd zKN$}{cvkvp$~TyT#gySW`GrQVmyNH{x5gB6%Je0pIJn4S32X^dxDJB)=?-iD|UyGvc@y@m@_wd=r*_T-Q+2g5lkLBHVeaD<# zmRL{KIpdbPt}Q&z!-0Ng;ZbTW8fc_pU-Z5tY;#k(QwKpe+{tN36P3_~f8>~phGwbF z*Z-we#=|PFep^Oz0-cUZS#d7kCgbBR;WL&1L1&73#M^3*RT1vdrN^dOok;+!*%JH2 zD>ZjrnJbva79UwurX)-?bvH;qdwBYEpJp&-c3o%nDh3DacDO`=v00GYGI54`n~_^P zG*y;g8vr}?T@|hq73bIDAK@q9_8lF;E|EKCcFfSndhZarj&Ab)n3&>SXTRb6ijyyv z)Fnv`v#poBa}@{1h%2?H)5e}Al}Pgx%v~R-M|XdCxh*Yvthp=Aac{V{;OLk`ibKMc z+Rn%>PiI`4ps2$;^WoGleMOfOv3Gxa#2&cqxi2QW8u1>V)qFpSy00U5`*=!D_lTl& zz^)3PT{loSP`%s3)$tywoKyqyE4W=CEh!giKH)*djvaf4tXF}$LF#} za$BbCc37g#xP9DgB6s@~@Sm%#4uV5-Zp&kk%O-m)?`f+oJ8^;~q+h&Ja}=PkTQF0( S{1Z!0OIO;%$;eIWJO2siC@7Ht literal 0 HcmV?d00001 diff --git a/submodules/LegacyComponents/Sources/TGAttachmentCameraView.m b/submodules/LegacyComponents/Sources/TGAttachmentCameraView.m index 30821b674e..3cea35671f 100644 --- a/submodules/LegacyComponents/Sources/TGAttachmentCameraView.m +++ b/submodules/LegacyComponents/Sources/TGAttachmentCameraView.m @@ -4,6 +4,7 @@ #import #import "TGAttachmentMenuCell.h" +#import "TGCameraController.h" #import #import @@ -46,6 +47,8 @@ _camera = camera; _previewView = [[TGCameraPreviewView alloc] initWithFrame:CGRectMake(0, 0, 84.0f, 84.0f)]; + [_previewView fadeInAnimated:false]; + [_previewView beginTransitionWithSnapshotImage:[TGCameraController startImage] animated:false]; [_wrapperView addSubview:_previewView]; [camera attachPreviewView:_previewView]; diff --git a/submodules/LegacyComponents/Sources/TGCameraController.m b/submodules/LegacyComponents/Sources/TGCameraController.m index 0cae82eb74..5258fe8e96 100644 --- a/submodules/LegacyComponents/Sources/TGCameraController.m +++ b/submodules/LegacyComponents/Sources/TGCameraController.m @@ -1102,7 +1102,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus __strong TGCameraController *strongSelf = weakSelf; if (strongSelf == nil) return; - + TGDispatchOnMainThread(^ { strongSelf->_shutterIsBusy = false; @@ -1120,6 +1120,10 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus strongSelf->_camera.disabled = false; } }); + + [[SQueue concurrentDefaultQueue] dispatch:^{ + [TGCameraController generateStartImageWithImage:result]; + }]; }]; } else if (cameraMode == PGCameraModeVideo || cameraMode == PGCameraModeSquareVideo || cameraMode == PGCameraModeSquareSwing) @@ -2246,11 +2250,21 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus _interfaceView.previewViewFrame = toFrame; } ++ (void)generateStartImageWithImage:(UIImage *)frameImage { + CGFloat minSize = MIN(frameImage.size.width, frameImage.size.height); + UIImage *image = TGPhotoEditorCrop(frameImage, nil, UIImageOrientationUp, 0.0f, CGRectMake((frameImage.size.width - minSize) / 2.0f, (frameImage.size.height - minSize) / 2.0f, minSize, minSize), false, CGSizeMake(240.0f, 240.0f), frameImage.size, true); + UIImage *startImage = TGSecretBlurredAttachmentImage(image, image.size, NULL, false, 0); + TGDispatchOnMainThread(^{ + [TGCameraController saveStartImage:startImage]; + }); +} + - (void)beginTransitionOutWithVelocity:(CGFloat)velocity { _dismissing = true; self.view.userInteractionEnabled = false; + _focusControl.active = false; _rectangleView.hidden = true; @@ -2267,6 +2281,11 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus referenceFrame = self.beginTransitionOut(); __weak TGCameraController *weakSelf = self; + [_camera captureNextFrameCompletion:^(UIImage *frameImage) { + [[SQueue concurrentDefaultQueue] dispatch:^{ + [TGCameraController generateStartImageWithImage:frameImage]; + }]; + }]; if (_standalone) { [self simpleTransitionOutWithVelocity:velocity completion:^ @@ -2757,11 +2776,6 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus } } -+ (bool)useLegacyCamera -{ - return false; -} - + (NSArray *)resultSignalsForSelectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext currentItem:(id)currentItem storeAssets:(bool)storeAssets saveEditedPhotos:(bool)saveEditedPhotos descriptionGenerator:(id (^)(id, NSString *, NSArray *, NSString *))descriptionGenerator { NSMutableArray *signals = [[NSMutableArray alloc] init]; @@ -3144,4 +3158,24 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus return value; } +#pragma mark - Start Image + +static UIImage *startImage = nil; + ++ (UIImage *)startImage +{ + if (startImage == nil) + startImage = TGComponentsImageNamed (@"CameraPlaceholder.jpg"); + + return startImage; +} + ++ (void)saveStartImage:(UIImage *)image +{ + if (image == nil) + return; + + startImage = image; +} + @end diff --git a/submodules/LegacyComponents/Sources/TGCameraPreviewView.m b/submodules/LegacyComponents/Sources/TGCameraPreviewView.m index b49624e7a9..abed3a0e18 100644 --- a/submodules/LegacyComponents/Sources/TGCameraPreviewView.m +++ b/submodules/LegacyComponents/Sources/TGCameraPreviewView.m @@ -129,10 +129,15 @@ if (strongSelf == nil) return; - if (resume) + if (resume) { [strongSelf endResetTransitionAnimated:true]; - else - [strongSelf fadeInAnimated:true]; + } else { + if (strongSelf->_snapshotView != nil) { + [strongSelf endTransitionAnimated:true]; + } else { + [strongSelf fadeInAnimated:true]; + } + } }; camera.captureStopped = ^(bool pause) @@ -262,6 +267,10 @@ } } +- (bool)hasTransitionSnapshot { + return _snapshotView != nil; +} + - (void)beginResetTransitionAnimated:(bool)animated { if (iosMajorVersion() < 7) @@ -319,7 +328,12 @@ if (_snapshotView != nil) { - CGSize size = TGScaleToFill(_snapshotView.frame.size, _wrapperView.frame.size); + CGSize imageSize = _snapshotView.frame.size; + if ([_snapshotView isKindOfClass:[UIImageView class]]) { + imageSize = ((UIImageView *)_snapshotView).image.size; + } + + CGSize size = TGScaleToFill(imageSize, _wrapperView.frame.size); _snapshotView.frame = CGRectMake(floor((self.frame.size.width - size.width) / 2.0f), floor((self.frame.size.height - size.height) / 2.0f), size.width, size.height); } } diff --git a/submodules/LegacyComponents/Sources/TGMediaAvatarMenuMixin.m b/submodules/LegacyComponents/Sources/TGMediaAvatarMenuMixin.m index 12b4f56a8a..c482f564ce 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAvatarMenuMixin.m +++ b/submodules/LegacyComponents/Sources/TGMediaAvatarMenuMixin.m @@ -53,7 +53,7 @@ _hasSearchButton = hasSearchButton; _hasDeleteButton = hasDeleteButton; _hasViewButton = hasViewButton; - _personalPhoto = ![TGCameraController useLegacyCamera] ? personalPhoto : false; + _personalPhoto = personalPhoto; _isVideo = isVideo; _signup = signup; } diff --git a/submodules/LegacyComponents/Sources/TGPhotoPaintController.m b/submodules/LegacyComponents/Sources/TGPhotoPaintController.m index fa503a47a3..dc759ae6d3 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoPaintController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoPaintController.m @@ -1414,7 +1414,8 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f; - (CGFloat)_brushWeightForSize:(CGFloat)size { - return ([self _brushBaseWeightForCurrentPainting] + [self _brushWeightRangeForCurrentPainting] * size) / _scrollView.zoomScale; + CGFloat scale = MAX(0.001, _scrollView.zoomScale); + return ([self _brushBaseWeightForCurrentPainting] + [self _brushWeightRangeForCurrentPainting] * size) / scale; } + (CGSize)maximumPaintingSize diff --git a/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift b/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift index 4016d8ce82..91974df539 100644 --- a/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift +++ b/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift @@ -182,7 +182,7 @@ private final class LegacyComponentsGlobalsProviderImpl: NSObject, LegacyCompone let convertedType: ManagedAudioSessionType switch type { case TGAudioSessionTypePlayAndRecord, TGAudioSessionTypePlayAndRecordHeadphones: - convertedType = .recordWithOthers + convertedType = .record(speaker: false) default: convertedType = .play } diff --git a/submodules/ShareController/BUILD b/submodules/ShareController/BUILD index 44333ca34d..e6fa68bd0b 100644 --- a/submodules/ShareController/BUILD +++ b/submodules/ShareController/BUILD @@ -27,6 +27,7 @@ swift_library( "//submodules/AccountContext:AccountContext", "//submodules/SegmentedControlNode:SegmentedControlNode", "//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode", + "//submodules/ShimmerEffect:ShimmerEffect", ], visibility = [ "//visibility:public", diff --git a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift index 09636a9dc6..9755c0ff9b 100644 --- a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift +++ b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift @@ -10,6 +10,7 @@ import TelegramStringFormatting import SelectablePeerNode import PeerPresenceStatusManager import AccountContext +import ShimmerEffect final class ShareControllerInteraction { var foundPeers: [RenderedPeer] = [] @@ -89,14 +90,14 @@ final class ShareControllerPeerGridItem: GridItem { let context: AccountContext let theme: PresentationTheme let strings: PresentationStrings - let peer: RenderedPeer + let peer: RenderedPeer? let presence: PeerPresence? let controllerInteraction: ShareControllerInteraction let search: Bool let section: GridSection? - init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer, presence: PeerPresence?, controllerInteraction: ShareControllerInteraction, sectionTitle: String? = nil, search: Bool = false) { + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer?, presence: PeerPresence?, controllerInteraction: ShareControllerInteraction, sectionTitle: String? = nil, search: Bool = false) { self.context = context self.theme = theme self.strings = strings @@ -130,12 +131,15 @@ final class ShareControllerPeerGridItem: GridItem { } final class ShareControllerPeerGridItemNode: GridItemNode { - private var currentState: (AccountContext, PresentationTheme, PresentationStrings, RenderedPeer, Bool, PeerPresence?)? + private var currentState: (AccountContext, PresentationTheme, PresentationStrings, RenderedPeer?, Bool, PeerPresence?)? private let peerNode: SelectablePeerNode private var presenceManager: PeerPresenceStatusManager? var controllerInteraction: ShareControllerInteraction? + private var placeholderNode: ShimmerEffectNode? + private var absoluteLocation: (CGRect, CGSize)? + override init() { self.peerNode = SelectablePeerNode() @@ -143,7 +147,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { self.peerNode.toggleSelection = { [weak self] in if let strongSelf = self { - if let (_, _, _, peer, search, _) = strongSelf.currentState { + if let (_, _, _, maybePeer, search, _) = strongSelf.currentState, let peer = maybePeer { if let _ = peer.peers[peer.peerId] { strongSelf.controllerInteraction?.togglePeer(peer, search) } @@ -159,13 +163,21 @@ final class ShareControllerPeerGridItemNode: GridItemNode { }) } - func setup(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer, presence: PeerPresence?, search: Bool, synchronousLoad: Bool, force: Bool) { + override func updateAbsoluteRect(_ absoluteRect: CGRect, within containerSize: CGSize) { + var rect = absoluteRect + self.absoluteLocation = (rect, containerSize) + if let shimmerNode = self.placeholderNode { + shimmerNode.updateAbsoluteRect(rect, within: containerSize) + } + } + + func setup(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer?, presence: PeerPresence?, search: Bool, synchronousLoad: Bool, force: Bool) { if force || self.currentState == nil || self.currentState!.0 !== context || self.currentState!.3 != peer || !arePeerPresencesEqual(self.currentState!.5, presence) { let itemTheme = SelectablePeerNodeTheme(textColor: theme.actionSheet.primaryTextColor, secretTextColor: theme.chatList.secretTitleColor, selectedTextColor: theme.actionSheet.controlAccentColor, checkBackgroundColor: theme.actionSheet.opaqueItemBackgroundColor, checkFillColor: theme.actionSheet.controlAccentColor, checkColor: theme.actionSheet.checkContentColor, avatarPlaceholderColor: theme.list.mediaPlaceholderColor) let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) var online = false - if let peer = peer.peer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != context.account.peerId { + if let peer = peer?.peer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != context.account.peerId { let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: timestamp) if case .online = relativeStatus { online = true @@ -173,7 +185,41 @@ final class ShareControllerPeerGridItemNode: GridItemNode { } self.peerNode.theme = itemTheme - self.peerNode.setup(context: context, theme: theme, strings: strings, peer: peer, online: online, synchronousLoad: synchronousLoad) + + if let peer = peer { + self.peerNode.setup(context: context, theme: theme, strings: strings, peer: EngineRenderedPeer(peer), online: online, synchronousLoad: synchronousLoad) + if let shimmerNode = self.placeholderNode { + self.placeholderNode = nil + shimmerNode.removeFromSupernode() + } + } else { + let shimmerNode: ShimmerEffectNode + if let current = self.placeholderNode { + shimmerNode = current + } else { + shimmerNode = ShimmerEffectNode() + self.placeholderNode = shimmerNode + self.addSubnode(shimmerNode) + } + shimmerNode.frame = self.bounds + if let (rect, size) = self.absoluteLocation { + shimmerNode.updateAbsoluteRect(rect, within: size) + } + + var shapes: [ShimmerEffectNode.Shape] = [] + + let titleLineWidth: CGFloat = 56.0 + let lineDiameter: CGFloat = 10.0 + + let iconFrame = CGRect(x: 13.0, y: 4.0, width: 60.0, height: 60.0) + shapes.append(.circle(iconFrame)) + + let titleFrame = CGRect(x: 15.0, y: 70.0, width: 56.0, height: 10.0) + shapes.append(.roundedRectLine(startPoint: CGPoint(x: titleFrame.minX, y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: titleLineWidth, diameter: lineDiameter)) + + shimmerNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, horizontal: true, size: self.bounds.size) + } +] self.currentState = (context, theme, strings, peer, search, presence) self.setNeedsLayout() if let presence = presence as? TelegramUserPresence { @@ -185,7 +231,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { func updateSelection(animated: Bool) { var selected = false - if let controllerInteraction = self.controllerInteraction, let (_, _, _, peer, _, _) = self.currentState { + if let controllerInteraction = self.controllerInteraction, let (_, _, _, maybePeer, _, _) = self.currentState, let peer = maybePeer { selected = controllerInteraction.selectedPeerIds.contains(peer.peerId) } @@ -197,5 +243,21 @@ final class ShareControllerPeerGridItemNode: GridItemNode { let bounds = self.bounds self.peerNode.frame = bounds + self.placeholderNode?.frame = bounds + + if let (_, theme, _, _, _, _) = self.currentState, let shimmerNode = self.placeholderNode { + var shapes: [ShimmerEffectNode.Shape] = [] + + let titleLineWidth: CGFloat = 56.0 + let lineDiameter: CGFloat = 10.0 + + let iconFrame = CGRect(x: (bounds.width - 60.0) / 2.0, y: 4.0, width: 60.0, height: 60.0) + shapes.append(.circle(iconFrame)) + + let titleFrame = CGRect(x: (bounds.width - titleLineWidth) / 2.0, y: 70.0, width: titleLineWidth, height: 10.0) + shapes.append(.roundedRectLine(startPoint: CGPoint(x: titleFrame.minX, y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: titleLineWidth, diameter: lineDiameter)) + + shimmerNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, horizontal: true, size: self.bounds.size) + } } } diff --git a/submodules/ShareController/Sources/ShareSearchContainerNode.swift b/submodules/ShareController/Sources/ShareSearchContainerNode.swift index 6580453523..e0b71149d6 100644 --- a/submodules/ShareController/Sources/ShareSearchContainerNode.swift +++ b/submodules/ShareController/Sources/ShareSearchContainerNode.swift @@ -110,13 +110,17 @@ private enum ShareSearchRecentEntry: Comparable, Identifiable { private struct ShareSearchPeerEntry: Comparable, Identifiable { let index: Int32 - let peer: RenderedPeer + let peer: RenderedPeer? let presence: PeerPresence? let theme: PresentationTheme let strings: PresentationStrings var stableId: Int64 { - return self.peer.peerId.toInt64() + if let peer = self.peer { + return peer.peerId.toInt64() + } else { + return Int64(index) + } } static func ==(lhs: ShareSearchPeerEntry, rhs: ShareSearchPeerEntry) -> Bool { @@ -134,7 +138,7 @@ private struct ShareSearchPeerEntry: Comparable, Identifiable { } func item(context: AccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem { - return ShareControllerPeerGridItem(context: context, theme: self.theme, strings: self.strings, peer: peer, presence: self.presence, controllerInteraction: interfaceInteraction, search: true) + return ShareControllerPeerGridItem(context: context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, controllerInteraction: interfaceInteraction, search: true) } } @@ -246,10 +250,13 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { if !query.isEmpty { let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) |> take(1) let foundLocalPeers = context.account.postbox.searchPeers(query: query.lowercased()) - let foundRemotePeers: Signal<([FoundPeer], [FoundPeer]), NoError> = .single(([], [])) + let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError> = .single(([], [], true)) |> then( context.engine.peers.searchPeers(query: query) |> delay(0.2, queue: Queue.concurrentDefaultQueue()) + |> map { a, b -> ([FoundPeer], [FoundPeer], Bool) in + return (a, b, false) + } ) return combineLatest(accountPeer, foundLocalPeers, foundRemotePeers) @@ -278,21 +285,28 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { } } - for foundPeer in foundRemotePeers.0 { - let peer = foundPeer.peer - if !existingPeerIds.contains(peer.id) && canSendMessagesToPeer(peer) { - existingPeerIds.insert(peer.id) - entries.append(ShareSearchPeerEntry(index: index, peer: RenderedPeer(peer: foundPeer.peer), presence: nil, theme: theme, strings: strings)) + if false, foundRemotePeers.2 { + for _ in 0 ..< 4 { + entries.append(ShareSearchPeerEntry(index: index, peer: nil, presence: nil, theme: theme, strings: strings)) index += 1 } - } - - for foundPeer in foundRemotePeers.1 { - let peer = foundPeer.peer - if !existingPeerIds.contains(peer.id) && canSendMessagesToPeer(peer) { - existingPeerIds.insert(peer.id) - entries.append(ShareSearchPeerEntry(index: index, peer: RenderedPeer(peer: peer), presence: nil, theme: theme, strings: strings)) - index += 1 + } else { + for foundPeer in foundRemotePeers.0 { + let peer = foundPeer.peer + if !existingPeerIds.contains(peer.id) && canSendMessagesToPeer(peer) { + existingPeerIds.insert(peer.id) + entries.append(ShareSearchPeerEntry(index: index, peer: RenderedPeer(peer: foundPeer.peer), presence: nil, theme: theme, strings: strings)) + index += 1 + } + } + + for foundPeer in foundRemotePeers.1 { + let peer = foundPeer.peer + if !existingPeerIds.contains(peer.id) && canSendMessagesToPeer(peer) { + existingPeerIds.insert(peer.id) + entries.append(ShareSearchPeerEntry(index: index, peer: RenderedPeer(peer: peer), presence: nil, theme: theme, strings: strings)) + index += 1 + } } } @@ -436,7 +450,7 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { var scrollToItem: GridNodeScrollToItem? if !self.contentGridNode.isHidden, let ensurePeerVisibleOnLayout = self.ensurePeerVisibleOnLayout { self.ensurePeerVisibleOnLayout = nil - if let index = self.entries.firstIndex(where: { $0.peer.peerId == ensurePeerVisibleOnLayout }) { + if let index = self.entries.firstIndex(where: { $0.peer?.peerId == ensurePeerVisibleOnLayout }) { scrollToItem = GridNodeScrollToItem(index: index, position: .visible, transition: transition, directionHint: .up, adjustForSection: false) } } diff --git a/submodules/TelegramAudio/Sources/ManagedAudioSession.swift b/submodules/TelegramAudio/Sources/ManagedAudioSession.swift index f9cd5d88a9..c1f5311f2a 100644 --- a/submodules/TelegramAudio/Sources/ManagedAudioSession.swift +++ b/submodules/TelegramAudio/Sources/ManagedAudioSession.swift @@ -39,7 +39,7 @@ private func nativeCategoryForType(_ type: ManagedAudioSessionType, headphones: return .ambient case .play: return .playback - case .record, .recordWithOthers, .voiceCall, .videoCall: + case .record, .recordWithOthers, .voiceCall, .videoCall: return .playAndRecord case .playWithPossiblePortOverride: if headphones { @@ -568,7 +568,7 @@ public final class ManagedAudioSession { index += 1 } - let lastIsRecordWithOthers = self.holders.last?.audioSessionType == .recordWithOthers + let lastIsRecordWithOthers = false // self.holders.last?.audioSessionType == .recordWithOthers if !deactivating { if let activeIndex = activeIndex { var deactivate = false @@ -745,9 +745,9 @@ public final class ManagedAudioSession { case .videoCall: mode = .videoChat options.insert(.mixWithOthers) - case .recordWithOthers: - mode = .videoRecording - options.insert(.mixWithOthers) +// case .recordWithOthers: +// mode = .videoRecording +// options.insert(.mixWithOthers) default: mode = .default } diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryContainerNode.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryContainerNode.swift index ba6070ab82..db322c564d 100644 --- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryContainerNode.swift +++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryContainerNode.swift @@ -24,7 +24,7 @@ public final class MediaNavigationAccessoryContainerNode: ASDisplayNode, UIGestu self.backgroundNode = ASDisplayNode() self.separatorNode = ASDisplayNode() - self.headerNode = MediaNavigationAccessoryHeaderNode(presentationData: self.presentationData) + self.headerNode = MediaNavigationAccessoryHeaderNode(context: context) super.init() diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift index f7cbb240a7..f40dead068 100644 --- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift +++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift @@ -10,6 +10,7 @@ import UniversalMediaPlayer import AccountContext import TelegramStringFormatting import ManagedAnimationNode +import ContextUI private let titleFont = Font.regular(12.0) private let subtitleFont = Font.regular(10.0) @@ -130,6 +131,7 @@ private func generateMaskImage(color: UIColor) -> UIImage? { public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollViewDelegate { public static let minimizedHeight: CGFloat = 37.0 + private let context: AccountContext private var theme: PresentationTheme private var strings: PresentationStrings private var dateTimeFormat: PresentationDateTimeFormat @@ -148,7 +150,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi private let closeButton: HighlightableButtonNode private let actionButton: HighlightTrackingButtonNode private let playPauseIconNode: PlayPauseIconNode - private let rateButton: HighlightableButtonNode + private let rateButton: RateButton private let accessibilityAreaNode: AccessibilityAreaNode private let scrubbingNode: MediaPlayerScrubbingNode @@ -167,24 +169,31 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi public var tapAction: (() -> Void)? public var close: (() -> Void)? - public var toggleRate: (() -> Void)? + public var setRate: ((AudioPlaybackRate) -> Void)? public var togglePlayPause: (() -> Void)? public var playPrevious: (() -> Void)? public var playNext: (() -> Void)? + public var getController: (() -> ViewController?)? + public var presentInGlobalOverlay: ((ViewController) -> Void)? + public var playbackBaseRate: AudioPlaybackRate? = nil { didSet { guard self.playbackBaseRate != oldValue, let playbackBaseRate = self.playbackBaseRate else { return } switch playbackBaseRate { + case .x0_5: + self.rateButton.setContent(.image(optionsRateImage(rate: "0.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x1: - self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateInactiveIcon(self.theme), for: []) + self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.controlColor))) self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateNormal self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange + case .x1_5: + self.rateButton.setContent(.image(optionsRateImage(rate: "1.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x2: - self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateActiveIcon(self.theme), for: []) + self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.accentTextColor))) self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateFast self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange @@ -208,7 +217,9 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi } } - public init(presentationData: PresentationData) { + public init(context: AccountContext) { + self.context = context + let presentationData = context.sharedContext.currentPresentationData.with { $0 } self.theme = presentationData.theme self.strings = presentationData.strings self.dateTimeFormat = presentationData.dateTimeFormat @@ -236,7 +247,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi self.closeButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 2.0) self.closeButton.displaysAsynchronously = false - self.rateButton = HighlightableButtonNode() + self.rateButton = RateButton() self.rateButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -4.0, bottom: -8.0, right: -4.0) self.rateButton.displaysAsynchronously = false @@ -265,9 +276,6 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi self.scrollNode.addSubnode(self.previousItemNode) self.scrollNode.addSubnode(self.nextItemNode) - //self.addSubnode(self.leftMaskNode) - //self.addSubnode(self.rightMaskNode) - self.addSubnode(self.closeButton) self.addSubnode(self.rateButton) self.addSubnode(self.accessibilityAreaNode) @@ -276,9 +284,13 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi self.addSubnode(self.actionButton) self.closeButton.addTarget(self, action: #selector(self.closeButtonPressed), forControlEvents: .touchUpInside) - self.rateButton.addTarget(self, action: #selector(self.rateButtonPressed), forControlEvents: .touchUpInside) self.actionButton.addTarget(self, action: #selector(self.actionButtonPressed), forControlEvents: .touchUpInside) + self.rateButton.addTarget(self, action: #selector(self.rateButtonPressed), forControlEvents: .touchUpInside) + self.rateButton.contextAction = { [weak self] sourceNode, gesture in + self?.openRateMenu(sourceNode: sourceNode, gesture: gesture) + } + self.addSubnode(self.scrubbingNode) self.addSubnode(self.separatorNode) @@ -300,13 +312,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi return } if let status = status { - let baseRate: AudioPlaybackRate - if status.baseRate.isEqual(to: 1.0) { - baseRate = .x1 - } else { - baseRate = .x2 - } - strongSelf.playbackBaseRate = baseRate + strongSelf.playbackBaseRate = AudioPlaybackRate(status.baseRate) } else { strongSelf.playbackBaseRate = .x1 } @@ -365,10 +371,14 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi if let playbackBaseRate = self.playbackBaseRate { switch playbackBaseRate { + case .x0_5: + self.rateButton.setContent(.image(optionsRateImage(rate: "0.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x1: - self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateInactiveIcon(self.theme), for: []) + self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.controlColor))) + case .x1_5: + self.rateButton.setContent(.image(optionsRateImage(rate: "1.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x2: - self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateActiveIcon(self.theme), for: []) + self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.accentTextColor))) default: break } @@ -475,8 +485,8 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi let bounds = CGRect(origin: CGPoint(), size: size) let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0)) transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 44.0 - rightInset, y: 0.0), size: CGSize(width: 44.0, height: minHeight))) - let rateButtonSize = CGSize(width: 24.0, height: minHeight) - transition.updateFrame(node: self.rateButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 18.0 - closeButtonSize.width - 17.0 - rateButtonSize.width - rightInset, y: 0.0), size: rateButtonSize)) + let rateButtonSize = CGSize(width: 30.0, height: minHeight) + transition.updateFrame(node: self.rateButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 33.0 - closeButtonSize.width - rateButtonSize.width - rightInset, y: -4.0), size: rateButtonSize)) transition.updateFrame(node: self.playPauseIconNode, frame: CGRect(origin: CGPoint(x: 6.0, y: 4.0 + UIScreenPixel), size: CGSize(width: 28.0, height: 28.0))) transition.updateFrame(node: self.actionButton, frame: CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: 40.0, height: 37.0))) transition.updateFrame(node: self.scrubbingNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 37.0 - 2.0), size: CGSize(width: size.width, height: 2.0))) @@ -491,7 +501,58 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi } @objc public func rateButtonPressed() { - self.toggleRate?() + let nextRate: AudioPlaybackRate + if let rate = self.playbackBaseRate { + switch rate { + case .x1: + nextRate = .x2 + default: + nextRate = .x1 + } + } else { + nextRate = .x2 + } + self.setRate?(nextRate) + } + + private func speedList(strings: PresentationStrings) -> [(String, String, AudioPlaybackRate)] { + let speedList: [(String, String, AudioPlaybackRate)] = [ + ("0.5x", "0.5x", .x0_5), + (strings.PlaybackSpeed_Normal, "1x", .x1), + ("1.5x", "1.5x", .x1_5), + ("2x", "2x", .x2) + ] + return speedList + } + + private func contextMenuSpeedItems() -> Signal<[ContextMenuItem], NoError> { + var items: [ContextMenuItem] = [] + + for (text, _, rate) in self.speedList(strings: self.strings) { + let isSelected = self.playbackBaseRate == rate + items.append(.action(ContextMenuActionItem(text: text, icon: { theme in + if isSelected { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + } else { + return nil + } + }, action: { [weak self] _, f in + f(.default) + + self?.setRate?(rate) + }))) + } + + return .single(items) + } + + private func openRateMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) { + guard let controller = self.getController?() else { + return + } + let items: Signal<[ContextMenuItem], NoError> = self.contextMenuSpeedItems() + let contextController = ContextController(account: self.context.account, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode)), items: items, reactionItems: [], gesture: gesture) + self.presentInGlobalOverlay?(contextController) } @objc public func actionButtonPressed() { @@ -554,3 +615,163 @@ private final class PlayPauseIconNode: ManagedAnimationNode { } } } + +private func optionsRateImage(rate: String, color: UIColor = .white) -> UIImage? { + return generateImage(CGSize(width: 30.0, height: 16.0), rotatedContext: { size, context in + UIGraphicsPushContext(context) + + context.clear(CGRect(origin: CGPoint(), size: size)) + + let lineWidth = 1.0 + UIScreenPixel + context.setLineWidth(lineWidth) + context.setStrokeColor(color.cgColor) + + + let string = NSMutableAttributedString(string: rate, font: Font.with(size: 11.0, design: .round, weight: .bold), textColor: color) + + var offset = CGPoint(x: 1.0, y: 0.0) + var width: CGFloat + if rate.count >= 3 { + if rate == "0.5X" { + string.addAttribute(.kern, value: -0.8 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) + offset.x += -0.5 + } else { + string.addAttribute(.kern, value: -0.5 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) + offset.x += -0.3 + } + width = 29.0 + } else { + string.addAttribute(.kern, value: -0.5 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) + width = 19.0 + offset.x += -0.3 + } + + let path = UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - width) / 2.0), y: 0.0, width: width, height: 16.0).insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0), byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 2.0, height: 2.0)) + context.addPath(path.cgPath) + context.strokePath() + + let boundingRect = string.boundingRect(with: size, options: [], context: nil) + string.draw(at: CGPoint(x: offset.x + floor((size.width - boundingRect.width) / 2.0), y: offset.y + UIScreenPixel + floor((size.height - boundingRect.height) / 2.0))) + + UIGraphicsPopContext() + }) +} + +private final class RateButton: HighlightableButtonNode { + enum Content { + case image(UIImage?) + } + + let referenceNode: ContextReferenceContentNode + let containerNode: ContextControllerSourceNode + private let iconNode: ASImageNode + + var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? + + private let wide: Bool + + init(wide: Bool = false) { + self.wide = wide + + self.referenceNode = ContextReferenceContentNode() + self.containerNode = ContextControllerSourceNode() + self.containerNode.animateScale = false + self.iconNode = ASImageNode() + self.iconNode.displaysAsynchronously = false + self.iconNode.displayWithoutProcessing = true + self.iconNode.contentMode = .scaleToFill + + super.init() + + self.containerNode.addSubnode(self.referenceNode) + self.referenceNode.addSubnode(self.iconNode) + self.addSubnode(self.containerNode) + + self.containerNode.shouldBegin = { [weak self] location in + guard let strongSelf = self, let _ = strongSelf.contextAction else { + return false + } + return true + } + self.containerNode.activated = { [weak self] gesture, _ in + guard let strongSelf = self else { + return + } + strongSelf.contextAction?(strongSelf.containerNode, gesture) + } + + self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 26.0, height: 44.0)) + self.referenceNode.frame = self.containerNode.bounds + + if let image = self.iconNode.image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.hitTestSlop = UIEdgeInsets(top: 0.0, left: -4.0, bottom: 0.0, right: -4.0) + } + + private var content: Content? + func setContent(_ content: Content, animated: Bool = false) { + if animated { + if let snapshotView = self.referenceNode.view.snapshotContentTree() { + snapshotView.frame = self.referenceNode.frame + self.view.addSubview(snapshotView) + + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + snapshotView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.3, removeOnCompletion: false) + + self.iconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + self.iconNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.3) + } + + switch content { + case let .image(image): + if let image = image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.iconNode.image = image + self.iconNode.isHidden = false + } + } else { + self.content = content + switch content { + case let .image(image): + if let image = image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.iconNode.image = image + self.iconNode.isHidden = false + } + } + } + + override func didLoad() { + super.didLoad() + self.view.isOpaque = false + } + + override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { + return CGSize(width: wide ? 32.0 : 22.0, height: 44.0) + } + + func onLayout() { + } +} + +private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { + private let controller: ViewController + private let sourceNode: ContextReferenceContentNode + + init(controller: ViewController, sourceNode: ContextReferenceContentNode) { + self.controller = controller + self.sourceNode = sourceNode + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds) + } +} diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryPanel.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryPanel.swift index 9e1e41a976..a7ab775591 100644 --- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryPanel.swift +++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryPanel.swift @@ -4,17 +4,21 @@ import Display import AsyncDisplayKit import TelegramCore import AccountContext +import TelegramUIPreferences public final class MediaNavigationAccessoryPanel: ASDisplayNode { public let containerNode: MediaNavigationAccessoryContainerNode public var close: (() -> Void)? - public var toggleRate: (() -> Void)? + public var setRate: ((AudioPlaybackRate) -> Void)? public var togglePlayPause: (() -> Void)? public var tapAction: (() -> Void)? public var playPrevious: (() -> Void)? public var playNext: (() -> Void)? + public var getController: (() -> ViewController?)? + public var presentInGlobalOverlay: ((ViewController) -> Void)? + public init(context: AccountContext, displayBackground: Bool = false) { self.containerNode = MediaNavigationAccessoryContainerNode(context: context, displayBackground: displayBackground) @@ -27,8 +31,8 @@ public final class MediaNavigationAccessoryPanel: ASDisplayNode { close() } } - self.containerNode.headerNode.toggleRate = { [weak self] in - self?.toggleRate?() + self.containerNode.headerNode.setRate = { [weak self] rate in + self?.setRate?(rate) } self.containerNode.headerNode.togglePlayPause = { [weak self] in if let strongSelf = self, let togglePlayPause = strongSelf.togglePlayPause { @@ -50,6 +54,20 @@ public final class MediaNavigationAccessoryPanel: ASDisplayNode { playNext() } } + + self.containerNode.headerNode.getController = { [weak self] in + if let strongSelf = self, let getController = strongSelf.getController { + return getController() + } else { + return nil + } + } + + self.containerNode.headerNode.presentInGlobalOverlay = { [weak self] c in + if let strongSelf = self, let presentInGlobalOverlay = strongSelf.presentInGlobalOverlay { + presentInGlobalOverlay(c) + } + } } public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { diff --git a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift index a48571c327..7bf253b1b4 100644 --- a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift +++ b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift @@ -645,32 +645,28 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context) mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo + mediaAccessoryPanel.getController = { [weak self] in + return self + } + mediaAccessoryPanel.presentInGlobalOverlay = { [weak self] c in + self?.presentInGlobalOverlay(c) + } mediaAccessoryPanel.close = { [weak self] in if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType { strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause)) } } - mediaAccessoryPanel.toggleRate = { - [weak self] in + mediaAccessoryPanel.setRate = { [weak self] rate in guard let strongSelf = self else { return } let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings) as? MusicPlaybackSettings ?? MusicPlaybackSettings.defaultSettings - let nextRate: AudioPlaybackRate - switch settings.voicePlaybackRate { - case .x1: - nextRate = .x2 - case .x2: - nextRate = .x1 - default: - nextRate = .x1 - } transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in - return settings.withUpdatedVoicePlaybackRate(nextRate) + return settings.withUpdatedVoicePlaybackRate(rate) }) - return nextRate + return rate } |> deliverOnMainQueue).start(next: { baseRate in guard let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType else { @@ -688,22 +684,31 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { }) let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let slowdown = baseRate == .x1 - strongSelf.present( - UndoOverlayController( - presentationData: presentationData, - content: .audioRate( - slowdown: slowdown, - text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp + let slowdown: Bool? + if baseRate == .x1 { + slowdown = true + } else if baseRate == .x2 { + slowdown = false + } else { + slowdown = nil + } + if let slowdown = slowdown { + strongSelf.present( + UndoOverlayController( + presentationData: presentationData, + content: .audioRate( + slowdown: slowdown, + text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp + ), + elevatedLayout: false, + animateInAsReplacement: hasTooltip, + action: { action in + return true + } ), - elevatedLayout: false, - animateInAsReplacement: hasTooltip, - action: { action in - return true - } - ), - in: .current - ) + in: .current + ) + } }) } mediaAccessoryPanel.togglePlayPause = { [weak self] in diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index c8cd1f27a2..ed879e8549 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -4776,7 +4776,6 @@ public final class VoiceChatController: ViewController { let speakingPeersUpdated = self.currentSpeakingPeers != speakingPeers self.currentCallMembers = callMembers - self.currentSpeakingPeers = speakingPeers self.currentInvitedPeers = invitedPeers var entries: [ListEntry] = [] @@ -5081,9 +5080,10 @@ public final class VoiceChatController: ViewController { self.updateMainVideo(waitForFullSize: true, entries: fullscreenEntries, force: true) return } - + self.updateRequestedVideoChannels() + self.currentSpeakingPeers = speakingPeers self.peerIdToEndpointId = peerIdToEndpointId var updateLayout = false diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 5c6ac8f1c9..35d84b0cec 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -442,6 +442,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return self.chatLocation } + private var scheduledScrollToMessageId: (MessageId, Double?)? + public var purposefulAction: (() -> Void)? var updatedClosedPinnedMessageId: ((MessageId) -> Void)? var requestedUnpinAllMessages: ((Int, MessageId) -> Void)? @@ -2423,7 +2425,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } if !found { - let _ = strongSelf.controllerInteraction?.openMessage(message, .timecode(Double(timestamp))) + if let _ = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) { + let _ = strongSelf.controllerInteraction?.openMessage(message, .timecode(Double(timestamp))) + } else { + strongSelf.navigateToMessage(messageLocation: .id(message.id, Double(timestamp)), animated: true, forceInCurrentChat: true) + } } } }, scheduleCurrentMessage: { [weak self] in @@ -4612,7 +4618,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } })) - if case let .message(_, _, maybeTimecode) = strongSelf.subject, let timecode = maybeTimecode, initial { + if let (messageId, maybeTimecode) = strongSelf.scheduledScrollToMessageId { + strongSelf.scheduledScrollToMessageId = nil + if let timecode = maybeTimecode, message.id == messageId { + Queue.mainQueue().after(0.2) { + let _ = strongSelf.controllerInteraction?.openMessage(message, .timecode(timecode)) + } + } + } else if case let .message(_, _, maybeTimecode) = strongSelf.subject, let timecode = maybeTimecode, initial { Queue.mainQueue().after(0.2) { let _ = strongSelf.controllerInteraction?.openMessage(message, .timecode(timecode)) } @@ -10729,6 +10742,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else if case let .index(index) = messageLocation, index.id.id == 0, index.timestamp > 0, case .scheduledMessages = self.presentationInterfaceState.subject { self.chatDisplayNode.historyNode.scrollToMessage(from: scrollFromIndex, to: index, animated: animated, scrollPosition: scrollPosition) } else { + if case let .id(messageId, maybeTimecode) = messageLocation, let timecode = maybeTimecode { + self.scheduledScrollToMessageId = (messageId, timecode) + } self.loadingMessage.set(.single(statusSubject) |> delay(0.1, queue: .mainQueue())) let searchLocation: ChatHistoryInitialSearchLocation switch messageLocation { @@ -10910,7 +10926,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var attemptSelectionImpl: ((Peer) -> Void)? let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: filter, attemptSelection: { peer in attemptSelectionImpl?(peer) - }, multipleSelection: true)) + }, multipleSelection: true, forwardedMessagesCount: messages.count)) let context = self.context attemptSelectionImpl = { [weak controller] peer in guard let controller = controller else { diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index 6cb1ee9204..0354afcac3 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1080,7 +1080,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let viaBotNode = viaBotApply() if strongSelf.viaBotNode == nil { strongSelf.viaBotNode = viaBotNode - strongSelf.addSubnode(viaBotNode) + strongSelf.contextSourceNode.contentNode.addSubnode(viaBotNode) } let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 15.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 14.0)), y: 8.0), size: viaBotLayout.size) viaBotNode.frame = viaBotFrame @@ -1161,7 +1161,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } else { let forwardBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)) strongSelf.forwardBackgroundNode = forwardBackgroundNode - strongSelf.addSubnode(forwardBackgroundNode) + strongSelf.contextSourceNode.contentNode.addSubnode(forwardBackgroundNode) } } @@ -1169,7 +1169,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let forwardInfoNode = forwardInfoApply(forwardInfoSize.width) if strongSelf.forwardInfoNode == nil { strongSelf.forwardInfoNode = forwardInfoNode - strongSelf.addSubnode(forwardInfoNode) + strongSelf.contextSourceNode.contentNode.addSubnode(forwardInfoNode) } let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - forwardInfoSize.width - layoutConstants.bubble.edgeInset - 12.0)), y: 8.0), size: forwardInfoSize) forwardInfoNode.frame = forwardInfoFrame diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index a76da960e4..69be62c976 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -43,11 +43,11 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD } private var forwardInfoNode: ChatMessageForwardInfoNode? - private var forwardBackgroundNode: ASImageNode? + private var forwardBackgroundNode: NavigationBackgroundNode? private var viaBotNode: TextNode? private var replyInfoNode: ChatMessageReplyInfoNode? - private var replyBackgroundNode: ASImageNode? + private var replyBackgroundNode: NavigationBackgroundNode? private var actionButtonsNode: ChatMessageActionButtonsNode? @@ -389,7 +389,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD var viaBotApply: (TextNodeLayout, () -> TextNode)? var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)? - var updatedReplyBackgroundNode: ASImageNode? + var updatedReplyBackgroundNode: NavigationBackgroundNode? var replyBackgroundImage: UIImage? var replyMarkup: ReplyMarkupMessageAttribute? @@ -432,6 +432,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD let botString = addAttributesToStringWithRanges(item.presentationData.strings.Conversation_MessageViaUser("@\(inlineBotNameString)")._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) viaBotApply = viaBotLayout(TextNodeLayoutArguments(attributedString: botString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0, availableWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + ignoreForward = true } } @@ -464,11 +466,10 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD if let currentReplyBackgroundNode = currentReplyBackgroundNode { updatedReplyBackgroundNode = currentReplyBackgroundNode } else { - updatedReplyBackgroundNode = ASImageNode() + updatedReplyBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)) } - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) - replyBackgroundImage = graphics.chatFreeformContentAdditionalInfoBackgroundImage + updatedReplyBackgroundNode?.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate) } var updatedShareButtonNode: ChatMessageShareButton? @@ -487,7 +488,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD var forwardAuthorSignature: String? var forwardInfoSizeApply: (CGSize, (CGFloat) -> ChatMessageForwardInfoNode)? - var updatedForwardBackgroundNode: ASImageNode? + var updatedForwardBackgroundNode: NavigationBackgroundNode? var forwardBackgroundImage: UIImage? if !ignoreForward, let forwardInfo = item.message.forwardInfo { @@ -517,11 +518,10 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD if let currentForwardBackgroundNode = currentForwardBackgroundNode { updatedForwardBackgroundNode = currentForwardBackgroundNode } else { - updatedForwardBackgroundNode = ASImageNode() + updatedForwardBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)) } - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) - forwardBackgroundImage = graphics.chatServiceBubbleFillImage + updatedForwardBackgroundNode?.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate) } var maxContentWidth = normalDisplaySize.width @@ -613,10 +613,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD if let updatedReplyBackgroundNode = updatedReplyBackgroundNode { if strongSelf.replyBackgroundNode == nil { strongSelf.replyBackgroundNode = updatedReplyBackgroundNode - strongSelf.addSubnode(updatedReplyBackgroundNode) - updatedReplyBackgroundNode.image = replyBackgroundImage - } else { - strongSelf.replyBackgroundNode?.image = replyBackgroundImage + strongSelf.contextSourceNode.contentNode.addSubnode(updatedReplyBackgroundNode) } } else if let replyBackgroundNode = strongSelf.replyBackgroundNode { replyBackgroundNode.removeFromSupernode() @@ -627,11 +624,13 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD let viaBotNode = viaBotApply() if strongSelf.viaBotNode == nil { strongSelf.viaBotNode = viaBotNode - strongSelf.addSubnode(viaBotNode) + strongSelf.contextSourceNode.contentNode.addSubnode(viaBotNode) } let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0), size: viaBotLayout.size) viaBotNode.frame = viaBotFrame - strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 4.0, y: viaBotFrame.minY - 2.0), size: CGSize(width: viaBotFrame.size.width + 8.0, height: viaBotFrame.size.height + 5.0)) + let replyBackgroundFrame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 4.0, y: viaBotFrame.minY - 2.0), size: CGSize(width: viaBotFrame.size.width + 8.0, height: viaBotFrame.size.height + 5.0)) + strongSelf.replyBackgroundNode?.frame = replyBackgroundFrame + strongSelf.replyBackgroundNode?.update(size: replyBackgroundFrame.size, cornerRadius: 8.0, transition: .immediate) } else if let viaBotNode = strongSelf.viaBotNode { viaBotNode.removeFromSupernode() strongSelf.viaBotNode = nil @@ -641,7 +640,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD let replyInfoNode = replyInfoApply() if strongSelf.replyInfoNode == nil { strongSelf.replyInfoNode = replyInfoNode - strongSelf.addSubnode(replyInfoNode) + strongSelf.contextSourceNode.contentNode.addSubnode(replyInfoNode) } var viaBotSize = CGSize() if let viaBotNode = strongSelf.viaBotNode { @@ -654,7 +653,9 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD } } replyInfoNode.frame = replyInfoFrame - strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0)) + let replyBackgroundFrame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0)) + strongSelf.replyBackgroundNode?.frame = replyBackgroundFrame + strongSelf.replyBackgroundNode?.update(size: replyBackgroundFrame.size, cornerRadius: 8.0, transition: .immediate) } else if let replyInfoNode = strongSelf.replyInfoNode { replyInfoNode.removeFromSupernode() strongSelf.replyInfoNode = nil @@ -694,8 +695,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD if let updatedForwardBackgroundNode = updatedForwardBackgroundNode { if strongSelf.forwardBackgroundNode == nil { strongSelf.forwardBackgroundNode = updatedForwardBackgroundNode - strongSelf.addSubnode(updatedForwardBackgroundNode) - updatedForwardBackgroundNode.image = forwardBackgroundImage + strongSelf.contextSourceNode.contentNode.addSubnode(updatedForwardBackgroundNode) } } else if let forwardBackgroundNode = strongSelf.forwardBackgroundNode { forwardBackgroundNode.removeFromSupernode() @@ -706,7 +706,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD let forwardInfoNode = forwardInfoApply(forwardInfoSize.width) if strongSelf.forwardInfoNode == nil { strongSelf.forwardInfoNode = forwardInfoNode - strongSelf.addSubnode(forwardInfoNode) + strongSelf.contextSourceNode.contentNode.addSubnode(forwardInfoNode) forwardInfoNode.openPsa = { [weak strongSelf] type, sourceNode in guard let strongSelf = strongSelf, let item = strongSelf.item else { return @@ -716,7 +716,9 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD } let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - forwardInfoSize.width - layoutConstants.bubble.edgeInset - 12.0)), y: 8.0), size: forwardInfoSize) forwardInfoNode.frame = forwardInfoFrame - strongSelf.forwardBackgroundNode?.frame = CGRect(origin: CGPoint(x: forwardInfoFrame.minX - 6.0, y: forwardInfoFrame.minY - 2.0), size: CGSize(width: forwardInfoFrame.size.width + 10.0, height: forwardInfoFrame.size.height + 4.0)) + let forwardBackgroundFrame = CGRect(origin: CGPoint(x: forwardInfoFrame.minX - 6.0, y: forwardInfoFrame.minY - 2.0), size: CGSize(width: forwardInfoFrame.size.width + 10.0, height: forwardInfoFrame.size.height + 4.0)) + strongSelf.forwardBackgroundNode?.frame = forwardBackgroundFrame + strongSelf.forwardBackgroundNode?.update(size: forwardBackgroundFrame.size, cornerRadius: 8.0, transition: .immediate) } else if let forwardInfoNode = strongSelf.forwardInfoNode { forwardInfoNode.removeFromSupernode() strongSelf.forwardInfoNode = nil diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index 35aa60b1d2..40b90c78cc 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -719,7 +719,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { let viaBotNode = viaBotApply() if strongSelf.viaBotNode == nil { strongSelf.viaBotNode = viaBotNode - strongSelf.addSubnode(viaBotNode) + strongSelf.contextSourceNode.contentNode.addSubnode(viaBotNode) } viaBotNode.frame = viaBotFrame if let replyBackgroundNode = strongSelf.replyBackgroundNode { diff --git a/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift b/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift index 91a1f10153..2827f864c0 100644 --- a/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift +++ b/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift @@ -11,13 +11,15 @@ private final class InstantVideoRadialStatusNodeParameters: NSObject { let progress: CGFloat let dimProgress: CGFloat let playProgress: CGFloat + let blinkProgress: CGFloat let hasSeek: Bool - init(color: UIColor, progress: CGFloat, dimProgress: CGFloat, playProgress: CGFloat, hasSeek: Bool) { + init(color: UIColor, progress: CGFloat, dimProgress: CGFloat, playProgress: CGFloat, blinkProgress: CGFloat, hasSeek: Bool) { self.color = color self.progress = progress self.dimProgress = dimProgress self.playProgress = playProgress + self.blinkProgress = blinkProgress self.hasSeek = hasSeek } } @@ -64,6 +66,12 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele } } + private var effectiveBlinkProgress: CGFloat = 0.0 { + didSet { + self.setNeedsDisplay() + } + } + private var _statusValue: MediaPlayerStatus? private var statusValue: MediaPlayerStatus? { get { @@ -196,8 +204,38 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele if let seekingProgress = self.seekingProgress { if seekingProgress > 0.98 && fraction > 0.0 && fraction < 0.05 { self.hapticFeedback.impact(.light) + + let blinkAnimation = POPBasicAnimation() + blinkAnimation.property = POPAnimatableProperty.property(withName: "blinkProgress", initializer: { property in + property?.readBlock = { node, values in + values?.pointee = (node as! InstantVideoRadialStatusNode).effectiveBlinkProgress + } + property?.writeBlock = { node, values in + (node as! InstantVideoRadialStatusNode).effectiveBlinkProgress = values!.pointee + } + property?.threshold = 0.01 + }) as? POPAnimatableProperty + blinkAnimation.fromValue = 1.0 as NSNumber + blinkAnimation.toValue = 0.0 as NSNumber + blinkAnimation.duration = 0.5 + self.pop_add(blinkAnimation, forKey: "blinkProgress") } else if seekingProgress > 0.0 && seekingProgress < 0.05 && fraction > 0.98 { self.hapticFeedback.impact(.light) + + let blinkAnimation = POPBasicAnimation() + blinkAnimation.property = POPAnimatableProperty.property(withName: "blinkProgress", initializer: { property in + property?.readBlock = { node, values in + values?.pointee = (node as! InstantVideoRadialStatusNode).effectiveBlinkProgress + } + property?.writeBlock = { node, values in + (node as! InstantVideoRadialStatusNode).effectiveBlinkProgress = values!.pointee + } + property?.threshold = 0.01 + }) as? POPAnimatableProperty + blinkAnimation.fromValue = -1.0 as NSNumber + blinkAnimation.toValue = 0.0 as NSNumber + blinkAnimation.duration = 0.5 + self.pop_add(blinkAnimation, forKey: "blinkProgress") } } let newProgress = min(0.99, fraction) @@ -216,7 +254,7 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele } override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? { - return InstantVideoRadialStatusNodeParameters(color: self.color, progress: self.effectiveProgress, dimProgress: self.effectiveDimProgress, playProgress: self.effectivePlayProgress, hasSeek: self.hasSeek) + return InstantVideoRadialStatusNodeParameters(color: self.color, progress: self.effectiveProgress, dimProgress: self.effectiveDimProgress, playProgress: self.effectivePlayProgress, blinkProgress: self.effectiveBlinkProgress, hasSeek: self.hasSeek) } @objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) { @@ -231,9 +269,23 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele if let parameters = parameters as? InstantVideoRadialStatusNodeParameters { context.setStrokeColor(parameters.color.cgColor) + context.addEllipse(in: bounds) + context.clip() + if !parameters.dimProgress.isZero { - context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.35 * min(1.0, parameters.dimProgress)).cgColor) - context.fillEllipse(in: bounds) + if parameters.playProgress == 1.0 { + context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.35 * min(1.0, parameters.dimProgress)).cgColor) + context.fillEllipse(in: bounds) + } else { + var locations: [CGFloat] = [0.0, 0.8, 1.0] + let alpha: CGFloat = 0.2 + 0.15 * parameters.playProgress + let colors: [CGColor] = [UIColor(rgb: 0x000000, alpha: alpha * min(1.0, parameters.dimProgress * parameters.playProgress)).cgColor, UIColor(rgb: 0x000000, alpha: alpha * min(1.0, parameters.dimProgress * parameters.playProgress)).cgColor, UIColor(rgb: 0x000000, alpha: alpha * min(1.0, parameters.dimProgress)).cgColor] + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! + + let center = bounds.center + context.drawRadialGradient(gradient, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: bounds.width / 2.0, options: .drawsAfterEndLocation) + } } context.setBlendMode(.normal) @@ -255,9 +307,19 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele } if !parameters.dimProgress.isZero { + context.setLineWidth(lineWidth) + + if parameters.blinkProgress > 0.0 { + context.setStrokeColor(parameters.color.withAlphaComponent(0.2 * parameters.blinkProgress).cgColor) + context.strokeEllipse(in: CGRect(x: (bounds.size.width - pathDiameter) / 2.0 , y: (bounds.size.height - pathDiameter) / 2.0, width: pathDiameter, height: pathDiameter)) + } + if parameters.hasSeek { - context.setStrokeColor(parameters.color.withAlphaComponent(0.2 * parameters.dimProgress).cgColor) - context.setLineWidth(lineWidth) + var progress = parameters.dimProgress + if parameters.blinkProgress < 0.0 { + progress = parameters.dimProgress + parameters.blinkProgress + } + context.setStrokeColor(parameters.color.withAlphaComponent(0.2 * progress).cgColor) context.strokeEllipse(in: CGRect(x: (bounds.size.width - pathDiameter) / 2.0 , y: (bounds.size.height - pathDiameter) / 2.0, width: pathDiameter, height: pathDiameter)) } diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift index 6545a7e36d..c5bd2ad74a 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift @@ -206,32 +206,28 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context, displayBackground: true) mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo + mediaAccessoryPanel.getController = { [weak self] in + return self?.parentController + } + mediaAccessoryPanel.presentInGlobalOverlay = { [weak self] c in + self?.parentController?.presentInGlobalOverlay(c) + } mediaAccessoryPanel.close = { [weak self] in if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType { strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause)) } } - mediaAccessoryPanel.toggleRate = { - [weak self] in + mediaAccessoryPanel.setRate = { [weak self] rate in guard let strongSelf = self else { return } let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings) as? MusicPlaybackSettings ?? MusicPlaybackSettings.defaultSettings - let nextRate: AudioPlaybackRate - switch settings.voicePlaybackRate { - case .x1: - nextRate = .x2 - case .x2: - nextRate = .x1 - default: - nextRate = .x1 - } transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in - return settings.withUpdatedVoicePlaybackRate(nextRate) + return settings.withUpdatedVoicePlaybackRate(rate) }) - return nextRate + return rate } |> deliverOnMainQueue).start(next: { baseRate in guard let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType else { @@ -250,22 +246,31 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { }) let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let slowdown = baseRate == .x1 - controller.present( - UndoOverlayController( - presentationData: presentationData, - content: .audioRate( - slowdown: slowdown, - text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp + let slowdown: Bool? + if baseRate == .x1 { + slowdown = true + } else if baseRate == .x2 { + slowdown = false + } else { + slowdown = nil + } + if let slowdown = slowdown { + controller.present( + UndoOverlayController( + presentationData: presentationData, + content: .audioRate( + slowdown: slowdown, + text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp + ), + elevatedLayout: false, + animateInAsReplacement: hasTooltip, + action: { action in + return true + } ), - elevatedLayout: false, - animateInAsReplacement: hasTooltip, - action: { action in - return true - } - ), - in: .current - ) + in: .current + ) + } } }) } diff --git a/submodules/TelegramUI/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Sources/PeerSelectionController.swift index 04b95dfe60..2873c2ae74 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionController.swift @@ -58,6 +58,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon private let hasContactSelector: Bool private let hasGlobalSearch: Bool private let pretendPresentedInModal: Bool + private let forwardedMessagesCount: Int override public var _presentedInModal: Bool { get { @@ -85,6 +86,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon self.attemptSelection = params.attemptSelection self.createNewGroup = params.createNewGroup self.pretendPresentedInModal = params.pretendPresentedInModal + self.forwardedMessagesCount = params.forwardedMessagesCount super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) @@ -148,7 +150,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon } override public func loadDisplayNode() { - self.displayNode = PeerSelectionControllerNode(context: self.context, filter: self.filter, hasChatListSelector: self.hasChatListSelector, hasContactSelector: self.hasContactSelector, hasGlobalSearch: self.hasGlobalSearch, createNewGroup: self.createNewGroup, present: { [weak self] c, a in + self.displayNode = PeerSelectionControllerNode(context: self.context, filter: self.filter, hasChatListSelector: self.hasChatListSelector, hasContactSelector: self.hasContactSelector, hasGlobalSearch: self.hasGlobalSearch, forwardedMessagesCount: self.forwardedMessagesCount, createNewGroup: self.createNewGroup, present: { [weak self] c, a in self?.present(c, in: .window(.root), with: a) }, presentInGlobalOverlay: { [weak self] c, a in self?.presentInGlobalOverlay(c, with: a) diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index 9d6eb2af3d..c599ae7588 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -66,7 +66,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { return self.readyValue.get() } - init(context: AccountContext, filter: ChatListNodePeersFilter, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) { + init(context: AccountContext, filter: ChatListNodePeersFilter, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessagesCount: Int, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) { self.context = context self.present = present self.presentInGlobalOverlay = presentInGlobalOverlay @@ -79,6 +79,16 @@ final class PeerSelectionControllerNode: ASDisplayNode { 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(PeerId(0)), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil) + var mockMessageIds: [MessageId]? + if forwardedMessagesCount > 0 { + var messageIds: [MessageId] = [] + for _ in 0 ..< forwardedMessagesCount { + messageIds.append(MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Local, id: Int32.random(in: 0 ..< Int32.max))) + } + mockMessageIds = messageIds + } + self.presentationInterfaceState = self.presentationInterfaceState.updatedInterfaceState { $0.withUpdatedForwardMessageIds(mockMessageIds) } + if hasChatListSelector && hasContactSelector { self.toolbarBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.rootController.navigationBar.blurredBackgroundColor) @@ -276,13 +286,6 @@ final class PeerSelectionControllerNode: ASDisplayNode { guard let textInputNode = textInputPanelNode.textInputNode else { return } -// let previousSupportedOrientations = strongSelf.supportedOrientations -// if layout.size.width > layout.size.height { -// strongSelf.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .landscape) -// } else { -// strongSelf.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) -// } - let controller = ChatSendMessageActionSheetController(context: strongSelf.context, interfaceState: strongSelf.presentationInterfaceState, gesture: gesture, sourceSendButton: node, textInputNode: textInputNode, completion: { [weak self] in if let strongSelf = self { // strongSelf.supportedOrientations = previousSupportedOrientations @@ -292,7 +295,6 @@ final class PeerSelectionControllerNode: ASDisplayNode { }, schedule: { [weak textInputPanelNode] in textInputPanelNode?.sendMessage(.schedule) }) -// strongSelf.sendMessageActionsController = controller strongSelf.presentInGlobalOverlay(controller, nil) }, openScheduledMessages: { }, openPeersNearby: { From f11f524de32fdcc6ac64c6d5cdd5555d655edf23 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 4 Aug 2021 21:45:45 +0300 Subject: [PATCH 06/18] Fix build --- .../ShareController/Sources/ShareControllerPeerGridItem.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift index 9755c0ff9b..128c89b238 100644 --- a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift +++ b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift @@ -219,7 +219,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { shimmerNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, horizontal: true, size: self.bounds.size) } -] + self.currentState = (context, theme, strings, peer, search, presence) self.setNeedsLayout() if let presence = presence as? TelegramUserPresence { From 50081e5450e76b11ac7493589178b3f03458a0cb Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 4 Aug 2021 22:08:00 +0300 Subject: [PATCH 07/18] Fix build --- .../ShareController/Sources/ShareControllerPeerGridItem.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift index 128c89b238..aa17c8afae 100644 --- a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift +++ b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift @@ -187,7 +187,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { self.peerNode.theme = itemTheme if let peer = peer { - self.peerNode.setup(context: context, theme: theme, strings: strings, peer: EngineRenderedPeer(peer), online: online, synchronousLoad: synchronousLoad) + self.peerNode.setup(context: context, theme: theme, strings: strings, peer: peer, online: online, synchronousLoad: synchronousLoad) if let shimmerNode = self.placeholderNode { self.placeholderNode = nil shimmerNode.removeFromSupernode() From e6c281e8401ba8563f187aa2a61dc07a48bb57de Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 4 Aug 2021 22:35:11 +0300 Subject: [PATCH 08/18] Bump version --- versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.json b/versions.json index e740e76b56..bdb36c70b9 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "7.9.1", + "app": "7.9.2", "bazel": "4.0.0", "xcode": "12.4" } From 54586fa1b3531cd2bf993edb88e1c46a473f8cfc Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 26 Aug 2021 19:52:01 +0400 Subject: [PATCH 09/18] Update tgcalls --- submodules/TgVoipWebrtc/tgcalls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index b5a053d60a..8d90d68fb3 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit b5a053d60ab96a2f7857135adef4f1a9e8cfe775 +Subproject commit 8d90d68fb39b6e2dbee94b587f960712c27daeae From 2985b00fad773a2c624e45cd3aac275f97713c1d Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 26 Aug 2021 19:52:08 +0400 Subject: [PATCH 10/18] Fix text --- submodules/TelegramUI/Sources/ChatController.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index d20afd7103..63b573b132 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -1294,9 +1294,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } + + var botPeer: TelegramUser? + if let peer = message.author as? TelegramUser, peer.botInfo != nil { + botPeer = peer + } else if let peer = message.peers[message.id.peerId] as? TelegramUser, peer.botInfo != nil { + botPeer = peer + } + if value { openBot() - } else if let botPeer = message.author as? TelegramUser { + } else if let botPeer = botPeer { strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_BotInteractiveUrlAlert(botPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { if let strongSelf = self { let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: strongSelf.context.sharedContext.accountManager, peerId: botPeer.id).start() From 7f7498c56356dd96c7c6fbbc5a3cd8aefd526404 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 26 Aug 2021 22:28:03 +0400 Subject: [PATCH 11/18] Update tgcalls --- submodules/TgVoipWebrtc/tgcalls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index 8d90d68fb3..dc8c87e75b 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit 8d90d68fb39b6e2dbee94b587f960712c27daeae +Subproject commit dc8c87e75bb88acb51dfdd781db2f8bcf1fb7433 From 4ca4bb2fc8c989d2880011b88e37587e1a3daa82 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 26 Aug 2021 22:28:31 +0400 Subject: [PATCH 12/18] Bump version --- versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.json b/versions.json index 55a2b9955f..b983cff018 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "7.9.1", + "app": "8.0", "bazel": "4.0.0", "xcode": "12.5.1" } From f14a3490675664c66aca74113f03ad0a469f8aee Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 26 Aug 2021 22:46:39 +0400 Subject: [PATCH 13/18] Fix game bot popup --- .../TelegramUI/Sources/ChatController.swift | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index a312acdc98..efcff612f8 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -1283,26 +1283,35 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.dismissInput() strongSelf.effectiveNavigationController?.pushViewController(GameController(context: strongSelf.context, url: url, message: message)) } + + var botPeer: TelegramUser? + for attribute in message.attributes { + if let attribute = attribute as? InlineBotMessageAttribute { + if let peerId = attribute.peerId { + botPeer = message.peers[peerId] as? TelegramUser + } + } + } + if botPeer == nil { + if let peer = message.author as? TelegramUser, peer.botInfo != nil { + botPeer = peer + } else if let peer = message.peers[message.id.peerId] as? TelegramUser, peer.botInfo != nil { + botPeer = peer + } + } - if let botPeer = message.author as? TelegramUser, botPeer.flags.contains(.isVerified) { + if let botPeer = botPeer, botPeer.flags.contains(.isVerified) { openBot() - } else { - let _ = (ApplicationSpecificNotice.getBotGameNotice(accountManager: strongSelf.context.sharedContext.accountManager, peerId: message.id.peerId) + } else if let botPeer = botPeer { + let _ = (ApplicationSpecificNotice.getBotGameNotice(accountManager: strongSelf.context.sharedContext.accountManager, peerId: botPeer.id) |> deliverOnMainQueue).start(next: { value in guard let strongSelf = self else { return } - var botPeer: TelegramUser? - if let peer = message.author as? TelegramUser, peer.botInfo != nil { - botPeer = peer - } else if let peer = message.peers[message.id.peerId] as? TelegramUser, peer.botInfo != nil { - botPeer = peer - } - if value { openBot() - } else if let botPeer = botPeer { + } else { strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_BotInteractiveUrlAlert(botPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { if let strongSelf = self { let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: strongSelf.context.sharedContext.accountManager, peerId: botPeer.id).start() From fed11d761d740afdb589479afd390850d3fcbf6c Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 26 Aug 2021 22:58:14 +0400 Subject: [PATCH 14/18] Update github actions --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dcf2761983..243257fc2f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ on: jobs: build: - runs-on: macos-10.15 + runs-on: macos-11 steps: - uses: actions/checkout@v2 @@ -17,7 +17,7 @@ jobs: fetch-depth: '0' - name: Set active Xcode path - run: sudo xcode-select -s /Applications/Xcode_12.4.app/Contents/Developer + run: sudo xcode-select -s /Applications/Xcode_12.5.1.app/Contents/Developer - name: Create canonical source directory run: | From 892a6655e543a5d66d110c8d3a7e841bc81bb8c4 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 27 Aug 2021 01:07:17 +0400 Subject: [PATCH 15/18] Fix build --- .../LegacyComponents/Sources/UICollectionView+TGTransitioning.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/LegacyComponents/Sources/UICollectionView+TGTransitioning.m b/submodules/LegacyComponents/Sources/UICollectionView+TGTransitioning.m index e78bc4e41a..863ef22012 100755 --- a/submodules/LegacyComponents/Sources/UICollectionView+TGTransitioning.m +++ b/submodules/LegacyComponents/Sources/UICollectionView+TGTransitioning.m @@ -6,7 +6,7 @@ @implementation UICollectionView (TGTransitioning) -- (BOOL)isTransitionInProgress +- (bool)isTransitionInProgress { return ([self tg_transitionData] != nil); } From 636a07b4fc8b347ea689c0db55167136d574640d Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 27 Aug 2021 01:24:07 +0400 Subject: [PATCH 16/18] Fix build --- submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m b/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m index acfe8a1e31..07a2a2e7f7 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m @@ -322,7 +322,7 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f; #pragma mark - Properties -- (bool)isTracking +- (BOOL)isTracking { return _knobView.highlighted; } From c09413907cb3b9c3a77031341204293632a18c22 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 27 Aug 2021 01:44:41 +0400 Subject: [PATCH 17/18] Temporarily disable -Werror in LegacyComponents --- submodules/LegacyComponents/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/LegacyComponents/BUILD b/submodules/LegacyComponents/BUILD index 16ddc7c99f..afe189ca32 100644 --- a/submodules/LegacyComponents/BUILD +++ b/submodules/LegacyComponents/BUILD @@ -30,7 +30,7 @@ objc_library( ]), copts = [ "-I{}/PublicHeaders/LegacyComponents".format(package_name()), - "-Werror", + #"-Werror", ], includes = [ "PublicHeaders", From 5638bda69300a9688626e997535e6068afd54286 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 27 Aug 2021 02:09:46 +0400 Subject: [PATCH 18/18] Channel UI fixes --- .../Peers/TelegramEnginePeers.swift | 7 +++-- .../Sources/ChatHistoryListNode.swift | 28 ++++++++++--------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 1a1336dc2e..17e018ff02 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -549,8 +549,11 @@ public extension TelegramEngine { } } - guard let peerGroupId = transaction.getPeerChatListIndex(peerId)?.0 else { - return nil + let peerGroupId: PeerGroupId + if let peerGroupIdValue = transaction.getPeerChatListIndex(peerId)?.0 { + peerGroupId = peerGroupIdValue + } else { + peerGroupId = .root } if let filterId = chatListFilterId { diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 16cfaa4fd7..78281fb7cc 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -1259,7 +1259,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if self.freezeOverscrollControl { return } - if let offset = offset, offset < 0.0, self.offerNextChannelToRead, let chatControllerNode = self.controllerInteraction.chatControllerNode() as? ChatControllerNode, chatControllerNode.shouldAllowOverscrollActions { + if let offset = offset, offset < -0.1, self.offerNextChannelToRead, let chatControllerNode = self.controllerInteraction.chatControllerNode() as? ChatControllerNode, chatControllerNode.shouldAllowOverscrollActions { let overscrollView: ComponentHostView if let current = self.overscrollView { overscrollView = current @@ -1271,22 +1271,24 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } let expandDistance = max(-offset - 12.0, 0.0) - let expandProgress: CGFloat = min(1.0, expandDistance / 90.0) + let expandProgress: CGFloat = min(1.0, expandDistance / 94.0) - if let _ = nextChannelToRead { - let previousType = self.currentOverscrollExpandProgress >= 0.99 - let currentType = expandProgress >= 0.99 + let previousType = self.currentOverscrollExpandProgress >= 1.0 + let currentType = expandProgress >= 1.0 - if previousType != currentType { - if self.feedback == nil { - self.feedback = HapticFeedback() - } - self.feedback?.tap() + if previousType != currentType { + if self.feedback == nil { + self.feedback = HapticFeedback() + } + if let _ = nextChannelToRead { + self.feedback?.tap() + } else { + self.feedback?.success() } - - self.currentOverscrollExpandProgress = expandProgress } + self.currentOverscrollExpandProgress = expandProgress + if let nextChannelToRead = self.nextChannelToRead { let swipeText: (String, [(Int, NSRange)]) let releaseText: (String, [(Int, NSRange)]) @@ -1307,7 +1309,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if expandProgress < 0.1 { chatControllerNode.setChatInputPanelOverscrollNode(overscrollNode: nil) - } else if expandProgress >= 0.99 { + } else if expandProgress >= 1.0 { if chatControllerNode.inputPanelOverscrollNode?.text.0 != releaseText.0 { chatControllerNode.setChatInputPanelOverscrollNode(overscrollNode: ChatInputPanelOverscrollNode(text: releaseText, color: self.currentPresentationData.theme.theme.rootController.navigationBar.secondaryTextColor, priority: 1)) }