From 0aa45a4b87589605acdd8ee721a59b229fd6c36a Mon Sep 17 00:00:00 2001 From: Peter <> Date: Mon, 19 Nov 2018 21:42:29 +0300 Subject: [PATCH] Fixed presenting controllers from in-app browser Fixed image jitter when sending messages after a photo Added ability to select peers from chat list in privacy restrictions Fixed glitches in music player UI --- TelegramUI/BlockedPeersController.swift | 7 ++-- TelegramUI/ChannelMembersController.swift | 2 +- TelegramUI/ContactListNode.swift | 32 ++++++++++++-- .../ContactMultiselectionController.swift | 18 ++++---- .../ContactMultiselectionControllerNode.swift | 8 +++- TelegramUI/GroupInfoController.swift | 2 +- TelegramUI/ItemListDisclosureItem.swift | 2 +- TelegramUI/OpenUrl.swift | 11 +++-- TelegramUI/OverlayPlayerControlsNode.swift | 17 +++++++- ...ectivePrivacySettingsPeersController.swift | 42 +++++++++---------- TelegramUI/TelegramController.swift | 15 ++++++- TelegramUI/TransformImageNode.swift | 38 +++++++++++++---- 12 files changed, 139 insertions(+), 55 deletions(-) diff --git a/TelegramUI/BlockedPeersController.swift b/TelegramUI/BlockedPeersController.swift index e8df493f7d..e3391a89aa 100644 --- a/TelegramUI/BlockedPeersController.swift +++ b/TelegramUI/BlockedPeersController.swift @@ -112,7 +112,7 @@ private enum BlockedPeersEntry: ItemListNodeEntry { func item(_ arguments: BlockedPeersControllerArguments) -> ListViewItem { switch self { case let .add(theme, text): - return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { + return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, sectionId: self.section, editing: false, action: { arguments.addPeer() }) case let .peerItem(_, theme, strings, dateTimeFormat, peer, editing, enabled): @@ -175,7 +175,7 @@ private func blockedPeersControllerEntries(presentationData: PresentationData, s var entries: [BlockedPeersEntry] = [] if let peers = peers { - entries.append(.add(presentationData.theme, presentationData.strings.Conversation_BlockUser)) + entries.append(.add(presentationData.theme, presentationData.strings.BlockedUsers_BlockUser)) var index: Int32 = 0 for peer in peers { @@ -213,7 +213,8 @@ public func blockedPeersController(account: Account) -> ViewController { } } }, addPeer: { - let controller = PeerSelectionController(account: account, filter: [.onlyUsers]) + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + let controller = PeerSelectionController(account: account, filter: [.onlyUsers], title: presentationData.strings.BlockedUsers_SelectUserTitle) controller.peerSelected = { [weak controller] peerId in if let strongController = controller { strongController.inProgress = true diff --git a/TelegramUI/ChannelMembersController.swift b/TelegramUI/ChannelMembersController.swift index ae7d2eae32..53ab1838b4 100644 --- a/TelegramUI/ChannelMembersController.swift +++ b/TelegramUI/ChannelMembersController.swift @@ -326,7 +326,7 @@ public func channelMembersController(account: Account, peerId: PeerId) -> ViewCo let arguments = ChannelMembersControllerArguments(account: account, addMember: { actionsDisposable.add((peersPromise.get() |> take(1) |> deliverOnMainQueue).start(next: { members in let disabledIds = members?.compactMap({$0.peer.id}) ?? [] - let contactsController = ContactMultiselectionController(account: account, mode: .peerSelection, options: [], filters: [.excludeSelf, .disable(disabledIds)]) + let contactsController = ContactMultiselectionController(account: account, mode: .peerSelection(searchChatList: false), options: [], filters: [.excludeSelf, .disable(disabledIds)]) let addMembers: ([ContactListPeerId]) -> Signal = { members -> Signal in let peerIds = members.compactMap { contact -> PeerId? in diff --git a/TelegramUI/ContactListNode.swift b/TelegramUI/ContactListNode.swift index 4ba24c8268..a2420f45dc 100644 --- a/TelegramUI/ContactListNode.swift +++ b/TelegramUI/ContactListNode.swift @@ -479,7 +479,7 @@ public struct ContactListAdditionalOption: Equatable { enum ContactListPresentation { case orderedByPresence(options: [ContactListAdditionalOption]) case natural(displaySearch: Bool, options: [ContactListAdditionalOption]) - case search(signal: Signal, searchDeviceContacts: Bool) + case search(signal: Signal, searchChatList: Bool, searchDeviceContacts: Bool) } struct ContactListNodeGroupSelectionState: Equatable { @@ -602,10 +602,36 @@ final class ContactListNode: ASDisplayNode { let selectionStateSignal = self.selectionStatePromise.get() let transition: Signal let themeAndStringsPromise = self.themeAndStringsPromise - if case let .search(query, searchDeviceContacts) = presentation { + if case let .search(query, searchChatList, searchDeviceContacts) = presentation { transition = query |> mapToSignal { query in - let foundLocalContacts = account.postbox.searchContacts(query: query.lowercased()) + let foundLocalContacts: Signal<([Peer], [PeerId : PeerPresence]), NoError> + if searchChatList { + let foundChatListPeers = account.postbox.searchPeers(query: query.lowercased(), groupId: nil) + foundLocalContacts = foundChatListPeers + |> mapToSignal { peers -> Signal<([Peer], [PeerId : PeerPresence]), NoError> in + var resultPeers: [Peer] = [] + for peer in peers { + if peer.peerId.namespace != Namespaces.Peer.CloudUser { + continue + } + if let mainPeer = peer.chatMainPeer { + resultPeers.append(mainPeer) + } + } + return account.postbox.transaction { transaction -> ([Peer], [PeerId : PeerPresence]) in + var resultPresences: [PeerId: PeerPresence] = [:] + for peer in resultPeers { + if let presence = transaction.getPeerPresence(peerId: peer.id) { + resultPresences[peer.id] = presence + } + } + return (resultPeers, resultPresences) + } + } + } else { + foundLocalContacts = account.postbox.searchContacts(query: query.lowercased()) + } let foundRemoteContacts: Signal<([FoundPeer], [FoundPeer]), NoError> = .single(([], [])) |> then( searchPeers(account: account, query: query) diff --git a/TelegramUI/ContactMultiselectionController.swift b/TelegramUI/ContactMultiselectionController.swift index 40de4da66e..ad0af73fdb 100644 --- a/TelegramUI/ContactMultiselectionController.swift +++ b/TelegramUI/ContactMultiselectionController.swift @@ -7,7 +7,7 @@ import TelegramCore enum ContactMultiselectionControllerMode { case groupCreation - case peerSelection + case peerSelection(searchChatList: Bool) case channelCreation } @@ -23,7 +23,6 @@ class ContactMultiselectionController: ViewController { var dismissed: (() -> Void)? - private let index: PeerNameIndex = .lastNameFirst private var _ready = Promise() @@ -62,6 +61,7 @@ class ContactMultiselectionController: ViewController { private var limitsConfigurationDisposable: Disposable? private let options: [ContactListAdditionalOption] private let filters: [ContactListFilter] + init(account: Account, mode: ContactMultiselectionControllerMode, options: [ContactListAdditionalOption], filters: [ContactListFilter] = [.excludeSelf]) { self.account = account self.mode = mode @@ -303,12 +303,14 @@ class ContactMultiselectionController: ViewController { override open func dismiss(completion: (() -> Void)? = nil) { if let presentationArguments = self.presentationArguments as? ViewControllerPresentationArguments { switch presentationArguments.presentationAnimation { - case .modalSheet: - self.dismissed?() - self.contactsNode.animateOut(completion: completion) - case .none: - self.dismissed?() - completion?() + case .modalSheet: + self.contactsNode.animateOut(completion: { [weak self] in + self?.dismissed?() + completion?() + }) + case .none: + self.dismissed?() + completion?() } } } diff --git a/TelegramUI/ContactMultiselectionControllerNode.swift b/TelegramUI/ContactMultiselectionControllerNode.swift index c81675d999..0810dea8ef 100644 --- a/TelegramUI/ContactMultiselectionControllerNode.swift +++ b/TelegramUI/ContactMultiselectionControllerNode.swift @@ -95,7 +95,11 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { selectionState = state return state } - let searchResultsNode = ContactListNode(account: account, presentation: .search(signal: searchText.get(), searchDeviceContacts: false), filters: filters, selectionState: selectionState) + var searchChatList = false + if case let .peerSelection(value) = mode { + searchChatList = value + } + let searchResultsNode = ContactListNode(account: account, presentation: .search(signal: searchText.get(), searchChatList: searchChatList, searchDeviceContacts: false), filters: filters, selectionState: selectionState) searchResultsNode.openPeer = { peer in self?.tokenListNode.setText("") self?.openPeer?(peer) @@ -170,7 +174,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { } func animateOut(completion: (() -> Void)?) { - self.layer.animatePosition(from: self.layer.position, to: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), duration: 0.2, timingFunction: kCAMediaTimingFunctionEaseInEaseOut, removeOnCompletion: false, completion: { [weak self] _ in + self.layer.animatePosition(from: self.layer.position, to: CGPoint(x: 0.0, y: self.layer.bounds.size.height), duration: 0.2, timingFunction: kCAMediaTimingFunctionEaseInEaseOut, removeOnCompletion: false, additive: true, completion: { [weak self] _ in if let strongSelf = self { strongSelf.dismiss?() completion?() diff --git a/TelegramUI/GroupInfoController.swift b/TelegramUI/GroupInfoController.swift index b01ed8f41f..160c8c8306 100644 --- a/TelegramUI/GroupInfoController.swift +++ b/TelegramUI/GroupInfoController.swift @@ -1360,7 +1360,7 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl } }) } else { - contactsController = ContactMultiselectionController(account: account, mode: .peerSelection, options: options, filters: [.excludeSelf, .disable(recentIds)]) + contactsController = ContactMultiselectionController(account: account, mode: .peerSelection(searchChatList: false), options: options, filters: [.excludeSelf, .disable(recentIds)]) } confirmationImpl = { [weak contactsController] peerId in diff --git a/TelegramUI/ItemListDisclosureItem.swift b/TelegramUI/ItemListDisclosureItem.swift index 2f380e9d7a..51f40dd36f 100644 --- a/TelegramUI/ItemListDisclosureItem.swift +++ b/TelegramUI/ItemListDisclosureItem.swift @@ -37,7 +37,7 @@ class ItemListDisclosureItem: ListViewItem, ItemListItem { let disclosureStyle: ItemListDisclosureStyle let action: (() -> Void)? - init(theme: PresentationTheme, icon: UIImage? = nil, title: String, kind: ItemListDisclosureKind = .generic, titleColor: ItemListDisclosureItemTitleColor = .primary, label: String, labelStyle: ItemListDisclosureLabelStyle = .text, sectionId: ItemListSectionId, style: ItemListStyle, disclosureStyle: ItemListDisclosureStyle = .arrow, action: (() -> Void)?) { + init(theme: PresentationTheme, icon: UIImage? = nil, title: String, kind: ItemListDisclosureKind = .generic, titleColor: ItemListDisclosureItemTitleColor = .primary, label: String, labelStyle: ItemListDisclosureLabelStyle = .text, sectionId: ItemListSectionId, style: ItemListStyle, disclosureStyle: ItemListDisclosureStyle = .arrow, action: (() -> Void)?) { self.theme = theme self.icon = icon self.title = title diff --git a/TelegramUI/OpenUrl.swift b/TelegramUI/OpenUrl.swift index 1b206fb647..ef2aed0420 100644 --- a/TelegramUI/OpenUrl.swift +++ b/TelegramUI/OpenUrl.swift @@ -191,10 +191,11 @@ public func openExternalUrl(account: Account, context: OpenURLContext = .generic break } }, present: { c, a in - if let navigationController = navigationController { - navigationController.view.window?.rootViewController?.dismiss(animated: true, completion: nil) - (navigationController.viewControllers.last as? ViewController)?.present(c, in: .window(.root), with: a) - } + account.telegramApplicationContext.applicationBindings.dismissNativeController() + + c.presentationArguments = a + + account.telegramApplicationContext.applicationBindings.getWindowHost()?.present(c, on: .root, blockInteraction: false) }, dismissInput: { dismissInput() }) @@ -217,6 +218,7 @@ public func openExternalUrl(account: Account, context: OpenURLContext = .generic } } if let peerId = peerId, let navigationController = navigationController { + account.telegramApplicationContext.applicationBindings.dismissNativeController() navigateToChatController(navigationController: navigationController, account: account, chatLocation: .peer(peerId)) } } @@ -315,6 +317,7 @@ public func openExternalUrl(account: Account, context: OpenURLContext = .generic } } if let navigationController = navigationController { + account.telegramApplicationContext.applicationBindings.dismissNativeController() (navigationController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet)) } } diff --git a/TelegramUI/OverlayPlayerControlsNode.swift b/TelegramUI/OverlayPlayerControlsNode.swift index 4e2b5133c1..2d55a0da55 100644 --- a/TelegramUI/OverlayPlayerControlsNode.swift +++ b/TelegramUI/OverlayPlayerControlsNode.swift @@ -178,7 +178,20 @@ final class OverlayPlayerControlsNode: ASDisplayNode { self.addSubnode(self.separatorNode) - let mappedStatus = combineLatest(status, self.scrubberNode.scrubbingTimestamp) |> map { value, scrubbingTimestamp -> MediaPlayerStatus in + let delayedStatus = status + |> mapToSignal { value -> Signal in + guard let value = value else { + return .single(nil) + } + switch value { + case .state: + return .single(value) + case .loading: + return .single(value) |> delay(0.1, queue: .mainQueue()) + } + } + + let mappedStatus = combineLatest(delayedStatus, self.scrubberNode.scrubbingTimestamp) |> map { value, scrubbingTimestamp -> MediaPlayerStatus in if let valueOrLoading = value, case let .state(value) = valueOrLoading { return MediaPlayerStatus(generationTimestamp: value.status.generationTimestamp, duration: value.status.duration, dimensions: value.status.dimensions, timestamp: scrubbingTimestamp ?? value.status.timestamp, baseRate: value.status.baseRate, seekId: value.status.seekId, status: value.status.status) } else { @@ -189,7 +202,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { self.leftDurationLabel.status = mappedStatus self.rightDurationLabel.status = mappedStatus - self.statusDisposable = (status + self.statusDisposable = (delayedStatus |> deliverOnMainQueue).start(next: { [weak self] value in if let strongSelf = self { var valueItemId: SharedMediaPlaylistItemId? diff --git a/TelegramUI/SelectivePrivacySettingsPeersController.swift b/TelegramUI/SelectivePrivacySettingsPeersController.swift index 14a9228529..e8cd7af072 100644 --- a/TelegramUI/SelectivePrivacySettingsPeersController.swift +++ b/TelegramUI/SelectivePrivacySettingsPeersController.swift @@ -248,32 +248,32 @@ public func selectivePrivacyPeersController(account: Account, title: String, ini removePeerDisposable.set(applyPeers.start()) }, addPeer: { - let controller = ContactMultiselectionController(account: account, mode: .peerSelection, options: []) + let controller = ContactMultiselectionController(account: account, mode: .peerSelection(searchChatList: true), options: []) addPeerDisposable.set((controller.result |> take(1) |> deliverOnMainQueue).start(next: { [weak controller] peerIds in let applyPeers: Signal = peersPromise.get() - |> take(1) - |> mapToSignal { peers -> Signal<[Peer], NoError> in - return account.postbox.transaction { transaction -> [Peer] in - var updatedPeers = peers - var existingIds = Set(updatedPeers.map { $0.id }) - for peerId in peerIds { - guard case let .peer(peerId) = peerId else { - continue - } - if let peer = transaction.getPeer(peerId), !existingIds.contains(peerId) { - existingIds.insert(peerId) - updatedPeers.append(peer) - } + |> take(1) + |> mapToSignal { peers -> Signal<[Peer], NoError> in + return account.postbox.transaction { transaction -> [Peer] in + var updatedPeers = peers + var existingIds = Set(updatedPeers.map { $0.id }) + for peerId in peerIds { + guard case let .peer(peerId) = peerId else { + continue + } + if let peer = transaction.getPeer(peerId), !existingIds.contains(peerId) { + existingIds.insert(peerId) + updatedPeers.append(peer) } - return updatedPeers } + return updatedPeers } - |> deliverOnMainQueue - |> mapToSignal { updatedPeers -> Signal in - peersPromise.set(.single(updatedPeers)) - updated(updatedPeers.map { $0.id }) - return .complete() - } + } + |> deliverOnMainQueue + |> mapToSignal { updatedPeers -> Signal in + peersPromise.set(.single(updatedPeers)) + updated(updatedPeers.map { $0.id }) + return .complete() + } removePeerDisposable.set(applyPeers.start()) controller?.dismiss() diff --git a/TelegramUI/TelegramController.swift b/TelegramUI/TelegramController.swift index abce796dec..1a5e0f2235 100644 --- a/TelegramUI/TelegramController.swift +++ b/TelegramUI/TelegramController.swift @@ -405,7 +405,20 @@ public class TelegramController: ViewController { mediaAccessoryPanel.containerNode.headerNode.playbackItem = item if let mediaManager = self.account.telegramApplicationContext.mediaManager { - mediaAccessoryPanel.containerNode.headerNode.playbackStatus = mediaManager.globalMediaPlayerState + let delayedStatus = mediaManager.globalMediaPlayerState + |> mapToSignal { value -> Signal<(SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError> in + guard let value = value else { + return .single(nil) + } + switch value.0 { + case .state: + return .single(value) + case .loading: + return .single(value) |> delay(0.1, queue: .mainQueue()) + } + } + + mediaAccessoryPanel.containerNode.headerNode.playbackStatus = delayedStatus |> map { state -> MediaPlayerStatus in if let stateOrLoading = state?.0, case let .state(state) = stateOrLoading { return state.status diff --git a/TelegramUI/TransformImageNode.swift b/TelegramUI/TransformImageNode.swift index 5ed6c36c8d..c840a4b244 100644 --- a/TelegramUI/TransformImageNode.swift +++ b/TelegramUI/TransformImageNode.swift @@ -20,6 +20,8 @@ public class TransformImageNode: ASDisplayNode { public var contentAnimations: TransformImageNodeContentAnimations = [] private var disposable = MetaDisposable() + private var currentTransform: ((TransformImageArguments) -> DrawingContext?)? + private var currentArguments: TransformImageArguments? private var argumentsPromise = ValuePromise(ignoreRepeated: true) private var overlayColor: UIColor? @@ -42,12 +44,12 @@ public class TransformImageNode: ASDisplayNode { let result = combineLatest(signal, argumentsPromise.get()) |> deliverOn(Queue.concurrentDefaultQueue()) - |> mapToThrottled { transform, arguments -> Signal in + |> mapToThrottled { transform, arguments -> Signal<((TransformImageArguments) -> DrawingContext?, TransformImageArguments, UIImage?)?, NoError> in return deferred { if let context = transform(arguments) { - return Signal.single(context.generateImage()) + return .single((transform, arguments, context.generateImage())) } else { - return Signal.single(nil) + return .single(nil) } } } @@ -69,7 +71,11 @@ public class TransformImageNode: ASDisplayNode { }) } - strongSelf.contents = next?.cgImage + if let (transform, arguments, image) = next { + strongSelf.currentTransform = transform + strongSelf.currentArguments = arguments + strongSelf.contents = image?.cgImage + } if let _ = strongSelf.overlayColor { strongSelf.applyOverlayColor(animated: false) } @@ -89,11 +95,27 @@ public class TransformImageNode: ASDisplayNode { } public func asyncLayout() -> (TransformImageArguments) -> (() -> Void) { - return { arguments in - self.argumentsPromise.set(arguments) - + let currentTransform = self.currentTransform + let currentArguments = self.currentArguments + return { [weak self] arguments in + let updatedImage: UIImage? + if currentArguments != arguments { + updatedImage = currentTransform?(arguments)?.generateImage() + } else { + updatedImage = nil + } return { - + guard let strongSelf = self else { + return + } + if let image = updatedImage { + strongSelf.contents = image.cgImage + strongSelf.currentArguments = arguments + if let _ = strongSelf.overlayColor { + strongSelf.applyOverlayColor(animated: false) + } + } + strongSelf.argumentsPromise.set(arguments) } } }