diff --git a/submodules/AccountContext/Sources/ChatListController.swift b/submodules/AccountContext/Sources/ChatListController.swift index ad9ac4ee69..ec9fb811e8 100644 --- a/submodules/AccountContext/Sources/ChatListController.swift +++ b/submodules/AccountContext/Sources/ChatListController.swift @@ -10,5 +10,5 @@ public protocol ChatListController: ViewController { func activateSearch() func deactivateSearch(animated: Bool) func activateCompose() - func maybeAskForPeerChatRemoval(peer: RenderedPeer, deleteGloballyIfPossible: Bool, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) + func maybeAskForPeerChatRemoval(peer: RenderedPeer, joined: Bool, deleteGloballyIfPossible: Bool, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) } diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index 0aaee436a0..7743bc1ad4 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -46,7 +46,7 @@ enum ChatContextMenuSource { case search(ChatListSearchContextActionSource) } -func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: ChatListNodeEntryPromoInfo?, source: ChatContextMenuSource, chatListController: ChatListControllerImpl?) -> Signal<[ContextMenuItem], NoError> { +func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: ChatListNodeEntryPromoInfo?, source: ChatContextMenuSource, chatListController: ChatListControllerImpl?, joined: Bool) -> Signal<[ContextMenuItem], NoError> { let presentationData = context.sharedContext.currentPresentationData.with({ $0 }) let strings = presentationData.strings return context.account.postbox.transaction { [weak chatListController] transaction -> [ContextMenuItem] in @@ -233,7 +233,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch updatedItems.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController)) + c.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController, joined: joined)) }))) return updatedItems @@ -379,7 +379,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch if case .chatList = source, groupAndIndex != nil { items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in if let chatListController = chatListController { - chatListController.deletePeerChat(peerId: peerId) + chatListController.deletePeerChat(peerId: peerId, joined: joined) } f(.default) }))) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index a949a737a9..44e06dca1e 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -575,11 +575,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, strongSelf.hidePsa(peerId) } - self.chatListDisplayNode.containerNode.deletePeerChat = { [weak self] peerId in + self.chatListDisplayNode.containerNode.deletePeerChat = { [weak self] peerId, joined in guard let strongSelf = self else { return } - strongSelf.deletePeerChat(peerId: peerId) + strongSelf.deletePeerChat(peerId: peerId, joined: joined) } self.chatListDisplayNode.containerNode.peerSelected = { [weak self] peer, animated, promoInfo in @@ -801,6 +801,16 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, gesture?.cancel() return } + + var joined = false + if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first { + for media in message.media { + if let action = media as? TelegramMediaAction, action.action == .peerJoined { + joined = true + } + } + } + switch item.content { case let .groupReference(groupReference): let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupReference.groupId, controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) @@ -810,7 +820,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, case let .peer(_, peer, _, _, _, _, _, _, promoInfo, _, _, _): let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf), reactionItems: [], gesture: gesture) + let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined), reactionItems: [], gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) } } @@ -823,7 +833,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf), reactionItems: [], gesture: gesture) + let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false), reactionItems: [], gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) } @@ -2069,7 +2079,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, let _ = hideAccountPromoInfoChat(account: self.context.account, peerId: id).start() } - func deletePeerChat(peerId: PeerId) { + func deletePeerChat(peerId: PeerId, joined: Bool) { let _ = (self.context.account.postbox.transaction { transaction -> RenderedPeer? in guard let peer = transaction.getPeer(peerId) else { return nil @@ -2099,7 +2109,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, } if let user = chatPeer as? TelegramUser, user.botInfo == nil, canRemoveGlobally { - strongSelf.maybeAskForPeerChatRemoval(peer: peer, completion: { _ in }, removed: {}) + strongSelf.maybeAskForPeerChatRemoval(peer: peer, joined: joined, completion: { _ in }, removed: {}) } else { let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) var items: [ActionSheetItem] = [] @@ -2189,16 +2199,24 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, if canRemoveGlobally { let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) var items: [ActionSheetItem] = [] - + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in - beginClear(.forEveryone) - actionSheet?.dismissAnimated() - })) - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in - beginClear(.forLocalPeer) - actionSheet?.dismissAnimated() - })) + + if joined || mainPeer.isDeleted { + items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Delete, color: .destructive, action: { [weak actionSheet] in + beginClear(.forEveryone) + actionSheet?.dismissAnimated() + })) + } else { + items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in + beginClear(.forEveryone) + actionSheet?.dismissAnimated() + })) + items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in + beginClear(.forLocalPeer) + actionSheet?.dismissAnimated() + })) + } actionSheet.setItemGroups([ ActionSheetItemGroup(items: items), @@ -2258,7 +2276,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, }) } - public func maybeAskForPeerChatRemoval(peer: RenderedPeer, deleteGloballyIfPossible: Bool = false, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) { + public func maybeAskForPeerChatRemoval(peer: RenderedPeer, joined: Bool = false, deleteGloballyIfPossible: Bool = false, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) { guard let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else { completion(false) return @@ -2279,31 +2297,41 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, var items: [ActionSheetItem] = [] items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder)) - items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak self, weak actionSheet] in - actionSheet?.dismissAnimated() - guard let strongSelf = self else { - return - } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText, actions: [ - TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { - completion(false) - }), - TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: { - self?.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { - removed() - }) - completion(true) - }) - ], parseMarkdown: true), in: .window(.root)) - })) - items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak self, weak actionSheet] in - actionSheet?.dismissAnimated() - self?.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { - removed() - }) - completion(true) - })) + if joined || mainPeer.isDeleted { + items.append(ActionSheetButtonItem(title: self.presentationData.strings.Common_Delete, color: .destructive, action: { [weak self, weak actionSheet] in + actionSheet?.dismissAnimated() + self?.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { + removed() + }) + completion(true) + })) + } else { + items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak self, weak actionSheet] in + actionSheet?.dismissAnimated() + guard let strongSelf = self else { + return + } + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText, actions: [ + TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { + completion(false) + }), + TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: { + self?.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { + removed() + }) + completion(true) + }) + ], parseMarkdown: true), in: .window(.root)) + })) + items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak self, weak actionSheet] in + actionSheet?.dismissAnimated() + self?.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { + removed() + }) + completion(true) + })) + } actionSheet.setItemGroups([ ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 6f4eb396bf..d336726443 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -195,7 +195,7 @@ private final class ChatListShimmerNode: ASDisplayNode { let timestamp1: Int32 = 100000 let peers = SimpleDictionary() let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ 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 + }, 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 }) @@ -478,8 +478,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { itemNode.listNode.hidePsa = { [weak self] peerId in self?.hidePsa?(peerId) } - itemNode.listNode.deletePeerChat = { [weak self] peerId in - self?.deletePeerChat?(peerId) + itemNode.listNode.deletePeerChat = { [weak self] peerId, joined in + self?.deletePeerChat?(peerId, joined) } itemNode.listNode.peerSelected = { [weak self] peerId, a, b in self?.peerSelected?(peerId, a, b) @@ -527,7 +527,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { var present: ((ViewController) -> Void)? var toggleArchivedFolderHiddenByDefault: (() -> Void)? var hidePsa: ((PeerId) -> Void)? - var deletePeerChat: ((PeerId) -> Void)? + var deletePeerChat: ((PeerId, Bool) -> Void)? var peerSelected: ((Peer, Bool, ChatListNodeEntryPromoInfo?) -> Void)? var groupSelected: ((PeerGroupId) -> Void)? var updatePeerGrouping: ((PeerId, Bool) -> Void)? diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 33af650006..96189597db 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -1028,7 +1028,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in - }, deletePeer: { _ in + }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in }, toggleArchivedFolderHiddenByDefault: { diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 7edf5afe3b..f29befbd66 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -1946,7 +1946,15 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { item.interaction.setPeerMuted(item.index.messageIndex.id.peerId, false) close = false case RevealOptionKey.delete.rawValue: - item.interaction.deletePeer(item.index.messageIndex.id.peerId) + var joined = false + if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first { + for media in message.media { + if let action = media as? TelegramMediaAction, action.action == .peerJoined { + joined = true + } + } + } + item.interaction.deletePeer(item.index.messageIndex.id.peerId, joined) case RevealOptionKey.archive.rawValue: item.interaction.updatePeerGrouping(item.index.messageIndex.id.peerId, true) close = false diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 326c5bcb5a..0506f6deb4 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -59,7 +59,7 @@ public final class ChatListNodeInteraction { let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void let setItemPinned: (PinnedItemId, Bool) -> Void let setPeerMuted: (PeerId, Bool) -> Void - let deletePeer: (PeerId) -> Void + let deletePeer: (PeerId, Bool) -> Void let updatePeerGrouping: (PeerId, Bool) -> Void let togglePeerMarkedUnread: (PeerId, Bool) -> Void let toggleArchivedFolderHiddenByDefault: () -> Void @@ -70,7 +70,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 (PeerId) -> 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) -> 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, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> 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 @@ -430,7 +430,7 @@ public final class ChatListNode: ListView { public var groupSelected: ((PeerGroupId) -> Void)? public var addContact: ((String) -> Void)? public var activateSearch: (() -> Void)? - public var deletePeerChat: ((PeerId) -> Void)? + public var deletePeerChat: ((PeerId, Bool) -> Void)? public var updatePeerGrouping: ((PeerId, Bool) -> Void)? public var presentAlert: ((String) -> Void)? public var present: ((ViewController) -> Void)? @@ -628,8 +628,8 @@ public final class ChatListNode: ListView { } self?.setCurrentRemovingPeerId(nil) }) - }, deletePeer: { [weak self] peerId in - self?.deletePeerChat?(peerId) + }, deletePeer: { [weak self] peerId, joined in + self?.deletePeerChat?(peerId, joined) }, updatePeerGrouping: { [weak self] peerId, group in self?.updatePeerGrouping?(peerId, group) }, togglePeerMarkedUnread: { [weak self, weak context] peerId, animated in diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index a548fd1bf5..faf1fefcfb 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -184,6 +184,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture public final var keepMinimalScrollHeightWithTopInset: CGFloat? + public final var itemNodeHitTest: ((CGPoint) -> Bool)? + public final var stackFromBottom: Bool = false public final var stackFromBottomInsetItemFactor: CGFloat = 0.0 public final var limitHitTestToNodes: Bool = false @@ -3876,67 +3878,73 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } self.touchesPosition = touchesPosition - self.selectionTouchLocation = touches.first!.location(in: self.view) - self.selectionTouchDelayTimer?.invalidate() - self.selectionLongTapDelayTimer?.invalidate() - self.selectionLongTapDelayTimer = nil - let timer = Timer(timeInterval: 0.08, target: ListViewTimerProxy { [weak self] in - if let strongSelf = self, strongSelf.selectionTouchLocation != nil { - strongSelf.clearHighlightAnimated(false) - - if let index = strongSelf.itemIndexAtPoint(strongSelf.touchesPosition) { - var canBeSelectedOrLongTapped = false - for itemNode in strongSelf.itemNodes { - if itemNode.index == index && (strongSelf.items[index].selectable && itemNode.canBeSelected) || itemNode.canBeLongTapped { - canBeSelectedOrLongTapped = true - } - } + var processSelection = true + if let itemNodeHitTest = self.itemNodeHitTest, !itemNodeHitTest(touchesPosition) { + processSelection = false + } + + if processSelection { + self.selectionTouchLocation = touches.first!.location(in: self.view) + self.selectionTouchDelayTimer?.invalidate() + self.selectionLongTapDelayTimer?.invalidate() + self.selectionLongTapDelayTimer = nil + let timer = Timer(timeInterval: 0.08, target: ListViewTimerProxy { [weak self] in + if let strongSelf = self, strongSelf.selectionTouchLocation != nil { + strongSelf.clearHighlightAnimated(false) - if canBeSelectedOrLongTapped { - strongSelf.highlightedItemIndex = index + if let index = strongSelf.itemIndexAtPoint(strongSelf.touchesPosition) { + var canBeSelectedOrLongTapped = false for itemNode in strongSelf.itemNodes { - if itemNode.index == index && itemNode.canBeSelected { - if true { - if !itemNode.isLayerBacked { - strongSelf.reorderItemNodeToFront(itemNode) - for (_, headerNode) in strongSelf.itemHeaderNodes { - strongSelf.reorderHeaderNodeToFront(headerNode) + if itemNode.index == index && (strongSelf.items[index].selectable && itemNode.canBeSelected) || itemNode.canBeLongTapped { + canBeSelectedOrLongTapped = true + } + } + + if canBeSelectedOrLongTapped { + strongSelf.highlightedItemIndex = index + for itemNode in strongSelf.itemNodes { + if itemNode.index == index && itemNode.canBeSelected { + if true { + if !itemNode.isLayerBacked { + strongSelf.reorderItemNodeToFront(itemNode) + for (_, headerNode) in strongSelf.itemHeaderNodes { + strongSelf.reorderHeaderNodeToFront(headerNode) + } } - } - let itemNodeFrame = itemNode.frame - let itemNodeBounds = itemNode.bounds - if strongSelf.items[index].selectable { - itemNode.setHighlighted(true, at: strongSelf.touchesPosition.offsetBy(dx: -itemNodeFrame.minX + itemNodeBounds.minX, dy: -itemNodeFrame.minY + itemNodeBounds.minY), animated: false) - } - - if itemNode.canBeLongTapped { - let timer = Timer(timeInterval: 0.3, target: ListViewTimerProxy { - if let strongSelf = self, strongSelf.highlightedItemIndex == index { - for itemNode in strongSelf.itemNodes { - if itemNode.index == index && itemNode.canBeLongTapped { - itemNode.longTapped() - strongSelf.clearHighlightAnimated(true) - strongSelf.selectionTouchLocation = nil - break + let itemNodeFrame = itemNode.frame + let itemNodeBounds = itemNode.bounds + if strongSelf.items[index].selectable { + itemNode.setHighlighted(true, at: strongSelf.touchesPosition.offsetBy(dx: -itemNodeFrame.minX + itemNodeBounds.minX, dy: -itemNodeFrame.minY + itemNodeBounds.minY), animated: false) + } + + if itemNode.canBeLongTapped { + let timer = Timer(timeInterval: 0.3, target: ListViewTimerProxy { + if let strongSelf = self, strongSelf.highlightedItemIndex == index { + for itemNode in strongSelf.itemNodes { + if itemNode.index == index && itemNode.canBeLongTapped { + itemNode.longTapped() + strongSelf.clearHighlightAnimated(true) + strongSelf.selectionTouchLocation = nil + break + } } } - } - }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false) - strongSelf.selectionLongTapDelayTimer = timer - RunLoop.main.add(timer, forMode: RunLoop.Mode.common) + }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false) + strongSelf.selectionLongTapDelayTimer = timer + RunLoop.main.add(timer, forMode: RunLoop.Mode.common) + } } + break } - break } } } } - } - }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false) - self.selectionTouchDelayTimer = timer - RunLoop.main.add(timer, forMode: RunLoop.Mode.common) - + }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false) + self.selectionTouchDelayTimer = timer + RunLoop.main.add(timer, forMode: RunLoop.Mode.common) + } super.touchesBegan(touches, with: event) self.updateScroller(transition: .immediate) diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index 26a12835c0..815b56521d 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -579,6 +579,7 @@ open class NavigationController: UINavigationController, ContainableController, var previousModalContainer: NavigationModalContainer? var visibleModalCount = 0 var topModalIsFlat = false + var isLandscape = layout.orientation == .landscape var hasVisibleStandaloneModal = false var topModalDismissProgress: CGFloat = 0.0 @@ -784,7 +785,7 @@ open class NavigationController: UINavigationController, ContainableController, let visibleRootModalDismissProgress: CGFloat var additionalModalFrameProgress: CGFloat if visibleModalCount == 1 { - effectiveRootModalDismissProgress = topModalIsFlat ? 1.0 : topModalDismissProgress + effectiveRootModalDismissProgress = (topModalIsFlat || isLandscape) ? 1.0 : topModalDismissProgress visibleRootModalDismissProgress = effectiveRootModalDismissProgress additionalModalFrameProgress = 0.0 } else if visibleModalCount >= 2 { @@ -851,7 +852,7 @@ open class NavigationController: UINavigationController, ContainableController, } let maxScale: CGFloat let maxOffset: CGFloat - if topModalIsFlat { + if topModalIsFlat || isLandscape { maxScale = 1.0 maxOffset = 0.0 } else if visibleModalCount <= 1 { diff --git a/submodules/Display/Source/Navigation/NavigationModalContainer.swift b/submodules/Display/Source/Navigation/NavigationModalContainer.swift index aa40c78ee3..247d077368 100644 --- a/submodules/Display/Source/Navigation/NavigationModalContainer.swift +++ b/submodules/Display/Source/Navigation/NavigationModalContainer.swift @@ -328,6 +328,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes self.scrollNode.view.isScrollEnabled = !isStandaloneModal + let isLandscape = layout.orientation == .landscape let containerLayout: ContainerViewLayout let containerFrame: CGRect let containerScale: CGFloat @@ -336,7 +337,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes self.panRecognizer?.isEnabled = true self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.25) self.container.clipsToBounds = true - if isStandaloneModal { + if isStandaloneModal || isLandscape { self.container.cornerRadius = 0.0 } else { self.container.cornerRadius = 10.0 @@ -351,7 +352,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes } var topInset: CGFloat - if isStandaloneModal { + if isStandaloneModal || isLandscape { topInset = 0.0 containerLayout = layout diff --git a/submodules/GameUI/Sources/GameControllerNode.swift b/submodules/GameUI/Sources/GameControllerNode.swift index 91d89e57bf..668c683ccc 100644 --- a/submodules/GameUI/Sources/GameControllerNode.swift +++ b/submodules/GameUI/Sources/GameControllerNode.swift @@ -62,6 +62,16 @@ final class GameControllerNode: ViewControllerTracingNode { }, name: "performAction") configuration.userContentController = userController + + configuration.allowsInlineMediaPlayback = true + if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { + configuration.mediaTypesRequiringUserActionForPlayback = [] + } else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { + configuration.requiresUserActionForMediaPlayback = false + } else { + configuration.mediaPlaybackRequiresUserAction = false + } + let webView = WKWebView(frame: CGRect(), configuration: configuration) if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { webView.allowsLinkPreview = false diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index a2fcb6fda9..78442b6a65 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -66,7 +66,7 @@ public final class HashtagSearchController: TelegramBaseController { }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in - }, deletePeer: { _ in + }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in }, toggleArchivedFolderHiddenByDefault: { diff --git a/submodules/ItemListUI/Sources/ItemListControllerNode.swift b/submodules/ItemListUI/Sources/ItemListControllerNode.swift index 21666e9305..59d3be2803 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerNode.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerNode.swift @@ -238,7 +238,9 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate { self.listNode = ListView() self.leftOverlayNode = ASDisplayNode() + self.leftOverlayNode.isUserInteractionEnabled = false self.rightOverlayNode = ASDisplayNode() + self.rightOverlayNode.isUserInteractionEnabled = false super.init() @@ -302,6 +304,14 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate { let _ = strongSelf.contentScrollingEnded?(strongSelf.listNode) } } + + self.listNode.itemNodeHitTest = { [weak self] point in + if let strongSelf = self { + return point.x > strongSelf.leftOverlayNode.frame.maxX && point.x < strongSelf.rightOverlayNode.frame.minX + } else { + return true + } + } let previousState = Atomic(value: nil) self.transitionDisposable.set(((state diff --git a/submodules/LegacyComponents/Sources/TGLocationViewController.m b/submodules/LegacyComponents/Sources/TGLocationViewController.m index 97489ab445..dd3ec6ea8c 100644 --- a/submodules/LegacyComponents/Sources/TGLocationViewController.m +++ b/submodules/LegacyComponents/Sources/TGLocationViewController.m @@ -71,6 +71,8 @@ bool _throttle; TGLocationPinAnnotationView *_ownLiveLocationView; __weak MKAnnotationView *_userLocationView; + + UIImageView *_headingArrowView; } @end @@ -162,6 +164,8 @@ [_liveLocationsDisposable dispose]; [_reloadDisposable dispose]; [_frequentUpdatesDisposable dispose]; + + [_locationManager stopUpdatingHeading]; } - (void)tg_setRightBarButtonItem:(UIBarButtonItem *)barButtonItem action:(bool)action animated:(bool)animated { @@ -438,6 +442,36 @@ { [super loadView]; + static UIImage *headingArrowImage = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^ + { + UIGraphicsBeginImageContextWithOptions(CGSizeMake(28.0f, 28.0f), false, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextClearRect(context, CGRectMake(0, 0, 28, 28)); + + CGContextSetFillColorWithColor(context, UIColorRGB(0x3393fe).CGColor); + + CGContextMoveToPoint(context, 14, 0); + CGContextAddLineToPoint(context, 19, 7); + CGContextAddLineToPoint(context, 9, 7); + CGContextClosePath(context); + CGContextFillPath(context); + + CGContextSetBlendMode(context, kCGBlendModeClear); + CGContextFillEllipseInRect(context, CGRectMake(5.0, 5.0, 18.0, 18.0)); + + headingArrowImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + }); + + _headingArrowView = [[UIImageView alloc] init]; + _headingArrowView.hidden = true; + _headingArrowView.frame = CGRectMake(0.0, 0.0, 28.0, 28.0); + _headingArrowView.image = headingArrowImage; + _tableView.scrollsToTop = false; _mapView.tapEnabled = false; @@ -495,6 +529,8 @@ { [super viewWillAppear:animated]; + [_locationManager startUpdatingHeading]; + if (self.previewMode && !animated) { UIView *contentView = [[_mapView subviews] firstObject]; @@ -950,6 +986,9 @@ { _userLocationView = view; + [_userLocationView addSubview:_headingArrowView]; + _headingArrowView.center = CGPointMake(view.frame.size.width / 2.0, view.frame.size.height / 2.0); + if (_ownLiveLocationView != nil) { [_userLocationView addSubview:_ownLiveLocationView]; @@ -982,6 +1021,14 @@ return CLLocationCoordinate2DMake(_locationAttachment.latitude, _locationAttachment.longitude); } +- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading +{ + if (newHeading != nil) { + _headingArrowView.hidden = false; + _headingArrowView.transform = CGAffineTransformMakeRotation(newHeading.magneticHeading / 180.0 * M_PI); + } +} + #pragma mark - - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m b/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m index f5a1ca7097..dbf7f8f746 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m @@ -86,7 +86,7 @@ static void setViewFrame(UIView *view, CGRect frame) { localizationPlaceholderText = TGLocalized(@"MediaPicker.AddCaption"); NSString *placeholderText = TGLocalized(@"MediaPicker.AddCaption"); - UIFont *placeholderFont = TGSystemFontOfSize(16); + UIFont *placeholderFont = TGSystemFontOfSize(17); CGSize placeholderSize = [placeholderText sizeWithFont:placeholderFont]; placeholderSize.width += 2.0f; placeholderSize.height += 2.0f; @@ -121,7 +121,7 @@ static void setViewFrame(UIView *view, CGRect frame) _placeholderLabel = [[UILabel alloc] init]; _placeholderLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; _placeholderLabel.backgroundColor = [UIColor clearColor]; - _placeholderLabel.font = TGSystemFontOfSize(16); + _placeholderLabel.font = TGSystemFontOfSize(17); _placeholderLabel.textColor = UIColorRGB(0x7f7f7f); _placeholderLabel.text = TGLocalized(@"MediaPicker.AddCaption"); _placeholderLabel.userInteractionEnabled = true; @@ -130,7 +130,7 @@ static void setViewFrame(UIView *view, CGRect frame) _inputFieldOnelineLabel = [[UILabel alloc] init]; _inputFieldOnelineLabel.backgroundColor = [UIColor clearColor]; - _inputFieldOnelineLabel.font = TGSystemFontOfSize(16); + _inputFieldOnelineLabel.font = TGSystemFontOfSize(17); _inputFieldOnelineLabel.hidden = true; _inputFieldOnelineLabel.numberOfLines = 1; _inputFieldOnelineLabel.textColor = [UIColor whiteColor]; @@ -169,7 +169,7 @@ static void setViewFrame(UIView *view, CGRect frame) _inputField.textColor = [UIColor whiteColor]; _inputField.disableFormatting = !_allowEntities; _inputField.placeholderView = _placeholderLabel; - _inputField.font = TGSystemFontOfSize(16); + _inputField.font = TGSystemFontOfSize(17); _inputField.accentColor = UIColorRGB(0x78b1f9); _inputField.clipsToBounds = true; _inputField.backgroundColor = nil; @@ -188,7 +188,7 @@ static void setViewFrame(UIView *view, CGRect frame) _inputField.internalTextView.scrollIndicatorInsets = UIEdgeInsetsMake(-inputFieldInternalEdgeInsets.top, 0, 5 - TGRetinaPixel, 0); - [_inputField setAttributedText:[TGMediaPickerCaptionInputPanel attributedStringForText:_caption entities:_entities fontSize:16.0f] keepFormatting:true animated:false]; + [_inputField setAttributedText:[TGMediaPickerCaptionInputPanel attributedStringForText:_caption entities:_entities fontSize:17.0f] keepFormatting:true animated:false]; [_inputFieldClippingContainer addSubview:_inputField]; } @@ -439,7 +439,7 @@ static void setViewFrame(UIView *view, CGRect frame) _fieldBackground.alpha = _placeholderLabel.hidden ? 1.0f : 0.0f; } - [self.inputField setAttributedText:[TGMediaPickerCaptionInputPanel attributedStringForText:_caption entities:_entities fontSize:16.0f] keepFormatting:true animated:false]; + [self.inputField setAttributedText:[TGMediaPickerCaptionInputPanel attributedStringForText:_caption entities:_entities fontSize:17.0f] keepFormatting:true animated:false]; } + (NSAttributedString *)attributedStringForText:(NSString *)text entities:(NSArray *)entities fontSize:(CGFloat)fontSize { @@ -777,14 +777,14 @@ static void setViewFrame(UIView *view, CGRect frame) if (text == nil) return nil; - NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithAttributedString:[TGMediaPickerCaptionInputPanel attributedStringForText:text entities:entities fontSize:16.0f]]; + NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithAttributedString:[TGMediaPickerCaptionInputPanel attributedStringForText:text entities:entities fontSize:17.0f]]; for (NSUInteger i = 0; i < string.length; i++) { unichar c = [text characterAtIndex:i]; if (c == '\t' || c == '\n') { - [string insertAttributedString:[[NSAttributedString alloc] initWithString:tokenString attributes:@{NSFontAttributeName:TGSystemFontOfSize(16.0f)}] atIndex:i]; + [string insertAttributedString:[[NSAttributedString alloc] initWithString:tokenString attributes:@{NSFontAttributeName:TGSystemFontOfSize(17.0f)}] atIndex:i]; break; } } diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m index b93165a11e..ef165a5e1e 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m @@ -167,13 +167,14 @@ if (recipientName.length > 0) { _arrowView = [[UIImageView alloc] initWithImage: TGComponentsImageNamed(@"PhotoPickerArrow")]; - _arrowView.alpha = 0.45f; + _arrowView.alpha = 0.6f; [_wrapperView addSubview:_arrowView]; _recipientLabel = [[UILabel alloc] init]; + _recipientLabel.alpha = 0.6; _recipientLabel.backgroundColor = [UIColor clearColor]; _recipientLabel.font = TGBoldSystemFontOfSize(13.0f); - _recipientLabel.textColor = UIColorRGBA(0xffffff, 0.45f); + _recipientLabel.textColor = UIColorRGB(0xffffff); _recipientLabel.text = recipientName; _recipientLabel.userInteractionEnabled = false; [_recipientLabel sizeToFit]; @@ -510,13 +511,7 @@ UIEdgeInsets screenEdges = [self screenEdges]; __weak TGMediaPickerGalleryInterfaceView *weakSelf = self; - - if ([itemView.headerView isKindOfClass:[TGMediaPickerScrubberHeaderView class]]) - { - TGMediaPickerScrubberHeaderView *headerView = (TGMediaPickerScrubberHeaderView *)itemView.headerView; - [headerView.scrubberView setRecipientName:_recipientLabel.text]; - } - + [self _layoutRecipientLabelForOrientation:[self interfaceOrientation] screenEdges:screenEdges hasHeaderView:(itemView.headerView != nil)]; if (_selectionContext != nil) @@ -1039,8 +1034,8 @@ { _checkButton.alpha = alpha; _muteButton.alpha = alpha; - _arrowView.alpha = alpha * 0.45f; - _recipientLabel.alpha = alpha; + _arrowView.alpha = alpha * 0.6f; + _recipientLabel.alpha = alpha * 0.6; } completion:^(BOOL finished) { if (finished) @@ -1070,8 +1065,8 @@ _muteButton.alpha = alpha; _muteButton.userInteractionEnabled = !hidden; - _arrowView.alpha = alpha * 0.45f; - _recipientLabel.alpha = alpha; + _arrowView.alpha = alpha * 0.6f; + _recipientLabel.alpha = alpha * 0.6; } if (hidden) @@ -1095,7 +1090,7 @@ { _checkButton.alpha = alpha; _muteButton.alpha = alpha; - _arrowView.alpha = alpha * 0.45f; + _arrowView.alpha = alpha * 0.6; _recipientLabel.alpha = alpha; _portraitToolbarView.alpha = alpha; _landscapeToolbarView.alpha = alpha; @@ -1132,7 +1127,7 @@ _muteButton.alpha = alpha; _muteButton.userInteractionEnabled = !hidden; - _arrowView.alpha = alpha * 0.45f; + _arrowView.alpha = alpha * 0.6; _recipientLabel.alpha = alpha; _portraitToolbarView.alpha = alpha; @@ -1391,6 +1386,8 @@ screenEdges.left += _safeAreaInset.left; screenEdges.right -= _safeAreaInset.right; + CGFloat panelInset = 0.0f; + switch (orientation) { case UIInterfaceOrientationLandscapeLeft: @@ -1402,13 +1399,10 @@ break; default: - frame = CGRectMake(screenEdges.left + 5, screenEdges.top + 6, _muteButton.frame.size.width, _muteButton.frame.size.height); + frame = CGRectMake(screenEdges.left + 5, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 45 - _safeAreaInset.bottom - panelInset - (hasHeaderView ? 64.0 : 0.0), _muteButton.frame.size.width, _muteButton.frame.size.height); break; } - if (hasHeaderView) - frame.origin.y += 64; - return frame; } @@ -1462,9 +1456,6 @@ break; } - if (hasHeaderView) - frame.origin.y += 64; - return frame; } @@ -1526,9 +1517,6 @@ _arrowView.frame = frame; _recipientLabel.frame = CGRectMake(CGRectGetMaxX(_arrowView.frame) + 6.0f, _arrowView.frame.origin.y - 2.0f, recipientWidth, _recipientLabel.frame.size.height); - - _arrowView.hidden = hasHeaderView; - _recipientLabel.hidden = hasHeaderView; } - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)__unused duration @@ -1670,15 +1658,14 @@ { [UIView performWithoutAnimation:^ { - _photoCounterButton.frame = CGRectMake(screenEdges.right - 56 - _safeAreaInset.right, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 40 - _safeAreaInset.bottom, 64, 38); + _photoCounterButton.frame = CGRectMake(screenEdges.right - 56 - _safeAreaInset.right, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 40 - _safeAreaInset.bottom - (hasHeaderView ? 64.0 : 0.0), 64, 38); - _selectedPhotosView.frame = CGRectMake(screenEdges.left + 4, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - photosViewSize - 54 - _safeAreaInset.bottom, self.frame.size.width - 4 * 2 - _safeAreaInset.right, photosViewSize); + _selectedPhotosView.frame = CGRectMake(screenEdges.left + 4, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - photosViewSize - 54 - _safeAreaInset.bottom - (hasHeaderView ? 64.0 : 0.0), self.frame.size.width - 4 * 2 - _safeAreaInset.right, photosViewSize); }]; _landscapeToolbarView.frame = CGRectMake(_landscapeToolbarView.frame.origin.x, screenEdges.top, TGPhotoEditorToolbarSize, self.frame.size.height); - CGFloat topInset = _safeAreaInset.top > FLT_EPSILON ? _safeAreaInset.top - 14.0 : 0.0f; - _headerWrapperView.frame = CGRectMake(screenEdges.left, screenEdges.top + topInset, self.frame.size.width, 64); + _headerWrapperView.frame = CGRectMake(screenEdges.left, _portraitToolbarView.frame.origin.y - 64.0 - [_captionMixin.inputPanel baseHeight], self.frame.size.width, 64.0); } break; } diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m index 4e8c6bcaea..84c7f381d7 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m @@ -71,6 +71,8 @@ bool _scrubbingPanelPresented; bool _scrubbingPanelLocked; bool _shouldResetScrubber; + NSArray *_cachedThumbnails; + UIImage *_immediateThumbnail; UILabel *_fileInfoLabel; @@ -215,13 +217,12 @@ _headerView = headerView; _headerView.autoresizingMask = UIViewAutoresizingFlexibleWidth; - _scrubberPanelView = [[UIView alloc] initWithFrame:CGRectMake(0, -64, _headerView.frame.size.width, 64)]; + _scrubberPanelView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _headerView.frame.size.width, 64)]; _scrubberPanelView.autoresizingMask = UIViewAutoresizingFlexibleWidth; - _scrubberPanelView.hidden = true; headerView.panelView = _scrubberPanelView; [_headerView addSubview:_scrubberPanelView]; - UIView *scrubberBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, -100.0f, _headerView.frame.size.width, 164.0f)]; + UIView *scrubberBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, _headerView.frame.size.width, 64.0f)]; scrubberBackgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth; scrubberBackgroundView.backgroundColor = [TGPhotoEditorInterfaceAssets toolbarTransparentBackgroundColor]; [_scrubberPanelView addSubview:scrubberBackgroundView]; @@ -301,7 +302,6 @@ _appeared = false; [self setScrubbingPanelApperanceLocked:false]; - [self setScrubbingPanelHidden:true animated:false]; [_positionTimer invalidate]; _positionTimer = nil; @@ -386,9 +386,14 @@ - (void)setItem:(TGMediaPickerGalleryVideoItem *)item synchronously:(bool)synchronously { bool itemChanged = ![item isEqual:self.item]; + bool itemIdChanged = item.uniqueId != self.item.uniqueId; [super setItem:item synchronously:synchronously]; + if (itemIdChanged) { + _immediateThumbnail = item.immediateThumbnailImage; + } + if (itemChanged) { [self _playerCleanup]; @@ -618,17 +623,15 @@ void (^changeBlock)(void) = ^ { - _scrubberPanelView.frame = CGRectMake(0.0f, -64.0f - _safeAreaInset.top, _scrubberPanelView.frame.size.width, _scrubberPanelView.frame.size.height); + _scrubberPanelView.alpha = 0.0f; }; void (^completionBlock)(BOOL) = ^(BOOL finished) { - if (finished) - _scrubberPanelView.hidden = true; }; if (animated) { - [UIView animateWithDuration:0.3f delay:0.0f options:(7 << 16) animations:changeBlock completion:completionBlock]; + [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:changeBlock completion:completionBlock]; } else { @@ -642,18 +645,17 @@ return; _scrubbingPanelPresented = true; - - _scrubberPanelView.hidden = false; + [_scrubberPanelView layoutSubviews]; [_scrubberView layoutSubviews]; void (^changeBlock)(void) = ^ { - _scrubberPanelView.frame = CGRectMake(0.0f, 0.0f, _scrubberPanelView.frame.size.width, _scrubberPanelView.frame.size.height); + _scrubberPanelView.alpha = 1.0f; }; if (animated) - [UIView animateWithDuration:0.3f delay:0.0f options:(7 << 16) animations:changeBlock completion:nil]; + [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:changeBlock completion:nil]; else changeBlock(); } @@ -708,7 +710,6 @@ { [_scrubberView resetThumbnails]; - [self setScrubbingPanelHidden:true animated:false]; [_scrubberPanelView setNeedsLayout]; [_scrubberPanelView layoutIfNeeded]; @@ -722,11 +723,14 @@ if (_containerView == nil) return; - _containerView.frame = self.bounds; + if (self.bounds.size.width > self.bounds.size.height) + _containerView.frame = self.bounds; + else + _containerView.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height - 44.0); [self _layoutPlayerView]; - _videoContentView.frame = (CGRect){CGPointZero, frame.size}; + _videoContentView.frame = (CGRect){CGPointZero, _containerView.frame.size}; if (_tooltipContainerView != nil && frame.size.width > frame.size.height) { @@ -753,8 +757,6 @@ mirrored = adjustments.cropMirrored; } -// _scrubberView.maximumLength = adjustments.sendAsGif ? TGVideoEditMaximumGifDuration : 0.0; - [self _layoutPlayerViewWithCropRect:cropRect videoFrameSize:videoFrameSize orientation:orientation mirrored:mirrored]; } @@ -1440,40 +1442,8 @@ trimEndValue = adjustments.trimEndValue; } } -// NSTimeInterval trimDuration = trimEndValue - trimStartValue; bool sendAsGif = !adjustments.sendAsGif; -// if (sendAsGif && _scrubberView.allowsTrimming) -// { -// if (trimDuration > TGVideoEditMaximumGifDuration) -// { -// trimEndValue = trimStartValue + TGVideoEditMaximumGifDuration; -// -// if (_scrubberView.value > trimEndValue) -// { -// [self stop]; -// [_scrubberView setValue:_scrubberView.trimStartValue resetPosition:true]; -// [self _seekToPosition:_scrubberView.value manual:true]; -// } -// -// _scrubberView.trimStartValue = trimStartValue; -// _scrubberView.trimEndValue = trimEndValue; -// [_scrubberView setTrimApplied:true]; -// [self updatePlayerRange:trimEndValue]; -// } -// } -// else if (_shouldResetScrubber) -// { -// trimStartValue = 0.0; -// trimEndValue = _videoDuration; -// -// _scrubberView.trimStartValue = trimStartValue; -// _scrubberView.trimEndValue = trimEndValue; -// -// [_scrubberView setTrimApplied:false]; -// [self updatePlayerRange:trimEndValue]; -// } - TGVideoEditAdjustments *updatedAdjustments = [TGVideoEditAdjustments editAdjustmentsWithOriginalSize:_videoDimensions cropRect:cropRect cropOrientation:adjustments.cropOrientation cropRotation:adjustments.cropRotation cropLockedAspectRatio:adjustments.cropLockedAspectRatio cropMirrored:adjustments.cropMirrored trimStartValue:trimStartValue trimEndValue:trimEndValue toolValues:adjustments.toolValues paintingData:adjustments.paintingData sendAsGif:sendAsGif preset:adjustments.preset]; [self.item.editingContext setAdjustments:updatedAdjustments forItem:self.item.editableMediaItem]; @@ -1596,6 +1566,23 @@ return timestamps; } +- (SSignal *)_placeholderThumbnails:(NSArray *)timestamps { + NSMutableArray *thumbnails = [[NSMutableArray alloc] init]; + + UIImage *image = _immediateThumbnail; + if (image == nil) + return [SSignal complete]; + + UIImage *blurredImage = TGBlurredRectangularImage(image, true, image.size, image.size, NULL, nil); + for (__unused NSNumber *value in timestamps) { + if (thumbnails.count == 0) + [thumbnails addObject:image]; + else + [thumbnails addObject:blurredImage]; + } + return [SSignal single:thumbnails]; +} + - (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber requestThumbnailImagesForTimestamps:(NSArray *)timestamps size:(CGSize)size isSummaryThumbnails:(bool)isSummaryThumbnails { if (timestamps.count == 0) @@ -1605,17 +1592,42 @@ TGMediaEditingContext *editingContext = self.item.editingContext; id editableItem = self.editableMediaItem; - SSignal *thumbnailsSignal = nil; - if ([self.item.asset isKindOfClass:[TGMediaAsset class]] && ![self itemIsLivePhoto]) - thumbnailsSignal = [TGMediaAssetImageSignals videoThumbnailsForAsset:self.item.asset size:size timestamps:timestamps]; - else if (avAsset != nil) - thumbnailsSignal = [avAsset mapToSignal:^SSignal *(AVAsset *avAsset) { - return [TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps]; - }]; +// SSignal *thumbnailsSignal = nil; +// if ([self.item.asset isKindOfClass:[TGMediaAsset class]] && ![self itemIsLivePhoto]) +// thumbnailsSignal = [TGMediaAssetImageSignals videoThumbnailsForAsset:self.item.asset size:size timestamps:timestamps]; +// else if (avAsset != nil) +// thumbnailsSignal = [avAsset mapToSignal:^SSignal *(AVAsset *avAsset) { +// return [TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps]; +// }]; + __strong TGMediaPickerGalleryVideoItemView *weakSelf = self; + SSignal *thumbnailsSignal = nil; + if (_cachedThumbnails != nil) { + thumbnailsSignal = [SSignal single:_cachedThumbnails]; + } else if ([self.item.asset isKindOfClass:[TGMediaAsset class]] && ![self itemIsLivePhoto]) { + thumbnailsSignal = [[self _placeholderThumbnails:timestamps] then:[[TGMediaAssetImageSignals videoThumbnailsForAsset:(TGMediaAsset *)self.item.asset size:size timestamps:timestamps] onNext:^(NSArray *images) { + __strong TGMediaPickerGalleryVideoItemView *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + if (strongSelf->_cachedThumbnails == nil) + strongSelf->_cachedThumbnails = images; + }]]; + } else if ([self.item.asset isKindOfClass:[TGCameraCapturedVideo class]]) { + thumbnailsSignal = [[((TGCameraCapturedVideo *)self.item.asset).avAsset takeLast] mapToSignal:^SSignal *(AVAsset *avAsset) { + return [[self _placeholderThumbnails:timestamps] then:[[TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps] onNext:^(NSArray *images) { + __strong TGMediaPickerGalleryVideoItemView *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + if (strongSelf->_cachedThumbnails == nil) + strongSelf->_cachedThumbnails = images; + }]]; + }]; + } + _requestingThumbnails = true; - __weak TGMediaPickerGalleryVideoItemView *weakSelf = self; [_thumbnailsDisposable setDisposable:[[[thumbnailsSignal map:^NSArray *(NSArray *images) { id adjustments = [editingContext adjustmentsForItem:editableItem]; if (adjustments.toolsApplied) { diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h index 1903dfcb0e..07e3a8613b 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h @@ -45,8 +45,6 @@ - (void)setThumbnailImage:(UIImage *)image forTimestamp:(NSTimeInterval)timestamp index:(NSInteger)index isSummaryThubmnail:(bool)isSummaryThumbnail; -- (void)setRecipientName:(NSString *)recipientName; - - (CGPoint)scrubberPositionForPosition:(NSTimeInterval)position; - (void)_updateScrubberAnimationsAndResetCurrentPosition:(bool)resetCurrentPosition; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m index 476c89aae4..c60928ba7e 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m @@ -81,9 +81,6 @@ typedef enum NSInteger _zoomedPivotTimestampIndex; NSArray *_zoomedTimestamps; NSMutableArray *_zoomedThumbnailViews; - - UIImageView *_arrowView; - UILabel *_recipientLabel; } @end @@ -417,33 +414,10 @@ typedef enum _tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; _tapGestureRecognizer.enabled = false; [_trimView addGestureRecognizer:_tapGestureRecognizer]; - - _arrowView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"PhotoPickerArrow")]; - _arrowView.alpha = 0.45f; - _arrowView.hidden = true; - [self addSubview:_arrowView]; - - _recipientLabel = [[UILabel alloc] init]; - _recipientLabel.backgroundColor = [UIColor clearColor]; - _recipientLabel.font = TGBoldSystemFontOfSize(13.0f); - _recipientLabel.textColor = UIColorRGBA(0xffffff, 0.45f); - _recipientLabel.hidden = true; - [self addSubview:_recipientLabel]; } return self; } -- (void)setRecipientName:(NSString *)recipientName -{ - _recipientLabel.text = recipientName; - _recipientLabel.hidden = recipientName.length == 0; - _arrowView.hidden = _recipientLabel.hidden; - - [_recipientLabel sizeToFit]; - - [self _layoutRecipientLabel]; -} - - (void)setHasDotPicker:(bool)hasDotPicker { _hasDotPicker = hasDotPicker; _tapGestureRecognizer.enabled = hasDotPicker; @@ -1052,11 +1026,9 @@ typedef enum - (void)_updateTimeLabels { - _currentTimeLabel.text = @""; + _currentTimeLabel.text = self.disableTimeDisplay ? @"" : [TGMediaPickerGalleryVideoScrubber _stringFromTotalSeconds:(NSInteger)self.value]; - NSString *text = [NSString stringWithFormat:@"%@ / %@", [TGMediaPickerGalleryVideoScrubber _stringFromTotalSeconds:(NSInteger)self.value], [TGMediaPickerGalleryVideoScrubber _stringFromTotalSeconds:(NSInteger)self.duration]]; - - _inverseTimeLabel.text = self.disableTimeDisplay ? @"" : text; + _inverseTimeLabel.text = self.disableTimeDisplay ? @"" : [TGMediaPickerGalleryVideoScrubber _stringFromTotalSeconds:(NSInteger)self.duration]; } #pragma mark - Scrubber Handle @@ -1466,18 +1438,6 @@ typedef enum } } -- (void)_layoutRecipientLabel -{ - if (self.frame.size.width < FLT_EPSILON) - return; - - CGFloat screenWidth = MAX(self.frame.size.width, self.frame.size.height); - CGFloat recipientWidth = MIN(_recipientLabel.frame.size.width, screenWidth - 100.0f); - - _arrowView.frame = CGRectMake(14.0f, 6.0f, _arrowView.frame.size.width, _arrowView.frame.size.height); - _recipientLabel.frame = CGRectMake(CGRectGetMaxX(_arrowView.frame) + 6.0f, _arrowView.frame.origin.y - 2.0f, recipientWidth, _recipientLabel.frame.size.height); -} - - (void)setFrame:(CGRect)frame { if (isnan(frame.origin.x) || isnan(frame.origin.y) || isnan(frame.size.width) || isnan(frame.size.height)) @@ -1501,8 +1461,6 @@ typedef enum _zoomedThumbnailWrapperView.frame = _summaryThumbnailWrapperView.frame; [self _updateScrubberAnimationsAndResetCurrentPosition:true]; - - [self _layoutRecipientLabel]; } + (NSString *)_stringFromTotalSeconds:(NSInteger)totalSeconds diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m b/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m index ebc05148dc..8a94b4c4e2 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m @@ -48,7 +48,7 @@ const CGFloat TGPhotoCounterButtonMaskFade = 18; { UIGraphicsBeginImageContextWithOptions(CGSizeMake(38.0f, 38.0f), false, 0.0f); CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.7f).CGColor); + CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.3f).CGColor); CGContextFillEllipseInRect(context, CGRectMake(3.5f, 1.0f, 31.0f, 31.0f)); diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m b/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m index 8beac5ce0e..67f1ec0008 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m @@ -126,7 +126,7 @@ CGRect rect = CGRectMake(0, 0, 39.0f, 39.0f); UIGraphicsBeginImageContextWithOptions(rect.size, false, 0); CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.6f).CGColor); + CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.3f).CGColor); CGContextFillEllipseInRect(context, CGRectInset(rect, 3, 3)); muteBackground = UIGraphicsGetImageFromCurrentImageContext(); diff --git a/submodules/LegacyComponents/Sources/TGPhotoPaintController.m b/submodules/LegacyComponents/Sources/TGPhotoPaintController.m index 3fe8874b3e..abf25402a4 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoPaintController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoPaintController.m @@ -1216,7 +1216,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f; if (![editorController isKindOfClass:[TGPhotoEditorController class]]) return; - if (hasStickers) { + if (!hasStickers) { [editorController setMinimalVideoDuration:duration]; } diff --git a/submodules/LocationUI/Sources/LocationAttributionItem.swift b/submodules/LocationUI/Sources/LocationAttributionItem.swift index 6f62515c4c..96f74ee56d 100644 --- a/submodules/LocationUI/Sources/LocationAttributionItem.swift +++ b/submodules/LocationUI/Sources/LocationAttributionItem.swift @@ -8,11 +8,18 @@ import TelegramPresentationData import ItemListUI import AppBundle +enum LocationAttribution: Equatable { + case foursquare + case google +} + class LocationAttributionItem: ListViewItem { let presentationData: ItemListPresentationData + let attribution: LocationAttribution - public init(presentationData: ItemListPresentationData) { + public init(presentationData: ItemListPresentationData, attribution: LocationAttribution) { self.presentationData = presentationData + self.attribution = attribution } public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { @@ -93,11 +100,20 @@ private class LocationAttributionItemNode: ListViewItemNode { strongSelf.layoutParams = params if let _ = updatedTheme { - strongSelf.imageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Location/FoursquareAttribution"), color: item.presentationData.theme.list.itemSecondaryTextColor) + switch item.attribution { + case .foursquare: + strongSelf.imageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Location/FoursquareAttribution"), color: item.presentationData.theme.list.itemSecondaryTextColor) + case .google: + if item.presentationData.theme.overallDarkAppearance { + strongSelf.imageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Location/GoogleAttribution"), color: item.presentationData.theme.list.itemSecondaryTextColor) + } else { + strongSelf.imageNode.image = UIImage(bundleImageName: "Location/GoogleAttribution") + } + } } if let image = strongSelf.imageNode.image { - strongSelf.imageNode.frame = CGRect(x: floor((params.width - image.size.width) / 2.0), y: 0.0, width: image.size.width, height: image.size.height) + strongSelf.imageNode.frame = CGRect(x: floor((params.width - image.size.width) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0), width: image.size.width, height: image.size.height) } } }) diff --git a/submodules/LocationUI/Sources/LocationMapNode.swift b/submodules/LocationUI/Sources/LocationMapNode.swift index 0576883bb4..c330c2017e 100644 --- a/submodules/LocationUI/Sources/LocationMapNode.swift +++ b/submodules/LocationUI/Sources/LocationMapNode.swift @@ -68,11 +68,30 @@ private class LocationMapView: MKMapView, UIGestureRecognizerDelegate { } } +private func generateHeadingArrowImage() -> UIImage? { + return generateImage(CGSize(width: 28.0, height: 28.0)) { size, context in + let bounds = CGRect(origin: CGPoint(), size: size) + context.clear(bounds) + + context.setFillColor(UIColor(rgb: 0x3393fe).cgColor) + + context.move(to: CGPoint(x: 14.0, y: 0.0)) + context.addLine(to: CGPoint(x: 19.0, y: 7.0)) + context.addLine(to: CGPoint(x: 9.0, y: 7.0)) + context.closePath() + context.fillPath() + + context.setBlendMode(.clear) + context.fillEllipse(in: bounds.insetBy(dx: 5.0, dy: 5.0)) + } +} + final class LocationMapNode: ASDisplayNode, MKMapViewDelegate { private let locationPromise = Promise(nil) private let pickerAnnotationContainerView: PickerAnnotationContainerView private weak var userLocationAnnotationView: MKAnnotationView? + private var headingArrowView: UIImageView? private let pinDisposable = MetaDisposable() @@ -103,6 +122,10 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate { override func didLoad() { super.didLoad() + self.headingArrowView = UIImageView() + self.headingArrowView?.frame = CGRect(origin: CGPoint(), size: CGSize(width: 28.0, height: 28.0)) + self.headingArrowView?.image = generateHeadingArrowImage() + self.mapView?.interactiveTransitionGestureRecognizerTest = { p in if p.x > 44.0 { return true @@ -232,6 +255,10 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate { for view in views { if view.annotation is MKUserLocation { self.userLocationAnnotationView = view + if let headingArrowView = self.headingArrowView { + view.addSubview(headingArrowView) + headingArrowView.center = CGPoint(x: view.frame.width / 2.0, y: view.frame.height / 2.0) + } if let annotationView = self.customUserLocationAnnotationView { view.addSubview(annotationView) } @@ -347,6 +374,18 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate { } } + var userHeading: CGFloat? = nil { + didSet { + if let heading = self.userHeading { + self.headingArrowView?.isHidden = false + self.headingArrowView?.transform = CGAffineTransform(rotationAngle: CGFloat(heading / 180.0 * CGFloat.pi)) + } else { + self.headingArrowView?.isHidden = true + self.headingArrowView?.transform = CGAffineTransform.identity + } + } + } + var annotations: [LocationPinAnnotation] = [] { didSet { guard let mapView = self.mapView else { diff --git a/submodules/LocationUI/Sources/LocationPickerController.swift b/submodules/LocationUI/Sources/LocationPickerController.swift index 1a150f9c18..3602c194f3 100644 --- a/submodules/LocationUI/Sources/LocationPickerController.swift +++ b/submodules/LocationUI/Sources/LocationPickerController.swift @@ -289,7 +289,7 @@ public final class LocationPickerController: ViewController { return } - self.displayNode = LocationPickerControllerNode(context: self.context, presentationData: self.presentationData, mode: self.mode, interaction: interaction) + self.displayNode = LocationPickerControllerNode(context: self.context, presentationData: self.presentationData, mode: self.mode, interaction: interaction, locationManager: self.locationManager) self.displayNodeDidLoad() self.permissionDisposable = (DeviceAccess.authorizationStatus(subject: .location(.send)) diff --git a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift index 03b0ebcdb3..4aee790229 100644 --- a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift @@ -17,6 +17,7 @@ import AppBundle import CoreLocation import Geocoding import PhoneNumberFormat +import DeviceAccess private struct LocationPickerTransaction { let deletions: [ListViewDeleteItem] @@ -40,7 +41,7 @@ private enum LocationPickerEntry: Comparable, Identifiable { case liveLocation(PresentationTheme, String, String, CLLocationCoordinate2D?) case header(PresentationTheme, String) case venue(PresentationTheme, TelegramMediaMap, Int) - case attribution(PresentationTheme) + case attribution(PresentationTheme, LocationAttribution) var stableId: LocationPickerEntryId { switch self { @@ -83,8 +84,8 @@ private enum LocationPickerEntry: Comparable, Identifiable { } else { return false } - case let .attribution(lhsTheme): - if case let .attribution(rhsTheme) = rhs, lhsTheme === rhsTheme { + case let .attribution(lhsTheme, lhsAttribution): + if case let .attribution(rhsTheme, rhsAttribution) = rhs, lhsTheme === rhsTheme, lhsAttribution == rhsAttribution { return true } else { return false @@ -131,7 +132,7 @@ private enum LocationPickerEntry: Comparable, Identifiable { func item(account: Account, presentationData: PresentationData, interaction: LocationPickerInteraction?) -> ListViewItem { switch self { - case let .location(theme, title, subtitle, venue, coordinate): + case let .location(_, title, subtitle, venue, coordinate): let icon: LocationActionListItemIcon if let venue = venue { icon = .venue(venue) @@ -147,23 +148,23 @@ private enum LocationPickerEntry: Comparable, Identifiable { }, highlighted: { highlighted in interaction?.updateSendActionHighlight(highlighted) }) - case let .liveLocation(theme, title, subtitle, coordinate): + case let .liveLocation(_, title, subtitle, coordinate): return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), account: account, title: title, subtitle: subtitle, icon: .liveLocation, action: { if let coordinate = coordinate { interaction?.sendLiveLocation(coordinate) } }) - case let .header(theme, title): + case let .header(_, title): return LocationSectionHeaderItem(presentationData: ItemListPresentationData(presentationData), title: title) - case let .venue(theme, venue, _): + case let .venue(_, venue, _): let venueType = venue.venue?.type ?? "" return ItemListVenueItem(presentationData: ItemListPresentationData(presentationData), account: account, venue: venue, style: .plain, action: { interaction?.sendVenue(venue) }, infoAction: ["home", "work"].contains(venueType) ? { interaction?.openHomeWorkInfo() } : nil) - case let .attribution(theme): - return LocationAttributionItem(presentationData: ItemListPresentationData(presentationData)) + case let .attribution(_, attribution): + return LocationAttributionItem(presentationData: ItemListPresentationData(presentationData), attribution: attribution) } } } @@ -240,12 +241,13 @@ struct LocationPickerState { } } -final class LocationPickerControllerNode: ViewControllerTracingNode { +final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationManagerDelegate { private let context: AccountContext private var presentationData: PresentationData private let presentationDataPromise: Promise private let mode: LocationPickerMode private let interaction: LocationPickerInteraction + private let locationManager: LocationManager private let listNode: ListView private let emptyResultsTextNode: ImmediateTextNode @@ -269,12 +271,13 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)? private var listOffset: CGFloat? - init(context: AccountContext, presentationData: PresentationData, mode: LocationPickerMode, interaction: LocationPickerInteraction) { + init(context: AccountContext, presentationData: PresentationData, mode: LocationPickerMode, interaction: LocationPickerInteraction, locationManager: LocationManager) { self.context = context self.presentationData = presentationData self.presentationDataPromise = Promise(presentationData) self.mode = mode self.interaction = interaction + self.locationManager = locationManager self.state = LocationPickerState() self.statePromise = Promise(self.state) @@ -496,15 +499,21 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { entries.append(.header(presentationData.theme, presentationData.strings.Map_ChooseAPlace.uppercased())) - var displayedVenues = foundVenues != nil || state.searchingVenuesAround ? foundVenues : venues + let displayedVenues = foundVenues != nil || state.searchingVenuesAround ? foundVenues : venues if let venues = displayedVenues { var index: Int = 0 + var attribution: LocationAttribution? for venue in venues { + if venue.venue?.provider == "foursquare" { + attribution = .foursquare + } else if venue.venue?.provider == "gplaces" { + attribution = .google + } entries.append(.venue(presentationData.theme, venue, index)) index += 1 } - if !venues.isEmpty { - entries.append(.attribution(presentationData.theme)) + if let attribution = attribution { + entries.append(.attribution(presentationData.theme, attribution)) } } let previousEntries = previousEntries.swap(entries) @@ -533,7 +542,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { switch previousState.selectedLocation { case .none, .venue: updateMap = true - case let .location(previousCoordinate, address): + case let .location(previousCoordinate, _): if previousCoordinate != coordinate { updateMap = true } @@ -574,7 +583,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { if let (layout, navigationBarHeight) = strongSelf.validLayout { var updateLayout = false - var transition: ContainedViewLayoutTransition = .animated(duration: 0.45, curve: .spring) + let transition: ContainedViewLayoutTransition = .animated(duration: 0.45, curve: .spring) if previousState.displayingMapModeOptions != state.displayingMapModeOptions { updateLayout = true @@ -685,11 +694,20 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { strongSelf.goToUserLocation() } } + + self.locationManager.manager.startUpdatingHeading() + self.locationManager.manager.delegate = self } deinit { self.disposable?.dispose() self.geocodingDisposable.dispose() + + self.locationManager.manager.stopUpdatingHeading() + } + + func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) { + self.headerNode.mapNode.userHeading = CGFloat(newHeading.magneticHeading) } func updatePresentationData(_ presentationData: PresentationData) { @@ -721,7 +739,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { } private func dequeueTransition() { - guard let layout = self.validLayout, let transition = self.enqueuedTransitions.first else { + guard let _ = self.validLayout, let transition = self.enqueuedTransitions.first else { return } self.enqueuedTransitions.remove(at: 0) diff --git a/submodules/OpenInExternalAppUI/Sources/OpenInOptions.swift b/submodules/OpenInExternalAppUI/Sources/OpenInOptions.swift index 8d77aeeec4..e855eae738 100644 --- a/submodules/OpenInExternalAppUI/Sources/OpenInOptions.swift +++ b/submodules/OpenInExternalAppUI/Sources/OpenInOptions.swift @@ -181,7 +181,11 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope if withDirections { return .openUrl(url: "comgooglemaps-x-callback://?daddr=\(coordinates)&directionsmode=driving&x-success=telegram://?resume=true&x-source=Telegram") } else { - return .openUrl(url: "comgooglemaps-x-callback://?center=\(coordinates)&q=\(coordinates)&x-success=telegram://?resume=true&x-source=Telegram") + if let venue = location.venue, let venueId = venue.id, let provider = venue.provider, provider == "gplaces" { + return .openUrl(url: "https://www.google.com/maps/search/?api=1&query=\(venue.address ?? "")&query_place_id=\(venueId)") + } else { + return .openUrl(url: "comgooglemaps-x-callback://?center=\(coordinates)&q=\(coordinates)&x-success=telegram://?resume=true&x-source=Telegram") + } } })) diff --git a/submodules/PeerInfoUI/Sources/ChannelInfoController.swift b/submodules/PeerInfoUI/Sources/ChannelInfoController.swift index 096bb13634..80d960096e 100644 --- a/submodules/PeerInfoUI/Sources/ChannelInfoController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelInfoController.swift @@ -1076,8 +1076,8 @@ public func channelInfoController(context: AccountContext, peerId: PeerId) -> Vi } for childController in tabController.controllers { if let chatListController = childController as? ChatListController { - chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { [weak navigationController] deleted in - if deleted { + chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), joined: false, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { [weak navigationController] removed in + if removed { navigationController?.popToRoot(animated: true) } }, removed: { diff --git a/submodules/PeerInfoUI/Sources/GroupInfoController.swift b/submodules/PeerInfoUI/Sources/GroupInfoController.swift index b662e52113..f398e98e0f 100644 --- a/submodules/PeerInfoUI/Sources/GroupInfoController.swift +++ b/submodules/PeerInfoUI/Sources/GroupInfoController.swift @@ -2439,7 +2439,7 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId: } for childController in tabController.controllers { if let chatListController = childController as? ChatListController { - chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { [weak navigationController] removed in + chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), joined: false, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { [weak navigationController] removed in if removed { navigationController?.popToRoot(animated: true) } diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index f2b4aafdf5..9c01ad1baa 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -214,7 +214,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView var items: [ChatListItem] = [] let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, additionalCategorySelected: { _ in - }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ 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/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 9ded3d0d98..5d38dc26fd 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -767,7 +767,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate var items: [ChatListItem] = [] let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, additionalCategorySelected: { _ in - }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ 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/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 2cfda99215..86079f02cf 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -351,7 +351,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { var items: [ChatListItem] = [] let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, additionalCategorySelected: { _ in - }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ 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/StatisticsUI/Sources/StatsMessageItem.swift b/submodules/StatisticsUI/Sources/StatsMessageItem.swift index 4d5c8fde37..0a8b31facc 100644 --- a/submodules/StatisticsUI/Sources/StatsMessageItem.swift +++ b/submodules/StatisticsUI/Sources/StatsMessageItem.swift @@ -173,7 +173,7 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { let leftInset = 16.0 + params.leftInset let rightInset = 16.0 + params.rightInset var totalLeftInset = leftInset - let additionalRightInset: CGFloat = 93.0 + let additionalRightInset: CGFloat = 128.0 let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize) @@ -229,9 +229,9 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: label, font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - totalLeftInset - rightInset - additionalRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - let (viewsLayout, viewsApply) = makeViewsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageViews(item.views), font: labelFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets())) + let (viewsLayout, viewsApply) = makeViewsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageViews(item.views), font: labelFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 128.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets())) - let (forwardsLayout, forwardsApply) = makeForwardsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageForwards(item.forwards), font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets())) + let (forwardsLayout, forwardsApply) = makeForwardsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageForwards(item.forwards), font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 128.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets())) let verticalInset: CGFloat = 11.0 let titleSpacing: CGFloat = 3.0 diff --git a/submodules/TelegramCore/Sources/PeerStatistics.swift b/submodules/TelegramCore/Sources/PeerStatistics.swift index fd713c698c..83f9383e9a 100644 --- a/submodules/TelegramCore/Sources/PeerStatistics.swift +++ b/submodules/TelegramCore/Sources/PeerStatistics.swift @@ -30,8 +30,8 @@ public enum StatsGraph: Equatable { switch self { case .Empty: return true - case let .Failed(error): - return error.lowercased().contains("not enough data") + case .Failed: + return true default: return false } diff --git a/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/Contents.json new file mode 100644 index 0000000000..67b12b6b3f --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "powered_by_google_on_white@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "powered_by_google_on_white@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@2x.png b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@2x.png new file mode 100644 index 0000000000..4676cb59be Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@2x.png differ diff --git a/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@3x.png b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@3x.png new file mode 100644 index 0000000000..64b2cab5aa Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@3x.png differ diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 69a59c6118..ae34a36dd3 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -570,6 +570,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if strongSelf.presentationInterfaceState.interfaceState.selectionState != nil { return } + + strongSelf.dismissAllTooltips() + let recognizer: TapLongTapOrDoubleTapGestureRecognizer? = anyRecognizer as? TapLongTapOrDoubleTapGestureRecognizer let gesture: ContextGesture? = anyRecognizer as? ContextGesture if let messages = strongSelf.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(message.id) { @@ -1985,7 +1988,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) }, displaySwipeToReplyHint: { [weak self] in if let strongSelf = self, let validLayout = strongSelf.validLayout, min(validLayout.size.width, validLayout.size.height) > 320.0 { - strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .swipeToReply(title: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintTitle, text: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintText), elevatedLayout: true, action: { _ in return false }), in: .window(.root)) + strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .swipeToReply(title: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintTitle, text: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintText), elevatedLayout: false, action: { _ in return false }), in: .current) } }, dismissReplyMarkupMessage: { [weak self] message in guard let strongSelf = self, strongSelf.presentationInterfaceState.keyboardButtonsMessage?.id == message.id else { @@ -2044,6 +2047,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } + + if strongSelf.presentationInterfaceState.interfaceState.selectionState != nil { + return + } + + strongSelf.dismissAllTooltips() + let context = strongSelf.context let _ = (context.account.postbox.transaction { transaction -> Peer? in return transaction.getPeer(peer.id) @@ -5228,18 +5238,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.dismissAllTooltips() - self.window?.forEachController({ controller in - if let controller = controller as? UndoOverlayController { - controller.dismissWithCommitAction() - } - }) - self.forEachController({ controller in - if let controller = controller as? TooltipScreen { - controller.dismiss() - } - return true - }) - self.sendMessageActionsController?.dismiss() if let _ = self.peekData { @@ -6077,7 +6075,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G disposable.set((signal |> deliverOnMainQueue).start(completed: { [weak self] in if let strongSelf = self, let _ = strongSelf.validLayout { - strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: true, action: { _ in return false }), in: .current) + strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: false, action: { _ in return false }), in: .current) } })) @@ -7118,12 +7116,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } if let value = value { - self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, account: self.context.account, text: value, action: canSendMessagesToChat(self.presentationInterfaceState) ? self.presentationData.strings.Conversation_SendDice : nil), elevatedLayout: true, action: { [weak self] action in + self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, account: self.context.account, text: value, action: canSendMessagesToChat(self.presentationInterfaceState) ? self.presentationData.strings.Conversation_SendDice : nil), elevatedLayout: false, action: { [weak self] action in if let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState), action == .undo { strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: dice.emoji)), replyToMessageId: nil, localGroupingKey: nil)]) } return false - }), in: .window(.root)) + }), in: .current) } } @@ -9179,6 +9177,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.silentPostTooltipController?.dismiss() self.mediaRecordingModeTooltipController?.dismiss() self.mediaRestrictedTooltipController?.dismiss() + + self.window?.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitAction() + } + }) + self.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitAction() + } + if let controller = controller as? TooltipScreen { + controller.dismiss() + } + return true + }) } private func commitPurposefulAction() { diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index ceaa5643f0..5d66223437 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -974,10 +974,14 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me optionsMap[id]!.insert(.deleteLocally) } else if let peer = transaction.getPeer(id.peerId) { var isAction = false + var isDice = false for media in message.media { if media is TelegramMediaAction || media is TelegramMediaExpiredContent { isAction = true } + if media is TelegramMediaDice { + isDice = true + } } if let channel = peer as? TelegramChannel { if message.flags.contains(.Incoming) { @@ -1064,6 +1068,11 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me } else if limitsConfiguration.canRemoveIncomingMessagesInPrivateChats { canDeleteGlobally = true } + + let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) + if isDice && Int64(message.timestamp) + 60 * 60 * 24 > Int64(timestamp) { + canDeleteGlobally = false + } if message.flags.contains(.Incoming) { hadPersonalIncoming = true } diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index 399e715d5a..a69e9c64c8 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -183,7 +183,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in - }, deletePeer: { _ in + }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in }, toggleArchivedFolderHiddenByDefault: { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index bdb6eebf1a..1eed0e779d 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -3837,7 +3837,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } for childController in tabController.controllers { if let chatListController = childController as? ChatListController { - chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), deleteGloballyIfPossible: globally, completion: { [weak navigationController] deleted in + chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), joined: false, deleteGloballyIfPossible: globally, completion: { [weak navigationController] deleted in if deleted { navigationController?.popToRoot(animated: true) } diff --git a/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift b/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift index aa1329692a..40cf0fec67 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift @@ -88,24 +88,24 @@ final class WebEmbedPlayerNode: ASDisplayNode, WKNavigationDelegate { let userContentController = WKUserContentController() userContentController.addUserScript(WKUserScript(source: "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta)", injectionTime: .atDocumentEnd, forMainFrameOnly: true)) - let config = WKWebViewConfiguration() - config.allowsInlineMediaPlayback = true - config.userContentController = userContentController + let configuration = WKWebViewConfiguration() + configuration.allowsInlineMediaPlayback = true + configuration.userContentController = userContentController if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { - config.mediaTypesRequiringUserActionForPlayback = [] + configuration.mediaTypesRequiringUserActionForPlayback = [] } else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - config.requiresUserActionForMediaPlayback = false + configuration.requiresUserActionForMediaPlayback = false } else { - config.mediaPlaybackRequiresUserAction = false + configuration.mediaPlaybackRequiresUserAction = false } if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - config.allowsPictureInPictureMediaPlayback = false + configuration.allowsPictureInPictureMediaPlayback = false } let frame = CGRect(origin: CGPoint.zero, size: intrinsicDimensions) - self.webView = WKWebView(frame: frame, configuration: config) + self.webView = WKWebView(frame: frame, configuration: configuration) super.init() self.frame = frame