mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Upgraded navigation
This commit is contained in:
parent
99c6240172
commit
538e3dbe70
@ -432,7 +432,7 @@ public protocol SharedAccountContext: class {
|
||||
func resolveUrl(account: Account, url: String) -> Signal<ResolvedUrl, NoError>
|
||||
func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void)
|
||||
func openAddContact(context: AccountContext, firstName: String, lastName: String, phoneNumber: String, label: String, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void)
|
||||
func openAddPersonContact(context: AccountContext, peerId: PeerId, present: @escaping (ViewController, Any?) -> Void)
|
||||
func openAddPersonContact(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void)
|
||||
func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void)
|
||||
|
||||
func navigateToCurrentCall()
|
||||
|
@ -194,6 +194,7 @@ public final class CallListController: ViewController {
|
||||
|
||||
@objc func callPressed() {
|
||||
let controller = self.context.sharedContext.makeContactSelectionController(ContactSelectionControllerParams(context: self.context, title: { $0.Calls_NewCall }))
|
||||
controller.navigationPresentation = .modal
|
||||
self.createActionDisposable.set((controller.result
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak controller, weak self] peer in
|
||||
|
@ -84,9 +84,13 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, source: ChatC
|
||||
if !isSavedMessages, let peer = peer as? TelegramUser {
|
||||
if !transaction.isPeerContact(peerId: peer.id) {
|
||||
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_AddToContacts, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||
context.sharedContext.openAddPersonContact(context: context, peerId: peerId, present: { controller, arguments in
|
||||
context.sharedContext.openAddPersonContact(context: context, peerId: peerId, pushController: { controller in
|
||||
if let navigationController = chatListController?.navigationController as? NavigationController {
|
||||
navigationController.pushViewController(controller)
|
||||
}
|
||||
}, present: { c, a in
|
||||
if let chatListController = chatListController {
|
||||
chatListController.present(controller, in: .window(.root), with: arguments)
|
||||
chatListController.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
})
|
||||
f(.default)
|
||||
@ -224,6 +228,10 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, source: ChatC
|
||||
})))
|
||||
}
|
||||
|
||||
if let item = items.last, case .separator = item {
|
||||
items.removeLast()
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
}
|
||||
|
@ -1020,8 +1020,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
|
||||
@objc private func composePressed() {
|
||||
let controller = self.context.sharedContext.makeComposeController(context: self.context)
|
||||
self.present(controller, in: .window(.root))
|
||||
//(self.navigationController as? NavigationController)?.replaceAllButRootController(self.context.sharedContext.makeComposeController(context: self.context), animated: true)
|
||||
(self.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
}
|
||||
|
||||
public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
|
||||
|
@ -387,6 +387,7 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
controller.navigationPresentation = .modal
|
||||
presentControllerImpl = { [weak controller] c, a in
|
||||
controller?.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
|
@ -523,7 +523,7 @@ public class ContactsController: ViewController {
|
||||
switch status {
|
||||
case .allowed:
|
||||
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: "", lastName: "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: "+")]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [])
|
||||
strongSelf.present(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -534,7 +534,7 @@ public class ContactsController: ViewController {
|
||||
} else {
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil))
|
||||
}
|
||||
}), completed: nil, cancelled: nil), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}), completed: nil, cancelled: nil))
|
||||
case .notDetermined:
|
||||
DeviceAccess.authorizeAccess(to: .contacts)
|
||||
default:
|
||||
|
@ -38,6 +38,8 @@ public class InviteContactsController: ViewController, MFMessageComposeViewContr
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
|
||||
self.navigationPresentation = .modal
|
||||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
|
||||
self.title = self.presentationData.strings.Contacts_InviteFriends
|
||||
|
@ -575,6 +575,7 @@ public extension ContainedViewLayoutTransition {
|
||||
func updateSublayerTransformScale(node: ASDisplayNode, scale: CGFloat, completion: ((Bool) -> Void)? = nil) {
|
||||
if !node.isNodeLoaded {
|
||||
node.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0)
|
||||
completion?(true)
|
||||
return
|
||||
}
|
||||
let t = node.layer.sublayerTransform
|
||||
@ -606,12 +607,13 @@ public extension ContainedViewLayoutTransition {
|
||||
func updateSublayerTransformScaleAndOffset(node: ASDisplayNode, scale: CGFloat, offset: CGPoint, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if !node.isNodeLoaded {
|
||||
node.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0)
|
||||
completion?(true)
|
||||
return
|
||||
}
|
||||
let t = node.layer.sublayerTransform
|
||||
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
||||
let currentOffset = CGPoint(x: t.m41, y: t.m42)
|
||||
if currentScale.isEqual(to: scale) && currentOffset == offset {
|
||||
let currentOffset = CGPoint(x: t.m41 / currentScale, y: t.m42 / currentScale)
|
||||
if abs(currentScale - scale) <= CGFloat.ulpOfOne && abs(currentOffset.x - offset.x) <= CGFloat.ulpOfOne && abs(currentOffset.y - offset.y) <= CGFloat.ulpOfOne {
|
||||
if let completion = completion {
|
||||
completion(true)
|
||||
}
|
||||
@ -647,6 +649,7 @@ public extension ContainedViewLayoutTransition {
|
||||
func updateSublayerTransformScale(node: ASDisplayNode, scale: CGPoint, completion: ((Bool) -> Void)? = nil) {
|
||||
if !node.isNodeLoaded {
|
||||
node.subnodeTransform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
|
||||
completion?(true)
|
||||
return
|
||||
}
|
||||
let t = node.layer.sublayerTransform
|
||||
@ -713,6 +716,7 @@ public extension ContainedViewLayoutTransition {
|
||||
func updateTransformScale(node: ASDisplayNode, scale: CGPoint, completion: ((Bool) -> Void)? = nil) {
|
||||
if !node.isNodeLoaded {
|
||||
node.subnodeTransform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
|
||||
completion?(true)
|
||||
return
|
||||
}
|
||||
let t = node.layer.transform
|
||||
|
@ -759,7 +759,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private func lowestSectionNode() -> ASDisplayNode? {
|
||||
public func lowestSectionNode() -> ASDisplayNode? {
|
||||
var lowestHeaderNode: ASDisplayNode?
|
||||
var lowestHeaderNodeIndex: Int?
|
||||
for (_, headerNode) in self.sectionNodes {
|
||||
|
377
submodules/Display/Display/Navigation/NavigationContainer.swift
Normal file
377
submodules/Display/Display/Navigation/NavigationContainer.swift
Normal file
@ -0,0 +1,377 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
|
||||
final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
private final class Child {
|
||||
let value: ViewController
|
||||
var layout: ContainerViewLayout
|
||||
|
||||
init(value: ViewController, layout: ContainerViewLayout) {
|
||||
self.value = value
|
||||
self.layout = layout
|
||||
}
|
||||
}
|
||||
|
||||
private final class PendingChild {
|
||||
enum TransitionType {
|
||||
case push
|
||||
case pop
|
||||
}
|
||||
|
||||
let value: Child
|
||||
let transitionType: TransitionType
|
||||
let transition: ContainedViewLayoutTransition
|
||||
let disposable: MetaDisposable = MetaDisposable()
|
||||
var isReady: Bool = false
|
||||
|
||||
init(value: Child, transitionType: TransitionType, transition: ContainedViewLayoutTransition, update: @escaping (PendingChild) -> Void) {
|
||||
self.value = value
|
||||
self.transitionType = transitionType
|
||||
self.transition = transition
|
||||
var localIsReady: Bool?
|
||||
self.disposable.set((value.value.ready.get()
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
if localIsReady == nil {
|
||||
localIsReady = true
|
||||
} else if let strongSelf = self {
|
||||
update(strongSelf)
|
||||
}
|
||||
}))
|
||||
if let localIsReady = localIsReady {
|
||||
self.isReady = true
|
||||
} else {
|
||||
localIsReady = false
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.disposable.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
private final class TopTransition {
|
||||
let previous: Child
|
||||
let coordinator: NavigationTransitionCoordinator
|
||||
|
||||
init(previous: Child, coordinator: NavigationTransitionCoordinator) {
|
||||
self.previous = previous
|
||||
self.coordinator = coordinator
|
||||
}
|
||||
}
|
||||
|
||||
private struct State {
|
||||
var layout: ContainerViewLayout?
|
||||
var canBeClosed: Bool?
|
||||
var top: Child?
|
||||
var transition: TopTransition?
|
||||
var pending: PendingChild?
|
||||
}
|
||||
|
||||
private(set) var controllers: [ViewController] = []
|
||||
private var state: State = State(layout: nil, canBeClosed: nil, top: nil, transition: nil, pending: nil)
|
||||
|
||||
private(set) var isReady: Bool = false
|
||||
var isReadyUpdated: (() -> Void)?
|
||||
var controllerRemoved: (ViewController) -> Void
|
||||
var keyboardManager: KeyboardManager? {
|
||||
didSet {
|
||||
if self.keyboardManager !== oldValue {
|
||||
self.keyboardManager?.surfaces = self.state.top?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(controllerRemoved: @escaping (ViewController) -> Void) {
|
||||
self.controllerRemoved = controllerRemoved
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
||||
panRecognizer.delegate = self
|
||||
panRecognizer.delaysTouchesBegan = false
|
||||
panRecognizer.cancelsTouchesInView = true
|
||||
self.view.addGestureRecognizer(panRecognizer)
|
||||
}
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if let _ = otherGestureRecognizer as? UIPanGestureRecognizer {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
guard let layout = self.state.layout else {
|
||||
return
|
||||
}
|
||||
guard self.state.transition == nil else {
|
||||
return
|
||||
}
|
||||
let beginGesture = self.controllers.count > 1
|
||||
|
||||
if beginGesture {
|
||||
let topController = self.controllers[self.controllers.count - 1]
|
||||
let bottomController = self.controllers[self.controllers.count - 2]
|
||||
|
||||
if let topController = topController as? ViewController {
|
||||
if !topController.attemptNavigation({ [weak self] in
|
||||
//let _ = self?.popViewController(animated: true)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
topController.viewWillDisappear(true)
|
||||
let topView = topController.view!
|
||||
bottomController.containerLayoutUpdated(layout, transition: .immediate)
|
||||
bottomController.viewWillAppear(true)
|
||||
let bottomView = bottomController.view!
|
||||
|
||||
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.view, topView: topView, topNavigationBar: topController.navigationBar, bottomView: bottomView, bottomNavigationBar: bottomController.navigationBar, didUpdateProgress: { [weak self] progress, transition in
|
||||
if let strongSelf = self {
|
||||
strongSelf.keyboardManager?.surfaces = strongSelf.state.top?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? []
|
||||
/*for i in 0 ..< strongSelf._viewControllers.count {
|
||||
if let controller = strongSelf._viewControllers[i].controller as? ViewController {
|
||||
if i < strongSelf._viewControllers.count - 1 {
|
||||
controller.updateNavigationCustomData((strongSelf.viewControllers[i + 1] as? ViewController)?.customData, progress: 1.0 - progress, transition: transition)
|
||||
} else {
|
||||
controller.updateNavigationCustomData(nil, progress: 1.0 - progress, transition: transition)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
})
|
||||
bottomController.displayNode.recursivelyEnsureDisplaySynchronously(true)
|
||||
self.state.transition = TopTransition(previous: Child(value: bottomController, layout: layout), coordinator: navigationTransitionCoordinator)
|
||||
}
|
||||
case .changed:
|
||||
if let navigationTransitionCoordinator = self.state.transition?.coordinator, !navigationTransitionCoordinator.animatingCompletion {
|
||||
let translation = recognizer.translation(in: self.view).x
|
||||
let progress = max(0.0, min(1.0, translation / self.view.frame.width))
|
||||
navigationTransitionCoordinator.progress = progress
|
||||
}
|
||||
case .ended, .cancelled:
|
||||
if let navigationTransitionCoordinator = self.state.transition?.coordinator, !navigationTransitionCoordinator.animatingCompletion {
|
||||
let velocity = recognizer.velocity(in: self.view).x
|
||||
|
||||
if velocity > 1000 || navigationTransitionCoordinator.progress > 0.2 {
|
||||
//(self.view as! NavigationControllerView).inTransition = true
|
||||
navigationTransitionCoordinator.animateCompletion(velocity, completion: { [weak self] in
|
||||
guard let strongSelf = self, let layout = strongSelf.state.layout, let transition = strongSelf.state.transition, let top = strongSelf.state.top else {
|
||||
return
|
||||
}
|
||||
//(self.view as! NavigationControllerView).inTransition = false
|
||||
|
||||
let topController = top.value
|
||||
let bottomController = transition.previous.value
|
||||
strongSelf.keyboardManager?.updateInteractiveInputOffset(layout.size.height, transition: .immediate, completion: {})
|
||||
topController.view.endEditing(true)
|
||||
|
||||
strongSelf.state.transition = nil
|
||||
|
||||
strongSelf.controllerRemoved(top.value)
|
||||
|
||||
//topController.viewDidDisappear(true)
|
||||
//bottomController.viewDidAppear(true)
|
||||
})
|
||||
} else {
|
||||
/*if self.viewControllers.count >= 2 {
|
||||
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
|
||||
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
|
||||
|
||||
topController.viewWillAppear(true)
|
||||
bottomController.viewWillDisappear(true)
|
||||
}*/
|
||||
|
||||
//(self.view as! NavigationControllerView).inTransition = true
|
||||
navigationTransitionCoordinator.animateCancel({ [weak self] in
|
||||
guard let strongSelf = self, let top = strongSelf.state.top, let transition = strongSelf.state.transition else {
|
||||
return
|
||||
}
|
||||
//(self.view as! NavigationControllerView).inTransition = false
|
||||
strongSelf.state.transition = nil
|
||||
|
||||
top.value.viewDidAppear(true)
|
||||
transition.previous.value.viewDidDisappear(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func update(layout: ContainerViewLayout, canBeClosed: Bool, controllers: [ViewController], transition: ContainedViewLayoutTransition) {
|
||||
let previousControllers = self.controllers
|
||||
self.controllers = controllers
|
||||
|
||||
for i in 0 ..< controllers.count {
|
||||
if i == 0 {
|
||||
if canBeClosed {
|
||||
controllers[i].navigationBar?.previousItem = .close
|
||||
} else {
|
||||
controllers[i].navigationBar?.previousItem = nil
|
||||
}
|
||||
} else {
|
||||
controllers[i].navigationBar?.previousItem = .item(controllers[i - 1].navigationItem)
|
||||
}
|
||||
}
|
||||
|
||||
self.state.layout = layout
|
||||
self.state.canBeClosed = canBeClosed
|
||||
|
||||
if controllers.last !== self.state.top?.value {
|
||||
if controllers.last !== self.state.pending?.value.value {
|
||||
self.state.pending = nil
|
||||
if let last = controllers.last {
|
||||
let transitionType: PendingChild.TransitionType
|
||||
if !previousControllers.contains(where: { $0 === last }) {
|
||||
transitionType = .push
|
||||
} else {
|
||||
transitionType = .pop
|
||||
}
|
||||
self.state.pending = PendingChild(value: self.makeChild(layout: layout, value: last), transitionType: transitionType, transition: transition, update: { [weak self] pendingChild in
|
||||
self?.pendingChildIsReady(pendingChild)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let pending = self.state.pending {
|
||||
if pending.isReady {
|
||||
self.state.pending = nil
|
||||
let previous = self.state.top
|
||||
self.state.top = pending.value
|
||||
self.topTransition(from: previous, to: pending.value, transitionType: pending.transitionType, layout: layout, transition: pending.transition)
|
||||
if !self.isReady {
|
||||
self.isReady = true
|
||||
self.isReadyUpdated?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if controllers.isEmpty && self.state.top != nil {
|
||||
let previous = self.state.top
|
||||
self.state.top = nil
|
||||
self.topTransition(from: previous, to: nil, transitionType: .pop, layout: layout, transition: .immediate)
|
||||
}
|
||||
|
||||
if let top = self.state.top {
|
||||
self.applyLayout(layout: layout, to: top, transition: transition)
|
||||
}
|
||||
|
||||
if self.state.transition == nil {
|
||||
self.keyboardManager?.surfaces = self.state.top?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? []
|
||||
}
|
||||
}
|
||||
|
||||
private func topTransition(from fromValue: Child?, to toValue: Child?, transitionType: PendingChild.TransitionType, layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
if case .animated = transition, let fromValue = fromValue, let toValue = toValue {
|
||||
self.keyboardManager?.surfaces = fromValue.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? []
|
||||
if let currentTransition = self.state.transition {
|
||||
assertionFailure()
|
||||
}
|
||||
|
||||
fromValue.value.viewWillDisappear(true)
|
||||
toValue.value.viewWillAppear(true)
|
||||
toValue.value.setIgnoreAppearanceMethodInvocations(true)
|
||||
if let layout = self.state.layout {
|
||||
toValue.value.displayNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
}
|
||||
let mappedTransitionType: NavigationTransition
|
||||
let topController: ViewController
|
||||
let bottomController: ViewController
|
||||
switch transitionType {
|
||||
case .push:
|
||||
mappedTransitionType = .Push
|
||||
self.addSubnode(toValue.value.displayNode)
|
||||
topController = toValue.value
|
||||
bottomController = fromValue.value
|
||||
case .pop:
|
||||
mappedTransitionType = .Pop
|
||||
self.insertSubnode(toValue.value.displayNode, belowSubnode: fromValue.value.displayNode)
|
||||
topController = fromValue.value
|
||||
bottomController = toValue.value
|
||||
}
|
||||
toValue.value.setIgnoreAppearanceMethodInvocations(false)
|
||||
|
||||
let topTransition = TopTransition(previous: fromValue, coordinator: NavigationTransitionCoordinator(transition: mappedTransitionType, container: self.view, topView: topController.view, topNavigationBar: topController.navigationBar, bottomView: bottomController.view, bottomNavigationBar: bottomController.navigationBar, didUpdateProgress: nil))
|
||||
self.state.transition = topTransition
|
||||
|
||||
topTransition.coordinator.animateCompletion(0.0, completion: { [weak self, weak topTransition] in
|
||||
guard let strongSelf = self, let topTransition = topTransition, strongSelf.state.transition === topTransition else {
|
||||
return
|
||||
}
|
||||
strongSelf.state.transition = nil
|
||||
|
||||
topTransition.previous.value.setIgnoreAppearanceMethodInvocations(true)
|
||||
topTransition.previous.value.displayNode.removeFromSupernode()
|
||||
topTransition.previous.value.setIgnoreAppearanceMethodInvocations(false)
|
||||
topTransition.previous.value.viewDidDisappear(true)
|
||||
if let toValue = strongSelf.state.top, let layout = strongSelf.state.layout {
|
||||
toValue.value.displayNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
strongSelf.applyLayout(layout: layout, to: toValue, transition: .immediate)
|
||||
toValue.value.viewDidAppear(true)
|
||||
strongSelf.keyboardManager?.surfaces = toValue.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? []
|
||||
}
|
||||
})
|
||||
} else {
|
||||
self.keyboardManager?.surfaces = toValue?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? []
|
||||
if let fromValue = fromValue {
|
||||
fromValue.value.viewWillDisappear(false)
|
||||
fromValue.value.setIgnoreAppearanceMethodInvocations(true)
|
||||
fromValue.value.displayNode.removeFromSupernode()
|
||||
fromValue.value.setIgnoreAppearanceMethodInvocations(false)
|
||||
fromValue.value.viewDidDisappear(false)
|
||||
}
|
||||
if let toValue = toValue {
|
||||
self.applyLayout(layout: layout, to: toValue, transition: .immediate)
|
||||
toValue.value.displayNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
toValue.value.viewWillAppear(false)
|
||||
toValue.value.setIgnoreAppearanceMethodInvocations(true)
|
||||
self.addSubnode(toValue.value.displayNode)
|
||||
toValue.value.setIgnoreAppearanceMethodInvocations(false)
|
||||
toValue.value.viewDidAppear(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func makeChild(layout: ContainerViewLayout, value: ViewController) -> Child {
|
||||
value.containerLayoutUpdated(layout, transition: .immediate)
|
||||
return Child(value: value, layout: layout)
|
||||
}
|
||||
|
||||
private func applyLayout(layout: ContainerViewLayout, to child: Child, transition: ContainedViewLayoutTransition) {
|
||||
if child.layout != layout {
|
||||
child.layout = layout
|
||||
child.value.containerLayoutUpdated(layout, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private func pendingChildIsReady(_ child: PendingChild) {
|
||||
if let pending = self.state.pending, pending === child {
|
||||
pending.isReady = true
|
||||
self.performUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
private func performUpdate() {
|
||||
if let layout = self.state.layout, let canBeClosed = self.state.canBeClosed {
|
||||
self.update(layout: layout, canBeClosed: canBeClosed, controllers: self.controllers, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
793
submodules/Display/Display/Navigation/NavigationController.swift
Normal file
793
submodules/Display/Display/Navigation/NavigationController.swift
Normal file
@ -0,0 +1,793 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
|
||||
public enum NavigationStatusBarStyle {
|
||||
case black
|
||||
case white
|
||||
}
|
||||
|
||||
public final class NavigationControllerTheme {
|
||||
public let statusBar: NavigationStatusBarStyle
|
||||
public let navigationBar: NavigationBarTheme
|
||||
public let emptyAreaColor: UIColor
|
||||
|
||||
public init(statusBar: NavigationStatusBarStyle, navigationBar: NavigationBarTheme, emptyAreaColor: UIColor) {
|
||||
self.statusBar = statusBar
|
||||
self.navigationBar = navigationBar
|
||||
self.emptyAreaColor = emptyAreaColor
|
||||
}
|
||||
}
|
||||
|
||||
public struct NavigationAnimationOptions : OptionSet {
|
||||
public let rawValue: Int
|
||||
|
||||
public init(rawValue: Int) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
public init() {
|
||||
self.rawValue = 0
|
||||
}
|
||||
|
||||
public static let removeOnMasterDetails = NavigationAnimationOptions(rawValue: 1 << 0)
|
||||
}
|
||||
|
||||
private final class NavigationControllerContainerView: UIView {
|
||||
override class var layerClass: AnyClass {
|
||||
return CATracingLayer.self
|
||||
}
|
||||
}
|
||||
|
||||
public enum NavigationEmptyDetailsBackgoundMode {
|
||||
case image(UIImage)
|
||||
case wallpaper(UIImage)
|
||||
}
|
||||
|
||||
private final class NavigationControllerView: UITracingLayerView {
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override class var layerClass: AnyClass {
|
||||
return CATracingLayer.self
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
return super.hitTest(point, with: event)
|
||||
}
|
||||
}
|
||||
|
||||
private enum ControllerTransition {
|
||||
case none
|
||||
case appearance
|
||||
}
|
||||
|
||||
private final class ControllerRecord {
|
||||
let controller: UIViewController
|
||||
var transition: ControllerTransition = .none
|
||||
|
||||
init(controller: UIViewController) {
|
||||
self.controller = controller
|
||||
}
|
||||
}
|
||||
|
||||
public enum NavigationControllerMode {
|
||||
case single
|
||||
case automaticMasterDetail
|
||||
}
|
||||
|
||||
public enum MasterDetailLayoutBlackout : Equatable {
|
||||
case master
|
||||
case details
|
||||
}
|
||||
|
||||
private enum RootContainer {
|
||||
case flat(NavigationContainer)
|
||||
case split(NavigationSplitContainer)
|
||||
}
|
||||
|
||||
open class NavigationController: UINavigationController, ContainableController, UIGestureRecognizerDelegate {
|
||||
public var isOpaqueWhenInOverlay: Bool = true
|
||||
public var blocksBackgroundWhenInOverlay: Bool = true
|
||||
public var isModalWhenInOverlay: Bool = false
|
||||
public var updateTransitionWhenPresentedAsModal: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
private let _ready = Promise<Bool>(true)
|
||||
open var ready: Promise<Bool> {
|
||||
return self._ready
|
||||
}
|
||||
|
||||
private var masterDetailsBlackout: MasterDetailLayoutBlackout?
|
||||
private var backgroundDetailsMode: NavigationEmptyDetailsBackgoundMode?
|
||||
|
||||
public var lockOrientation: Bool = false
|
||||
|
||||
public var deferScreenEdgeGestures: UIRectEdge = UIRectEdge()
|
||||
|
||||
private let mode: NavigationControllerMode
|
||||
private var theme: NavigationControllerTheme
|
||||
|
||||
public private(set) weak var overlayPresentingController: ViewController?
|
||||
|
||||
private var controllerView: NavigationControllerView {
|
||||
return self.view as! NavigationControllerView
|
||||
}
|
||||
|
||||
private var rootContainer: RootContainer?
|
||||
private var rootModalFrame: NavigationModalFrame?
|
||||
private var modalContainers: [NavigationModalContainer] = []
|
||||
private var validLayout: ContainerViewLayout?
|
||||
private var validStatusBarStyle: NavigationStatusBarStyle?
|
||||
|
||||
private var scheduledLayoutTransitionRequestId: Int = 0
|
||||
private var scheduledLayoutTransitionRequest: (Int, ContainedViewLayoutTransition)?
|
||||
|
||||
private var _presentedViewController: UIViewController?
|
||||
open override var presentedViewController: UIViewController? {
|
||||
return self._presentedViewController
|
||||
}
|
||||
|
||||
private var _viewControllers: [ViewController] = []
|
||||
override open var viewControllers: [UIViewController] {
|
||||
get {
|
||||
return self._viewControllers.map { $0 as! ViewController }
|
||||
} set(value) {
|
||||
self.setViewControllers(value, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
override open var topViewController: UIViewController? {
|
||||
return self._viewControllers.last
|
||||
}
|
||||
|
||||
private var _displayNode: ASDisplayNode?
|
||||
public var displayNode: ASDisplayNode {
|
||||
return self._displayNode!
|
||||
}
|
||||
|
||||
var statusBarHost: StatusBarHost?
|
||||
var keyboardManager: KeyboardManager?
|
||||
|
||||
public func updateMasterDetailsBlackout(_ blackout: MasterDetailLayoutBlackout?, transition: ContainedViewLayoutTransition) {
|
||||
self.masterDetailsBlackout = blackout
|
||||
if isViewLoaded {
|
||||
self.view.endEditing(true)
|
||||
}
|
||||
self.requestLayout(transition: transition)
|
||||
}
|
||||
|
||||
public func updateBackgroundDetailsMode(_ mode: NavigationEmptyDetailsBackgoundMode?, transition: ContainedViewLayoutTransition) {
|
||||
self.backgroundDetailsMode = mode
|
||||
self.requestLayout(transition: transition)
|
||||
}
|
||||
|
||||
public init(mode: NavigationControllerMode, theme: NavigationControllerTheme, backgroundDetailsMode: NavigationEmptyDetailsBackgoundMode? = nil) {
|
||||
self.mode = mode
|
||||
self.theme = theme
|
||||
self.backgroundDetailsMode = backgroundDetailsMode
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
public required init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
public func combinedSupportedOrientations(currentOrientationToLock: UIInterfaceOrientationMask) -> ViewControllerSupportedOrientations {
|
||||
var supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .allButUpsideDown)
|
||||
if let controller = self.viewControllers.last {
|
||||
if let controller = controller as? ViewController {
|
||||
if controller.lockOrientation {
|
||||
if let lockedOrientation = controller.lockedOrientation {
|
||||
supportedOrientations = supportedOrientations.intersection(ViewControllerSupportedOrientations(regularSize: lockedOrientation, compactSize: lockedOrientation))
|
||||
} else {
|
||||
supportedOrientations = supportedOrientations.intersection(ViewControllerSupportedOrientations(regularSize: currentOrientationToLock, compactSize: currentOrientationToLock))
|
||||
}
|
||||
} else {
|
||||
supportedOrientations = supportedOrientations.intersection(controller.supportedOrientations)
|
||||
}
|
||||
}
|
||||
}
|
||||
return supportedOrientations
|
||||
}
|
||||
|
||||
public func updateTheme(_ theme: NavigationControllerTheme) {
|
||||
let statusBarStyleUpdated = self.theme.statusBar != theme.statusBar
|
||||
self.theme = theme
|
||||
if self.isViewLoaded {
|
||||
if statusBarStyleUpdated {
|
||||
self.validStatusBarStyle = self.theme.statusBar
|
||||
let normalStatusBarStyle: UIStatusBarStyle
|
||||
switch self.theme.statusBar {
|
||||
case .black:
|
||||
normalStatusBarStyle = .default
|
||||
case .white:
|
||||
normalStatusBarStyle = .lightContent
|
||||
}
|
||||
self.statusBarHost?.setStatusBarStyle(normalStatusBarStyle, animated: false)
|
||||
}
|
||||
self.controllerView.backgroundColor = theme.emptyAreaColor
|
||||
}
|
||||
}
|
||||
|
||||
open func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize? {
|
||||
return nil
|
||||
}
|
||||
|
||||
public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
if !self.isViewLoaded {
|
||||
self.loadView()
|
||||
}
|
||||
self.validLayout = layout
|
||||
transition.updateFrame(view: self.view, frame: CGRect(origin: self.view.frame.origin, size: layout.size))
|
||||
self.updateContainers(layout: layout, transition: transition)
|
||||
}
|
||||
|
||||
private func updateContainers(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
let navigationLayout = makeNavigationLayout(layout: layout, controllers: self._viewControllers)
|
||||
|
||||
var transition = transition
|
||||
|
||||
var modalContainers: [NavigationModalContainer] = []
|
||||
for i in 0 ..< navigationLayout.modal.count {
|
||||
var existingModalContainer: NavigationModalContainer?
|
||||
loop: for currentModalContainer in self.modalContainers {
|
||||
for controller in navigationLayout.modal[i].controllers {
|
||||
if currentModalContainer.container.controllers.contains(where: { $0 === controller }) {
|
||||
existingModalContainer = currentModalContainer
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let modalContainer: NavigationModalContainer
|
||||
let containerTransition: ContainedViewLayoutTransition
|
||||
if let existingModalContainer = existingModalContainer {
|
||||
modalContainer = existingModalContainer
|
||||
containerTransition = transition
|
||||
} else {
|
||||
modalContainer = NavigationModalContainer(theme: self.theme, controllerRemoved: { [weak self] controller in
|
||||
self?.controllerRemoved(controller)
|
||||
})
|
||||
self.modalContainers.append(modalContainer)
|
||||
if !modalContainer.isReady {
|
||||
modalContainer.isReadyUpdated = { [weak self, weak modalContainer] in
|
||||
guard let strongSelf = self, let modalContainer = modalContainer else {
|
||||
return
|
||||
}
|
||||
if let layout = strongSelf.validLayout {
|
||||
strongSelf.updateContainers(layout: layout, transition: .animated(duration: 0.5, curve: .spring))
|
||||
}
|
||||
}
|
||||
}
|
||||
modalContainer.updateDismissProgress = { [weak self, weak modalContainer] _ in
|
||||
guard let strongSelf = self, let modalContainer = modalContainer else {
|
||||
return
|
||||
}
|
||||
if let layout = strongSelf.validLayout {
|
||||
strongSelf.updateContainers(layout: layout, transition: .immediate)
|
||||
}
|
||||
}
|
||||
modalContainer.interactivelyDismissed = { [weak self, weak modalContainer] in
|
||||
guard let strongSelf = self, let modalContainer = modalContainer else {
|
||||
return
|
||||
}
|
||||
let controllers = strongSelf._viewControllers.filter { controller in
|
||||
return !modalContainer.container.controllers.contains(where: { $0 === controller })
|
||||
}
|
||||
strongSelf.setViewControllers(controllers, animated: false)
|
||||
}
|
||||
containerTransition = .immediate
|
||||
}
|
||||
|
||||
containerTransition.updateFrame(node: modalContainer, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
modalContainer.update(layout: layout, controllers: navigationLayout.modal[i].controllers, transition: containerTransition)
|
||||
modalContainers.append(modalContainer)
|
||||
}
|
||||
|
||||
for container in self.modalContainers {
|
||||
if !modalContainers.contains(where: { $0 === container }) {
|
||||
transition = container.dismiss(transition: transition, completion: { [weak container] in
|
||||
container?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
self.modalContainers = modalContainers
|
||||
|
||||
var previousModalContainer: NavigationModalContainer?
|
||||
var visibleModalCount = 0
|
||||
var topModalDismissProgress: CGFloat = 0.0
|
||||
|
||||
for i in (0 ..< navigationLayout.modal.count).reversed() {
|
||||
let modalContainer = self.modalContainers[i]
|
||||
if modalContainer.supernode == nil && modalContainer.isReady {
|
||||
if let previousModalContainer = previousModalContainer {
|
||||
self.displayNode.insertSubnode(modalContainer, belowSubnode: previousModalContainer)
|
||||
} else {
|
||||
self.displayNode.addSubnode(modalContainer)
|
||||
}
|
||||
modalContainer.animateIn(transition: transition)
|
||||
}
|
||||
if modalContainer.supernode != nil {
|
||||
visibleModalCount += 1
|
||||
if previousModalContainer == nil {
|
||||
topModalDismissProgress = modalContainer.dismissProgress
|
||||
modalContainer.container.keyboardManager = self.keyboardManager
|
||||
} else {
|
||||
modalContainer.container.keyboardManager = nil
|
||||
}
|
||||
previousModalContainer = modalContainer
|
||||
}
|
||||
}
|
||||
|
||||
switch navigationLayout.root {
|
||||
case let .flat(controllers):
|
||||
if let rootContainer = self.rootContainer {
|
||||
switch rootContainer {
|
||||
case let .flat(flatContainer):
|
||||
if previousModalContainer == nil {
|
||||
flatContainer.keyboardManager = self.keyboardManager
|
||||
} else {
|
||||
flatContainer.keyboardManager = nil
|
||||
}
|
||||
transition.updateFrame(node: flatContainer, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: transition)
|
||||
case let .split(splitContainer):
|
||||
let flatContainer = NavigationContainer(controllerRemoved: { [weak self] controller in
|
||||
self?.controllerRemoved(controller)
|
||||
})
|
||||
self.displayNode.insertSubnode(flatContainer, at: 0)
|
||||
self.rootContainer = .flat(flatContainer)
|
||||
flatContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: .immediate)
|
||||
splitContainer.removeFromSupernode()
|
||||
}
|
||||
} else {
|
||||
let flatContainer = NavigationContainer(controllerRemoved: { [weak self] controller in
|
||||
self?.controllerRemoved(controller)
|
||||
})
|
||||
self.displayNode.insertSubnode(flatContainer, at: 0)
|
||||
self.rootContainer = .flat(flatContainer)
|
||||
flatContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: .immediate)
|
||||
}
|
||||
case let .split(masterControllers, detailControllers):
|
||||
if let rootContainer = self.rootContainer {
|
||||
switch rootContainer {
|
||||
case let .flat(flatContainer):
|
||||
let splitContainer = NavigationSplitContainer(theme: self.theme, controllerRemoved: { [weak self] controller in
|
||||
self?.controllerRemoved(controller)
|
||||
})
|
||||
self.displayNode.insertSubnode(splitContainer, at: 0)
|
||||
self.rootContainer = .split(splitContainer)
|
||||
splitContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, transition: .immediate)
|
||||
flatContainer.removeFromSupernode()
|
||||
case let .split(splitContainer):
|
||||
transition.updateFrame(node: splitContainer, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, transition: transition)
|
||||
}
|
||||
} else {
|
||||
let splitContainer = NavigationSplitContainer(theme: self.theme, controllerRemoved: { [weak self] controller in
|
||||
self?.controllerRemoved(controller)
|
||||
})
|
||||
self.displayNode.insertSubnode(splitContainer, at: 0)
|
||||
self.rootContainer = .split(splitContainer)
|
||||
splitContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
switch layout.metrics.widthClass {
|
||||
case .compact:
|
||||
if visibleModalCount != 0 {
|
||||
let rootModalFrame: NavigationModalFrame
|
||||
var modalFrameTransition: ContainedViewLayoutTransition = transition
|
||||
var forceStatusBarAnimation = false
|
||||
if let current = self.rootModalFrame {
|
||||
rootModalFrame = current
|
||||
transition.updateFrame(node: rootModalFrame, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
rootModalFrame.update(layout: layout, transition: modalFrameTransition)
|
||||
rootModalFrame.updateDismissal(transition: transition, progress: topModalDismissProgress, completion: {})
|
||||
forceStatusBarAnimation = true
|
||||
} else {
|
||||
rootModalFrame = NavigationModalFrame(theme: self.theme)
|
||||
self.rootModalFrame = rootModalFrame
|
||||
rootModalFrame.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
rootModalFrame.update(layout: layout, transition: .immediate)
|
||||
rootModalFrame.animateIn(transition: transition)
|
||||
if let rootContainer = self.rootContainer {
|
||||
var rootContainerNode: ASDisplayNode
|
||||
switch rootContainer {
|
||||
case let .flat(container):
|
||||
rootContainerNode = container
|
||||
case let .split(container):
|
||||
rootContainerNode = container
|
||||
}
|
||||
self.displayNode.insertSubnode(rootModalFrame, aboveSubnode: rootContainerNode)
|
||||
}
|
||||
rootModalFrame.updateDismissal(transition: transition, progress: topModalDismissProgress, completion: {})
|
||||
}
|
||||
if topModalDismissProgress < 0.5 {
|
||||
self.statusBarHost?.setStatusBarStyle(.lightContent, animated: transition.isAnimated || forceStatusBarAnimation)
|
||||
} else {
|
||||
let normalStatusBarStyle: UIStatusBarStyle
|
||||
switch self.theme.statusBar {
|
||||
case .black:
|
||||
normalStatusBarStyle = .default
|
||||
case .white:
|
||||
normalStatusBarStyle = .lightContent
|
||||
}
|
||||
self.statusBarHost?.setStatusBarStyle(normalStatusBarStyle, animated: transition.isAnimated || forceStatusBarAnimation)
|
||||
}
|
||||
if let rootContainer = self.rootContainer {
|
||||
var rootContainerNode: ASDisplayNode
|
||||
switch rootContainer {
|
||||
case let .flat(container):
|
||||
rootContainerNode = container
|
||||
case let .split(container):
|
||||
rootContainerNode = container
|
||||
}
|
||||
let maxScale = (layout.size.width - 16.0 * 2.0) / layout.size.width
|
||||
var topInset: CGFloat = 0.0
|
||||
if let statusBarHeight = layout.statusBarHeight {
|
||||
topInset += statusBarHeight
|
||||
}
|
||||
let maxOffset: CGFloat = (topInset - (layout.size.height - layout.size.height * maxScale) / 2.0)
|
||||
|
||||
let scale = 1.0 * topModalDismissProgress + (1.0 - topModalDismissProgress) * maxScale
|
||||
let offset = (1.0 - topModalDismissProgress) * maxOffset
|
||||
transition.updateSublayerTransformScaleAndOffset(node: rootContainerNode, scale: scale, offset: CGPoint(x: 0.0, y: offset))
|
||||
}
|
||||
} else {
|
||||
if let rootModalFrame = self.rootModalFrame {
|
||||
self.rootModalFrame = nil
|
||||
rootModalFrame.updateDismissal(transition: transition, progress: 1.0, completion: { [weak rootModalFrame] in
|
||||
rootModalFrame?.removeFromSupernode()
|
||||
})
|
||||
let normalStatusBarStyle: UIStatusBarStyle
|
||||
switch self.theme.statusBar {
|
||||
case .black:
|
||||
normalStatusBarStyle = .default
|
||||
case .white:
|
||||
normalStatusBarStyle = .lightContent
|
||||
}
|
||||
self.statusBarHost?.setStatusBarStyle(normalStatusBarStyle, animated: transition.isAnimated)
|
||||
}
|
||||
if let rootContainer = self.rootContainer {
|
||||
var rootContainerNode: ASDisplayNode
|
||||
switch rootContainer {
|
||||
case let .flat(container):
|
||||
rootContainerNode = container
|
||||
case let .split(container):
|
||||
rootContainerNode = container
|
||||
}
|
||||
transition.updateSublayerTransformScaleAndOffset(node: rootContainerNode, scale: 1.0, offset: CGPoint())
|
||||
}
|
||||
}
|
||||
case .regular:
|
||||
if let rootModalFrame = self.rootModalFrame {
|
||||
self.rootModalFrame = nil
|
||||
rootModalFrame.updateDismissal(transition: .immediate, progress: 1.0, completion: { [weak rootModalFrame] in
|
||||
rootModalFrame?.removeFromSupernode()
|
||||
})
|
||||
let normalStatusBarStyle: UIStatusBarStyle
|
||||
switch self.theme.statusBar {
|
||||
case .black:
|
||||
normalStatusBarStyle = .default
|
||||
case .white:
|
||||
normalStatusBarStyle = .lightContent
|
||||
}
|
||||
self.statusBarHost?.setStatusBarStyle(normalStatusBarStyle, animated: false)
|
||||
}
|
||||
if let rootContainer = self.rootContainer {
|
||||
var rootContainerNode: ASDisplayNode
|
||||
switch rootContainer {
|
||||
case let .flat(container):
|
||||
rootContainerNode = container
|
||||
case let .split(container):
|
||||
rootContainerNode = container
|
||||
}
|
||||
ContainedViewLayoutTransition.immediate.updateSublayerTransformScaleAndOffset(node: rootContainerNode, scale: 1.0, offset: CGPoint())
|
||||
}
|
||||
}
|
||||
|
||||
if self.validStatusBarStyle != self.theme.statusBar {
|
||||
self.validStatusBarStyle = self.theme.statusBar
|
||||
let normalStatusBarStyle: UIStatusBarStyle
|
||||
switch self.theme.statusBar {
|
||||
case .black:
|
||||
normalStatusBarStyle = .default
|
||||
case .white:
|
||||
normalStatusBarStyle = .lightContent
|
||||
}
|
||||
self.statusBarHost?.setStatusBarStyle(normalStatusBarStyle, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
private func controllerRemoved(_ controller: ViewController) {
|
||||
self.filterController(controller, animated: false)
|
||||
}
|
||||
|
||||
public func updateModalTransition(_ value: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
}
|
||||
|
||||
public func updateToInterfaceOrientation(_ orientation: UIInterfaceOrientation) {
|
||||
/*for record in self._viewControllers {
|
||||
if let controller = record.controller as? ContainableController {
|
||||
controller.updateToInterfaceOrientation(orientation)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
open override func loadView() {
|
||||
self._displayNode = ASDisplayNode(viewBlock: {
|
||||
return NavigationControllerView()
|
||||
}, didLoad: nil)
|
||||
|
||||
self.view = self.displayNode.view
|
||||
self.view.clipsToBounds = true
|
||||
self.view.autoresizingMask = []
|
||||
|
||||
self.controllerView.backgroundColor = self.theme.emptyAreaColor
|
||||
|
||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
||||
self.navigationBar.prefersLargeTitles = false
|
||||
}
|
||||
self.navigationBar.removeFromSuperview()
|
||||
|
||||
/*let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
||||
panRecognizer.delegate = self
|
||||
panRecognizer.delaysTouchesBegan = false
|
||||
panRecognizer.cancelsTouchesInView = true
|
||||
self.view.addGestureRecognizer(panRecognizer)*/
|
||||
}
|
||||
|
||||
public func pushViewController(_ controller: ViewController) {
|
||||
self.pushViewController(controller, completion: {})
|
||||
}
|
||||
|
||||
public func pushViewController(_ controller: ViewController, animated: Bool = true, completion: @escaping () -> Void) {
|
||||
let navigateAction: () -> Void = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if !controller.hasActiveInput {
|
||||
//strongSelf.view.endEditing(true)
|
||||
}
|
||||
/*strongSelf.scheduleAfterLayout({
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}*/
|
||||
strongSelf.pushViewController(controller, animated: animated)
|
||||
completion()
|
||||
//})
|
||||
}
|
||||
|
||||
/*if let lastController = self.viewControllers.last as? ViewController, !lastController.attemptNavigation(navigateAction) {
|
||||
} else {*/
|
||||
navigateAction()
|
||||
//}
|
||||
}
|
||||
|
||||
open override func pushViewController(_ viewController: UIViewController, animated: Bool) {
|
||||
var controllers = self.viewControllers
|
||||
controllers.append(viewController)
|
||||
self.setViewControllers(controllers, animated: animated)
|
||||
}
|
||||
|
||||
public func replaceTopController(_ controller: ViewController, animated: Bool, ready: ValuePromise<Bool>? = nil) {
|
||||
ready?.set(true)
|
||||
var controllers = self.viewControllers
|
||||
controllers.removeLast()
|
||||
controllers.append(controller)
|
||||
self.setViewControllers(controllers, animated: animated)
|
||||
}
|
||||
|
||||
public func filterController(_ controller: ViewController, animated: Bool) {
|
||||
let controllers = self.viewControllers.filter({ $0 !== controller })
|
||||
if controllers.count != self.viewControllers.count {
|
||||
self.setViewControllers(controllers, animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
public func replaceControllersAndPush(controllers: [UIViewController], controller: ViewController, animated: Bool, options: NavigationAnimationOptions = [], ready: ValuePromise<Bool>? = nil, completion: @escaping () -> Void = {}) {
|
||||
ready?.set(true)
|
||||
var controllers = controllers
|
||||
controllers.append(controller)
|
||||
self.setViewControllers(controllers, animated: animated)
|
||||
completion()
|
||||
}
|
||||
|
||||
public func replaceAllButRootController(_ controller: ViewController, animated: Bool, animationOptions: NavigationAnimationOptions = [], ready: ValuePromise<Bool>? = nil, completion: @escaping () -> Void = {}) {
|
||||
ready?.set(true)
|
||||
var controllers = self.viewControllers
|
||||
while controllers.count > 1 {
|
||||
controllers.removeLast()
|
||||
}
|
||||
controllers.append(controller)
|
||||
self.setViewControllers(controllers, animated: animated)
|
||||
completion()
|
||||
}
|
||||
|
||||
public func popToRoot(animated: Bool) {
|
||||
var controllers = self.viewControllers
|
||||
while controllers.count > 1 {
|
||||
controllers.removeLast()
|
||||
}
|
||||
self.setViewControllers(controllers, animated: animated)
|
||||
}
|
||||
|
||||
override open func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? {
|
||||
var poppedControllers: [UIViewController] = []
|
||||
var found = false
|
||||
var controllers = self.viewControllers
|
||||
if !controllers.contains(where: { $0 === viewController }) {
|
||||
return nil
|
||||
}
|
||||
while !controllers.isEmpty {
|
||||
if controllers[controllers.count - 1] === viewController {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
poppedControllers.insert(controllers[controllers.count - 1], at: 0)
|
||||
controllers.removeLast()
|
||||
}
|
||||
if found {
|
||||
self.setViewControllers(controllers, animated: animated)
|
||||
return poppedControllers
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
open override func popViewController(animated: Bool) -> UIViewController? {
|
||||
var controller: UIViewController?
|
||||
var controllers = self.viewControllers
|
||||
if controllers.count != 0 {
|
||||
controller = controllers[controllers.count - 1] as UIViewController
|
||||
controllers.remove(at: controllers.count - 1)
|
||||
self.setViewControllers(controllers, animated: animated)
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
||||
open override func setViewControllers(_ viewControllers: [UIViewController], animated: Bool) {
|
||||
self._viewControllers = viewControllers.map { controller in
|
||||
let controller = controller as! ViewController
|
||||
controller.navigation_setNavigationController(self)
|
||||
return controller
|
||||
}
|
||||
if let layout = self.validLayout {
|
||||
self.updateContainers(layout: layout, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
preconditionFailure()
|
||||
/*if let controller = viewControllerToPresent as? NavigationController {
|
||||
controller.navigation_setDismiss({ [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
}, rootController: self.view!.window!.rootViewController)
|
||||
self._presentedViewController = controller
|
||||
|
||||
self.view.endEditing(true)
|
||||
if let validLayout = self.validLayout {
|
||||
controller.containerLayoutUpdated(validLayout, transition: .immediate)
|
||||
}
|
||||
|
||||
var ready: Signal<Bool, NoError> = .single(true)
|
||||
|
||||
if let controller = controller.topViewController as? ViewController {
|
||||
ready = controller.ready.get()
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
}
|
||||
|
||||
self.currentPresentDisposable.set(ready.start(next: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
if flag {
|
||||
controller.view.frame = strongSelf.view.bounds.offsetBy(dx: 0.0, dy: strongSelf.view.bounds.height)
|
||||
strongSelf.view.addSubview(controller.view)
|
||||
UIView.animate(withDuration: 0.3, delay: 0.0, options: UIView.AnimationOptions(rawValue: 7 << 16), animations: {
|
||||
controller.view.frame = strongSelf.view.bounds
|
||||
}, completion: { _ in
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
controller.view.frame = strongSelf.view.bounds
|
||||
strongSelf.view.addSubview(controller.view)
|
||||
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
preconditionFailure("NavigationController can't present \(viewControllerToPresent). Only subclasses of NavigationController are allowed.")
|
||||
}*/
|
||||
}
|
||||
|
||||
override open func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
if let controller = self.presentedViewController {
|
||||
if flag {
|
||||
UIView.animate(withDuration: 0.3, delay: 0.0, options: UIView.AnimationOptions(rawValue: 7 << 16), animations: {
|
||||
controller.view.frame = self.view.bounds.offsetBy(dx: 0.0, dy: self.view.bounds.height)
|
||||
}, completion: { _ in
|
||||
controller.view.removeFromSuperview()
|
||||
self._presentedViewController = nil
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
controller.view.removeFromSuperview()
|
||||
self._presentedViewController = nil
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final var currentWindow: WindowHost? {
|
||||
if let window = self.view.window as? WindowHost {
|
||||
return window
|
||||
} else if let superwindow = self.view.window {
|
||||
for subview in superwindow.subviews {
|
||||
if let subview = subview as? WindowHost {
|
||||
return subview
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func scheduleAfterLayout(_ f: @escaping () -> Void) {
|
||||
(self.view as? UITracingLayerView)?.schedule(layout: {
|
||||
f()
|
||||
})
|
||||
self.view.setNeedsLayout()
|
||||
}
|
||||
|
||||
private func scheduleLayoutTransitionRequest(_ transition: ContainedViewLayoutTransition) {
|
||||
let requestId = self.scheduledLayoutTransitionRequestId
|
||||
self.scheduledLayoutTransitionRequestId += 1
|
||||
self.scheduledLayoutTransitionRequest = (requestId, transition)
|
||||
(self.view as? UITracingLayerView)?.schedule(layout: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if let (currentRequestId, currentRequestTransition) = strongSelf.scheduledLayoutTransitionRequest, currentRequestId == requestId {
|
||||
strongSelf.scheduledLayoutTransitionRequest = nil
|
||||
strongSelf.requestLayout(transition: currentRequestTransition)
|
||||
}
|
||||
}
|
||||
})
|
||||
self.view.setNeedsLayout()
|
||||
}
|
||||
|
||||
private func requestLayout(transition: ContainedViewLayoutTransition) {
|
||||
if self.isViewLoaded, let validLayout = self.validLayout {
|
||||
self.containerLayoutUpdated(validLayout, transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
76
submodules/Display/Display/Navigation/NavigationLayout.swift
Normal file
76
submodules/Display/Display/Navigation/NavigationLayout.swift
Normal file
@ -0,0 +1,76 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
|
||||
enum RootNavigationLayout {
|
||||
case split([ViewController], [ViewController])
|
||||
case flat([ViewController])
|
||||
}
|
||||
|
||||
struct ModalContainerLayout {
|
||||
var controllers: [ViewController]
|
||||
}
|
||||
|
||||
struct NavigationLayout {
|
||||
var root: RootNavigationLayout
|
||||
var modal: [ModalContainerLayout]
|
||||
}
|
||||
|
||||
func makeNavigationLayout(layout: ContainerViewLayout, controllers: [ViewController]) -> NavigationLayout {
|
||||
var rootControllers: [ViewController] = []
|
||||
var modalStack: [ModalContainerLayout] = []
|
||||
for controller in controllers {
|
||||
let requiresModal: Bool
|
||||
var beginsModal: Bool = false
|
||||
switch controller.navigationPresentation {
|
||||
case .default:
|
||||
requiresModal = false
|
||||
case .master:
|
||||
requiresModal = false
|
||||
case .modal:
|
||||
requiresModal = true
|
||||
beginsModal = true
|
||||
case .modalInLargeLayout:
|
||||
switch layout.metrics.widthClass {
|
||||
case .compact:
|
||||
requiresModal = false
|
||||
case .regular:
|
||||
requiresModal = true
|
||||
}
|
||||
}
|
||||
if requiresModal {
|
||||
if beginsModal || modalStack.isEmpty {
|
||||
modalStack.append(ModalContainerLayout(controllers: [controller]))
|
||||
} else {
|
||||
modalStack[modalStack.count - 1].controllers.append(controller)
|
||||
}
|
||||
} else if !modalStack.isEmpty {
|
||||
modalStack[modalStack.count - 1].controllers.append(controller)
|
||||
} else {
|
||||
rootControllers.append(controller)
|
||||
}
|
||||
}
|
||||
let rootLayout: RootNavigationLayout
|
||||
switch layout.metrics.widthClass {
|
||||
case .compact:
|
||||
rootLayout = .flat(rootControllers)
|
||||
case .regular:
|
||||
let masterControllers = rootControllers.filter {
|
||||
if case .master = $0.navigationPresentation {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
let detailControllers = rootControllers.filter {
|
||||
if case .master = $0.navigationPresentation {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
rootLayout = .split(masterControllers, detailControllers)
|
||||
}
|
||||
return NavigationLayout(root: rootLayout, modal: modalStack)
|
||||
}
|
@ -0,0 +1,196 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
|
||||
final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate {
|
||||
private var theme: NavigationControllerTheme
|
||||
|
||||
private let dim: ASDisplayNode
|
||||
private let scrollNode: ASScrollNode
|
||||
let container: NavigationContainer
|
||||
|
||||
private(set) var isReady: Bool = false
|
||||
private(set) var dismissProgress: CGFloat = 0.0
|
||||
var isReadyUpdated: (() -> Void)?
|
||||
var updateDismissProgress: ((CGFloat) -> Void)?
|
||||
var interactivelyDismissed: (() -> Void)?
|
||||
|
||||
private var ignoreScrolling = false
|
||||
private var animator: DisplayLinkAnimator?
|
||||
|
||||
init(theme: NavigationControllerTheme, controllerRemoved: @escaping (ViewController) -> Void) {
|
||||
self.theme = theme
|
||||
|
||||
self.dim = ASDisplayNode()
|
||||
self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
|
||||
self.dim.alpha = 0.0
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
|
||||
self.container = NavigationContainer(controllerRemoved: controllerRemoved)
|
||||
self.container.clipsToBounds = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.dim)
|
||||
self.addSubnode(self.scrollNode)
|
||||
self.scrollNode.addSubnode(self.container)
|
||||
|
||||
self.isReady = self.container.isReady
|
||||
self.container.isReadyUpdated = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if !strongSelf.isReady {
|
||||
strongSelf.isReady = true
|
||||
strongSelf.isReadyUpdated?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.animator?.invalidate()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.scrollNode.view.alwaysBounceVertical = false
|
||||
self.scrollNode.view.alwaysBounceHorizontal = false
|
||||
self.scrollNode.view.bounces = false
|
||||
self.scrollNode.view.showsVerticalScrollIndicator = false
|
||||
self.scrollNode.view.showsHorizontalScrollIndicator = false
|
||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
||||
self.scrollNode.view.contentInsetAdjustmentBehavior = .never
|
||||
}
|
||||
self.scrollNode.view.delaysContentTouches = false
|
||||
self.scrollNode.view.delegate = self
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if self.ignoreScrolling {
|
||||
return
|
||||
}
|
||||
var progress = (self.bounds.height - scrollView.bounds.origin.y) / self.bounds.height
|
||||
progress = max(0.0, min(1.0, progress))
|
||||
self.dismissProgress = progress
|
||||
self.dim.alpha = 1.0 - progress
|
||||
self.updateDismissProgress?(progress)
|
||||
}
|
||||
|
||||
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
||||
var progress = (self.bounds.height - scrollView.bounds.origin.y) / self.bounds.height
|
||||
progress = max(0.0, min(1.0, progress))
|
||||
|
||||
self.ignoreScrolling = true
|
||||
targetContentOffset.pointee = scrollView.contentOffset
|
||||
scrollView.setContentOffset(scrollView.contentOffset, animated: false)
|
||||
self.ignoreScrolling = false
|
||||
self.animator?.invalidate()
|
||||
let targetOffset: CGFloat
|
||||
if velocity.y < -0.5 || progress >= 0.5 {
|
||||
targetOffset = 0.0
|
||||
} else {
|
||||
targetOffset = self.bounds.height
|
||||
}
|
||||
let velocityFactor: CGFloat = 0.4 / max(1.0, abs(velocity.y))
|
||||
self.animator = DisplayLinkAnimator(duration: Double(min(0.3, velocityFactor)), from: scrollView.contentOffset.y, to: targetOffset, update: { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: value)
|
||||
}, completion: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.animator = nil
|
||||
if targetOffset == 0.0 {
|
||||
strongSelf.interactivelyDismissed?()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
}
|
||||
|
||||
func update(layout: ContainerViewLayout, controllers: [ViewController], transition: ContainedViewLayoutTransition) {
|
||||
transition.updateFrame(node: self.dim, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
self.scrollNode.view.contentSize = CGSize(width: layout.size.width, height: layout.size.height * 2.0)
|
||||
if !self.scrollNode.view.isDecelerating && !self.scrollNode.view.isDragging && self.animator == nil {
|
||||
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: layout.size))
|
||||
}
|
||||
|
||||
let containerLayout: ContainerViewLayout
|
||||
let containerFrame: CGRect
|
||||
switch layout.metrics.widthClass {
|
||||
case .compact:
|
||||
self.dim.isHidden = true
|
||||
self.container.clipsToBounds = true
|
||||
self.container.cornerRadius = 10.0
|
||||
if #available(iOS 11.0, *) {
|
||||
self.container.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
}
|
||||
|
||||
var topInset: CGFloat = 10.0
|
||||
if let statusBarHeight = layout.statusBarHeight {
|
||||
topInset += statusBarHeight
|
||||
}
|
||||
|
||||
containerLayout = ContainerViewLayout(size: CGSize(width: layout.size.width, height: layout.size.height - topInset), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(top: 0.0, left: layout.intrinsicInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.intrinsicInsets.right), safeInsets: UIEdgeInsets(top: 0.0, left: layout.safeInsets.left, bottom: layout.safeInsets.bottom, right: layout.safeInsets.right), statusBarHeight: nil, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver)
|
||||
containerFrame = CGRect(origin: CGPoint(x: 0.0, y: topInset), size: containerLayout.size)
|
||||
case .regular:
|
||||
self.dim.isHidden = false
|
||||
self.container.clipsToBounds = true
|
||||
self.container.cornerRadius = 10.0
|
||||
if #available(iOS 11.0, *) {
|
||||
self.container.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||
}
|
||||
|
||||
let verticalInset: CGFloat = 44.0
|
||||
|
||||
let maxSide = max(layout.size.width, layout.size.height)
|
||||
let containerSize = CGSize(width: max(layout.size.width - 20.0, floor(maxSide / 2.0)), height: layout.size.height - verticalInset * 2.0)
|
||||
containerFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - containerSize.width) / 2.0), y: floor((layout.size.height - containerSize.height) / 2.0)), size: containerSize)
|
||||
|
||||
var inputHeight: CGFloat?
|
||||
if let inputHeightValue = layout.inputHeight {
|
||||
inputHeight = max(0.0, inputHeightValue - (layout.size.height - containerFrame.maxY))
|
||||
}
|
||||
containerLayout = ContainerViewLayout(size: containerSize, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver)
|
||||
}
|
||||
transition.updateFrame(node: self.container, frame: containerFrame.offsetBy(dx: 0.0, dy: layout.size.height))
|
||||
self.container.update(layout: containerLayout, canBeClosed: true, controllers: controllers, transition: transition)
|
||||
}
|
||||
|
||||
func animateIn(transition: ContainedViewLayoutTransition) {
|
||||
transition.updateAlpha(node: self.dim, alpha: 1.0)
|
||||
transition.animatePositionAdditive(node: self.container, offset: CGPoint(x: 0.0, y: self.bounds.height + self.container.bounds.height / 2.0 - (self.container.position.y - self.bounds.height)))
|
||||
}
|
||||
|
||||
func dismiss(transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) -> ContainedViewLayoutTransition {
|
||||
for controller in self.container.controllers {
|
||||
controller.viewWillDisappear(transition.isAnimated)
|
||||
}
|
||||
|
||||
if transition.isAnimated {
|
||||
let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||
let positionTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||
alphaTransition.updateAlpha(node: self.dim, alpha: 0.0, beginWithCurrentState: true)
|
||||
positionTransition.updatePosition(node: self.container, position: CGPoint(x: self.container.position.x, y: self.bounds.height + self.container.bounds.height / 2.0 + self.bounds.height), beginWithCurrentState: true, completion: { [weak self] _ in
|
||||
completion()
|
||||
})
|
||||
return positionTransition
|
||||
} else {
|
||||
for controller in self.container.controllers {
|
||||
controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
controller.displayNode.removeFromSupernode()
|
||||
controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
controller.viewDidDisappear(transition.isAnimated)
|
||||
}
|
||||
completion()
|
||||
return transition
|
||||
}
|
||||
}
|
||||
}
|
113
submodules/Display/Display/Navigation/NavigationModalFrame.swift
Normal file
113
submodules/Display/Display/Navigation/NavigationModalFrame.swift
Normal file
@ -0,0 +1,113 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
|
||||
private func generateCornerImage(radius: CGFloat, mirror: Bool) -> UIImage? {
|
||||
return generateImage(CGSize(width: radius, height: radius), rotatedContext: { size, context in
|
||||
context.setFillColor(UIColor.black.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: mirror ? (-radius) : 0.0, y: 0.0), size: CGSize(width: radius * 2.0, height: radius * 2.0)))
|
||||
})
|
||||
}
|
||||
|
||||
final class NavigationModalFrame: ASDisplayNode {
|
||||
private let dim: ASDisplayNode
|
||||
private let topShade: ASDisplayNode
|
||||
private let leftShade: ASDisplayNode
|
||||
private let rightShade: ASDisplayNode
|
||||
private let topLeftCorner: ASImageNode
|
||||
private let topRightCorner: ASImageNode
|
||||
|
||||
private var currentMaxCornerRadius: CGFloat?
|
||||
|
||||
private var progress: CGFloat = 0.0
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
init(theme: NavigationControllerTheme) {
|
||||
self.dim = ASDisplayNode()
|
||||
self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
|
||||
self.dim.alpha = 0.0
|
||||
|
||||
self.topShade = ASDisplayNode()
|
||||
self.topShade.backgroundColor = .black
|
||||
self.leftShade = ASDisplayNode()
|
||||
self.leftShade.backgroundColor = .black
|
||||
self.rightShade = ASDisplayNode()
|
||||
self.rightShade.backgroundColor = .black
|
||||
|
||||
self.topLeftCorner = ASImageNode()
|
||||
self.topLeftCorner.displaysAsynchronously = false
|
||||
self.topLeftCorner.displayWithoutProcessing = true
|
||||
self.topRightCorner = ASImageNode()
|
||||
self.topRightCorner.displaysAsynchronously = false
|
||||
self.topRightCorner.displayWithoutProcessing = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.dim)
|
||||
self.addSubnode(self.topShade)
|
||||
self.addSubnode(self.leftShade)
|
||||
self.addSubnode(self.rightShade)
|
||||
self.addSubnode(self.topLeftCorner)
|
||||
self.addSubnode(self.topRightCorner)
|
||||
}
|
||||
|
||||
func update(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = layout
|
||||
|
||||
transition.updateFrame(node: self.dim, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
||||
self.updateShades(layout: layout, progress: 1.0 - self.progress, transition: transition)
|
||||
}
|
||||
|
||||
func animateIn(transition: ContainedViewLayoutTransition) {
|
||||
transition.updateAlpha(node: self.dim, alpha: 1.0)
|
||||
|
||||
if let layout = self.validLayout {
|
||||
self.updateShades(layout: layout, progress: 0.0, transition: .immediate)
|
||||
self.updateShades(layout: layout, progress: 1.0, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
func updateDismissal(transition: ContainedViewLayoutTransition, progress: CGFloat, completion: @escaping () -> Void) {
|
||||
self.progress = progress
|
||||
|
||||
transition.updateAlpha(node: self.dim, alpha: 1.0 - progress, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
if let layout = self.validLayout {
|
||||
self.updateShades(layout: layout, progress: 1.0 - progress, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateShades(layout: ContainerViewLayout, progress: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
let sideInset: CGFloat = 16.0
|
||||
var topInset: CGFloat = 0.0
|
||||
if let statusBarHeight = layout.statusBarHeight {
|
||||
topInset += statusBarHeight
|
||||
}
|
||||
|
||||
let cornerRadius: CGFloat = 8.0
|
||||
let initialCornerRadius: CGFloat
|
||||
if !layout.safeInsets.top.isZero {
|
||||
initialCornerRadius = 40.0
|
||||
} else {
|
||||
initialCornerRadius = 0.0
|
||||
}
|
||||
if self.currentMaxCornerRadius != cornerRadius {
|
||||
self.topLeftCorner.image = generateCornerImage(radius: max(initialCornerRadius, cornerRadius), mirror: false)
|
||||
self.topRightCorner.image = generateCornerImage(radius: max(initialCornerRadius, cornerRadius), mirror: true)
|
||||
}
|
||||
|
||||
let cornerSize = progress * cornerRadius + (1.0 - progress) * initialCornerRadius
|
||||
transition.updateFrame(node: self.topLeftCorner, frame: CGRect(origin: CGPoint(x: progress * sideInset, y: progress * topInset), size: CGSize(width: cornerSize, height: cornerSize)))
|
||||
transition.updateFrame(node: self.topRightCorner, frame: CGRect(origin: CGPoint(x: layout.size.width - progress * sideInset - cornerSize, y: progress * topInset), size: CGSize(width: cornerSize, height: cornerSize)))
|
||||
|
||||
transition.updateFrame(node: self.topShade, frame: CGRect(origin: CGPoint(x: 0.0, y: (1.0 - progress) * (-topInset)), size: CGSize(width: layout.size.width, height: topInset)))
|
||||
transition.updateFrame(node: self.leftShade, frame: CGRect(origin: CGPoint(x: (1.0 - progress) * (-sideInset), y: 0.0), size: CGSize(width: sideInset, height: layout.size.height)))
|
||||
transition.updateFrame(node: self.rightShade, frame: CGRect(origin: CGPoint(x: layout.size.width - sideInset * progress, y: 0.0), size: CGSize(width: sideInset, height: layout.size.height)))
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
|
||||
final class NavigationSplitContainer: ASDisplayNode {
|
||||
private var theme: NavigationControllerTheme
|
||||
|
||||
private let masterContainer: NavigationContainer
|
||||
private let detailContainer: NavigationContainer
|
||||
private let separator: ASDisplayNode
|
||||
|
||||
init(theme: NavigationControllerTheme, controllerRemoved: @escaping (ViewController) -> Void) {
|
||||
self.theme = theme
|
||||
|
||||
self.masterContainer = NavigationContainer(controllerRemoved: controllerRemoved)
|
||||
self.masterContainer.clipsToBounds = true
|
||||
|
||||
self.detailContainer = NavigationContainer(controllerRemoved: controllerRemoved)
|
||||
self.detailContainer.clipsToBounds = true
|
||||
|
||||
self.separator = ASDisplayNode()
|
||||
self.separator.backgroundColor = theme.navigationBar.separatorColor
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.masterContainer)
|
||||
self.addSubnode(self.detailContainer)
|
||||
self.addSubnode(self.separator)
|
||||
}
|
||||
|
||||
func update(layout: ContainerViewLayout, masterControllers: [ViewController], detailControllers: [ViewController], transition: ContainedViewLayoutTransition) {
|
||||
let masterWidth = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0))
|
||||
let detailWidth = layout.size.width - masterWidth
|
||||
|
||||
transition.updateFrame(node: self.masterContainer, frame: CGRect(origin: CGPoint(), size: CGSize(width: masterWidth, height: layout.size.height)))
|
||||
transition.updateFrame(node: self.detailContainer, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: detailWidth, height: layout.size.height)))
|
||||
transition.updateFrame(node: self.separator, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height)))
|
||||
|
||||
self.masterContainer.update(layout: ContainerViewLayout(size: CGSize(width: masterWidth, height: layout.size.height), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), canBeClosed: false, controllers: masterControllers, transition: transition)
|
||||
self.detailContainer.update(layout: ContainerViewLayout(size: CGSize(width: detailWidth, height: layout.size.height), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), canBeClosed: true, controllers: detailControllers, transition: transition)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,8 @@ class ScrollToTopView: UIScrollView, UIScrollViewDelegate {
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.isOpaque = false
|
||||
self.backgroundColor = .clear
|
||||
self.delegate = self
|
||||
self.scrollsToTop = true
|
||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
||||
|
@ -11,4 +11,6 @@ public protocol StatusBarHost {
|
||||
var keyboardView: UIView? { get }
|
||||
|
||||
var handleVolumeControl: Signal<Bool, NoError> { get }
|
||||
|
||||
func setStatusBarStyle(_ style: UIStatusBarStyle, animated: Bool)
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ class StatusBarManager {
|
||||
}
|
||||
self.volumeControlStatusBarNode.isDark = isDark
|
||||
|
||||
if let globalStatusBar = globalStatusBar, !forceHiddenBySystemWindows {
|
||||
/*if let globalStatusBar = globalStatusBar, !forceHiddenBySystemWindows {
|
||||
let statusBarStyle: UIStatusBarStyle
|
||||
if forceInCallStatusBarText != nil {
|
||||
statusBarStyle = .lightContent
|
||||
@ -298,6 +298,6 @@ class StatusBarManager {
|
||||
}
|
||||
} else {
|
||||
statusBarView.alpha = 0.0
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,8 @@ open class ViewControllerPresentationArguments {
|
||||
public enum ViewControllerNavigationPresentation {
|
||||
case `default`
|
||||
case master
|
||||
case modal
|
||||
case modalInLargeLayout
|
||||
}
|
||||
|
||||
@objc open class ViewController: UIViewController, ContainableController {
|
||||
@ -446,9 +448,9 @@ public enum ViewControllerNavigationPresentation {
|
||||
|
||||
override open func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
if let navigationController = self.navigationController as? NavigationController {
|
||||
navigationController.dismiss(animated: flag, completion: completion)
|
||||
navigationController.filterController(self, animated: flag)
|
||||
} else {
|
||||
super.dismiss(animated: flag, completion: completion)
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
@ -468,10 +470,10 @@ public enum ViewControllerNavigationPresentation {
|
||||
public func present(_ controller: ViewController, in context: PresentationContextType, with arguments: Any? = nil, blockInteraction: Bool = false, completion: @escaping () -> Void = {}) {
|
||||
controller.presentationArguments = arguments
|
||||
switch context {
|
||||
case .current:
|
||||
self.presentationContext.present(controller, on: PresentationSurfaceLevel(rawValue: 0), completion: completion)
|
||||
case let .window(level):
|
||||
self.window?.present(controller, on: level, blockInteraction: blockInteraction, completion: completion)
|
||||
case .current:
|
||||
self.presentationContext.present(controller, on: PresentationSurfaceLevel(rawValue: 0), completion: completion)
|
||||
case let .window(level):
|
||||
self.window?.present(controller, on: level, blockInteraction: blockInteraction, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
@ -507,6 +509,7 @@ public enum ViewControllerNavigationPresentation {
|
||||
}
|
||||
|
||||
open func dismiss(completion: (() -> Void)? = nil) {
|
||||
(self.navigationController as? NavigationController)?.filterController(self, animated: true)
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension 9.0, iOS 9.0, *)
|
||||
|
@ -692,6 +692,10 @@ public class Window1 {
|
||||
self._rootController = value
|
||||
|
||||
if let rootController = self._rootController {
|
||||
if let rootController = rootController as? NavigationController {
|
||||
rootController.statusBarHost = self.statusBarHost
|
||||
rootController.keyboardManager = self.keyboardManager
|
||||
}
|
||||
if !self.windowLayout.size.width.isZero && !self.windowLayout.size.height.isZero {
|
||||
rootController.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics), transition: .immediate)
|
||||
}
|
||||
@ -803,7 +807,7 @@ public class Window1 {
|
||||
}
|
||||
}
|
||||
}
|
||||
keyboardManager.surfaces = keyboardSurfaces
|
||||
//keyboardManager.surfaces = keyboardSurfaces
|
||||
|
||||
var supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .all)
|
||||
let orientationToLock: UIInterfaceOrientationMask
|
||||
|
@ -62,7 +62,6 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
var currentAccessibilityAreas: [AccessibilityAreaNode] = []
|
||||
|
||||
private var previousContentOffset: CGPoint?
|
||||
private var isDeceleratingBecauseOfDragging = false
|
||||
|
||||
private let hiddenMediaDisposable = MetaDisposable()
|
||||
@ -372,7 +371,6 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
self.scrollNode.view.contentOffset = contentOffset
|
||||
if didSetScrollOffset {
|
||||
self.previousContentOffset = contentOffset
|
||||
self.updateNavigationBar()
|
||||
if self.currentLayout != nil {
|
||||
self.setupScrollOffsetOnLayout = false
|
||||
@ -660,8 +658,6 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
self.updateVisibleItems(visibleBounds: self.scrollNode.view.bounds)
|
||||
self.updateNavigationBar()
|
||||
self.previousContentOffset = self.scrollNode.view.contentOffset
|
||||
}
|
||||
|
||||
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||
@ -694,50 +690,14 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
minBarHeight = 20.0
|
||||
}
|
||||
|
||||
var pageProgress: CGFloat = 0.0
|
||||
if !self.scrollNode.view.contentSize.height.isZero {
|
||||
let value = (contentOffset.y + self.scrollNode.view.contentInset.top) / (self.scrollNode.view.contentSize.height - bounds.size.height + self.scrollNode.view.contentInset.top)
|
||||
pageProgress = max(0.0, min(1.0, value))
|
||||
}
|
||||
|
||||
let delta: CGFloat
|
||||
if self.setupScrollOffsetOnLayout {
|
||||
delta = 0.0
|
||||
} else if let previousContentOffset = self.previousContentOffset {
|
||||
delta = contentOffset.y - previousContentOffset.y
|
||||
} else {
|
||||
delta = 0.0
|
||||
}
|
||||
self.previousContentOffset = contentOffset
|
||||
|
||||
var transition: ContainedViewLayoutTransition = .immediate
|
||||
var navigationBarFrame = self.navigationBar.frame
|
||||
navigationBarFrame.size.width = bounds.size.width
|
||||
if navigationBarFrame.size.height.isZero {
|
||||
navigationBarFrame.size.height = maxBarHeight
|
||||
}
|
||||
if forceState {
|
||||
transition = .animated(duration: 0.3, curve: .spring)
|
||||
|
||||
let transitionFactor = (navigationBarFrame.size.height - minBarHeight) / (maxBarHeight - minBarHeight)
|
||||
|
||||
if contentOffset.y <= -self.scrollNode.view.contentInset.top || transitionFactor > 0.4 {
|
||||
navigationBarFrame.size.height = maxBarHeight
|
||||
} else {
|
||||
navigationBarFrame.size.height = minBarHeight
|
||||
}
|
||||
} else {
|
||||
if contentOffset.y <= -self.scrollNode.view.contentInset.top {
|
||||
navigationBarFrame.size.height = maxBarHeight
|
||||
} else {
|
||||
navigationBarFrame.size.height -= delta
|
||||
}
|
||||
navigationBarFrame.size.height = max(minBarHeight, min(maxBarHeight, navigationBarFrame.size.height))
|
||||
}
|
||||
|
||||
if self.setupScrollOffsetOnLayout {
|
||||
navigationBarFrame.size.height = maxBarHeight
|
||||
}
|
||||
navigationBarFrame.size.height = maxBarHeight
|
||||
|
||||
let transitionFactor = (navigationBarFrame.size.height - minBarHeight) / (maxBarHeight - minBarHeight)
|
||||
|
||||
@ -756,7 +716,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.navigationBar, frame: navigationBarFrame)
|
||||
self.navigationBar.updateLayout(size: navigationBarFrame.size, minHeight: minBarHeight, maxHeight: maxBarHeight, topInset: containerLayout.safeInsets.top, leftInset: containerLayout.safeInsets.left, rightInset: containerLayout.safeInsets.right, title: title, pageProgress: pageProgress, transition: transition)
|
||||
self.navigationBar.updateLayout(size: navigationBarFrame.size, minHeight: minBarHeight, maxHeight: maxBarHeight, topInset: containerLayout.safeInsets.top, leftInset: containerLayout.safeInsets.left, rightInset: containerLayout.safeInsets.right, title: title, pageProgress: 0.0, transition: transition)
|
||||
|
||||
transition.animateView {
|
||||
self.scrollNode.view.scrollIndicatorInsets = UIEdgeInsets(top: navigationBarFrame.size.height, left: 0.0, bottom: containerLayout.intrinsicInsets.bottom, right: 0.0)
|
||||
|
@ -505,14 +505,6 @@ open class ItemListController<Entry: ItemListNodeEntry>: ViewController, KeyShor
|
||||
self.didDisappear?(animated)
|
||||
}
|
||||
|
||||
override open func dismiss(completion: (() -> Void)? = nil) {
|
||||
if !self.isDismissed {
|
||||
self.isDismissed = true
|
||||
(self.displayNode as! ItemListControllerNode<Entry>).animateOut(completion: completion)
|
||||
self.updateTransitionWhenPresentedAsModal?(0.0, .animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
}
|
||||
|
||||
public func frameForItemNode(_ predicate: (ListViewItemNode) -> Bool) -> CGRect? {
|
||||
var result: CGRect?
|
||||
(self.displayNode as! ItemListControllerNode<Entry>).listNode.forEachItemNode { itemNode in
|
||||
|
@ -508,7 +508,7 @@ open class LegacyController: ViewController, PresentableController {
|
||||
case .custom:
|
||||
self.presentingViewController?.dismiss(animated: false, completion: completion)
|
||||
case .navigation:
|
||||
break
|
||||
(self.navigationController as? NavigationController)?.filterController(self, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,8 @@ private func generateClearIcon(color: UIColor) -> UIImage? {
|
||||
}
|
||||
|
||||
public func legacyLocationPickerController(context: AccountContext, selfPeer: Peer, peer: Peer, sendLocation: @escaping (CLLocationCoordinate2D, MapVenue?, String?) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32) -> Void, theme: PresentationTheme, customLocationPicker: Bool = false, hasLiveLocation: Bool = true, presentationCompleted: @escaping () -> Void = {}) -> ViewController {
|
||||
let legacyController = LegacyController(presentation: .modal(animateIn: true), theme: theme)
|
||||
let legacyController = LegacyController(presentation: .navigation, theme: theme)
|
||||
legacyController.navigationPresentation = .modal
|
||||
legacyController.presentationCompleted = {
|
||||
presentationCompleted()
|
||||
}
|
||||
|
@ -1105,6 +1105,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
controller.navigationPresentation = .modal
|
||||
controller.experimentalSnapScrollToItem = true
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.view.endEditing(true)
|
||||
|
@ -600,20 +600,20 @@ public func channelAdminsController(context: AccountContext, peerId: PeerId, loa
|
||||
}
|
||||
}
|
||||
}
|
||||
presentControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: peer.id, initialParticipant: participant?.participant, updated: { _ in
|
||||
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
pushControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: peer.id, initialParticipant: participant?.participant, updated: { _ in
|
||||
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership))
|
||||
})
|
||||
dismissController = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
}
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
pushControllerImpl?(controller)
|
||||
|
||||
return current
|
||||
}
|
||||
})
|
||||
}, openAdmin: { participant in
|
||||
presentControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: participant.peerId, initialParticipant: participant, updated: { _ in
|
||||
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
pushControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: participant.peerId, initialParticipant: participant, updated: { _ in
|
||||
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership))
|
||||
})
|
||||
|
||||
let membersAndLoadMoreControl: (Disposable, PeerChannelMemberCategoryControl?)
|
||||
@ -734,14 +734,14 @@ public func channelAdminsController(context: AccountContext, peerId: PeerId, loa
|
||||
}
|
||||
}, openPeer: { _, participant in
|
||||
if let participant = participant?.participant, case .member = participant {
|
||||
presentControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: participant.peerId, initialParticipant: participant, updated: { _ in
|
||||
pushControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: participant.peerId, initialParticipant: participant, updated: { _ in
|
||||
updateState { state in
|
||||
return state.withUpdatedSearchingMembers(false)
|
||||
}
|
||||
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership))
|
||||
}
|
||||
}, present: { c, a in
|
||||
presentControllerImpl?(c, a)
|
||||
}, pushController: { c in
|
||||
pushControllerImpl?(c)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -486,8 +486,8 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId)
|
||||
if let rendered = rendered, case .member = rendered.participant {
|
||||
arguments.openPeer(rendered)
|
||||
}
|
||||
}, present: { c, a in
|
||||
presentControllerImpl?(c, a)
|
||||
}, pushController: { c in
|
||||
pushControllerImpl?(c)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -403,8 +403,8 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
|
||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Channel_AddBotErrorHaveRights, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Channel_AddBotAsAdmin, action: {
|
||||
contactsController?.dismiss()
|
||||
|
||||
presentControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: memberId, initialParticipant: nil, updated: { _ in
|
||||
}, upgradedToSupergroup: { _, f in f () }, transferedOwnership: { _ in }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
pushControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: memberId, initialParticipant: nil, updated: { _ in
|
||||
}, upgradedToSupergroup: { _, f in f () }, transferedOwnership: { _ in }))
|
||||
})]), nil)
|
||||
} else {
|
||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Channel_AddBotErrorHaveRights, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
@ -500,8 +500,8 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
|
||||
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) {
|
||||
pushControllerImpl?(infoController)
|
||||
}
|
||||
}, present: { c, a in
|
||||
presentControllerImpl?(c, a)
|
||||
}, pushController: { c in
|
||||
pushControllerImpl?(c)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -296,7 +296,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
|
||||
|
||||
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder, PresentationDateTimeFormat)>
|
||||
|
||||
init(context: AccountContext, peerId: PeerId, mode: ChannelMembersSearchMode, filters: [ChannelMembersSearchFilter], searchContext: GroupMembersSearchContext?, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, updateActivity: @escaping (Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
init(context: AccountContext, peerId: PeerId, mode: ChannelMembersSearchMode, filters: [ChannelMembersSearchFilter], searchContext: GroupMembersSearchContext?, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, updateActivity: @escaping (Bool) -> Void, pushController: @escaping (ViewController) -> Void) {
|
||||
self.context = context
|
||||
self.openPeer = openPeer
|
||||
self.mode = mode
|
||||
@ -341,16 +341,16 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
|
||||
state.revealedPeerId = nil
|
||||
return state
|
||||
}
|
||||
present(channelAdminController(context: context, peerId: peerId, adminId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
|
||||
}, upgradedToSupergroup: { _, f in f() }, transferedOwnership: { _ in }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
pushController(channelAdminController(context: context, peerId: peerId, adminId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
|
||||
}, upgradedToSupergroup: { _, f in f() }, transferedOwnership: { _ in }))
|
||||
}, restrictPeer: { participant in
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.revealedPeerId = nil
|
||||
return state
|
||||
}
|
||||
present(channelBannedMemberController(context: context, peerId: peerId, memberId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
|
||||
}, upgradedToSupergroup: { _, f in f() }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
pushController(channelBannedMemberController(context: context, peerId: peerId, memberId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
|
||||
}, upgradedToSupergroup: { _, f in f() }))
|
||||
}, removePeer: { memberId in
|
||||
updateState { state in
|
||||
var state = state
|
||||
|
@ -47,6 +47,8 @@ final class ChannelMembersSearchController: ViewController {
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
|
||||
self.navigationPresentation = .modal
|
||||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
|
||||
self.title = self.presentationData.strings.Channel_Members_Title
|
||||
@ -83,8 +85,8 @@ final class ChannelMembersSearchController: ViewController {
|
||||
self.controllerNode.requestOpenPeerFromSearch = { [weak self] peer, participant in
|
||||
self?.openPeer(peer, participant)
|
||||
}
|
||||
self.controllerNode.present = { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
self.controllerNode.pushController = { [weak self] c in
|
||||
(self?.navigationController as? NavigationController)?.pushViewController(c)
|
||||
}
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
@ -113,14 +115,6 @@ final class ChannelMembersSearchController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
override func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.view.endEditing(true)
|
||||
self.controllerNode.animateOut(completion: { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
|
@ -108,7 +108,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
||||
var requestActivateSearch: (() -> Void)?
|
||||
var requestDeactivateSearch: (() -> Void)?
|
||||
var requestOpenPeerFromSearch: ((Peer, RenderedChannelParticipant?) -> Void)?
|
||||
var present: ((ViewController, Any?) -> Void)?
|
||||
var pushController: ((ViewController) -> Void)?
|
||||
|
||||
var presentationData: PresentationData
|
||||
|
||||
@ -362,8 +362,8 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
||||
self?.requestOpenPeerFromSearch?(peer, participant)
|
||||
}, updateActivity: { value in
|
||||
|
||||
}, present: { [weak self] c, a in
|
||||
self?.present?(c, a)
|
||||
}, pushController: { [weak self] c in
|
||||
self?.pushController?(c)
|
||||
}), cancel: { [weak self] in
|
||||
if let requestDeactivateSearch = self?.requestDeactivateSearch {
|
||||
requestDeactivateSearch()
|
||||
|
@ -797,8 +797,8 @@ public func channelPermissionsController(context: AccountContext, peerId origina
|
||||
upgradedToSupergroupImpl?(upgradedPeerId, f)
|
||||
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
}, present: { c, a in
|
||||
presentControllerImpl?(c, a)
|
||||
}, pushController: { c in
|
||||
pushControllerImpl?(c)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -811,6 +811,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
|
||||
var addToExistingImpl: (() -> Void)?
|
||||
var openChatImpl: ((PeerId) -> Void)?
|
||||
var replaceControllerImpl: ((ViewController) -> Void)?
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
var openUrlImpl: ((String) -> Void)?
|
||||
var openAddressImpl: ((DeviceContactAddressData) -> Void)?
|
||||
@ -908,7 +909,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
|
||||
return state
|
||||
}
|
||||
}, updatePhoneLabel: { id, currentLabel in
|
||||
presentControllerImpl?(phoneLabelController(context: context, currentLabel: currentLabel, completion: { value in
|
||||
pushControllerImpl?(phoneLabelController(context: context, currentLabel: currentLabel, completion: { value in
|
||||
updateState { state in
|
||||
var state = state
|
||||
for i in 0 ..< state.phoneNumbers.count {
|
||||
@ -919,7 +920,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
|
||||
}
|
||||
return state
|
||||
}
|
||||
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}))
|
||||
}, deletePhone: { id in
|
||||
updateState { state in
|
||||
var state = state
|
||||
@ -976,14 +977,14 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
case .createContact:
|
||||
presentControllerImpl?(deviceContactInfoController(context: context, subject: .create(peer: subject.peer, contactData: subject.contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
||||
pushControllerImpl?(deviceContactInfoController(context: context, subject: .create(peer: subject.peer, contactData: subject.contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
||||
dismissImpl?(false)
|
||||
if let peer = peer {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
}), completed: nil, cancelled: nil), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}), completed: nil, cancelled: nil))
|
||||
case .addToExisting:
|
||||
addToExistingImpl?()
|
||||
case .sendMessage:
|
||||
@ -1210,6 +1211,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
|
||||
}
|
||||
|
||||
let controller = DeviceContactInfoController(context: context, state: signal)
|
||||
controller.navigationPresentation = .modal
|
||||
addToExistingImpl = { [weak controller] in
|
||||
guard let controller = controller else {
|
||||
return
|
||||
@ -1226,6 +1228,9 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
|
||||
replaceControllerImpl = { [weak controller] value in
|
||||
(controller?.navigationController as? NavigationController)?.replaceTopController(value, animated: true)
|
||||
}
|
||||
pushControllerImpl = { [weak controller] c in
|
||||
(controller?.navigationController as? NavigationController)?.pushViewController(c)
|
||||
}
|
||||
presentControllerImpl = { [weak controller] value, presentationArguments in
|
||||
controller?.present(value, in: .window(.root), with: presentationArguments)
|
||||
}
|
||||
@ -1293,7 +1298,8 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
|
||||
|
||||
private func addContactToExisting(context: AccountContext, parentController: ViewController, contactData: DeviceContactExtendedData, completion: @escaping (Peer?, DeviceContactStableId, DeviceContactExtendedData) -> Void) {
|
||||
let contactsController = context.sharedContext.makeContactSelectionController(ContactSelectionControllerParams(context: context, title: { $0.Contacts_Title }, displayDeviceContacts: true))
|
||||
parentController.present(contactsController, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
contactsController.navigationPresentation = .modal
|
||||
(parentController.navigationController as? NavigationController)?.pushViewController(contactsController)
|
||||
let _ = (contactsController.result
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let peer = peer {
|
||||
|
@ -1868,10 +1868,10 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
|
||||
let _ = (peerView.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { peerView in
|
||||
presentControllerImpl?(channelAdminController(context: context, peerId: peerView.peerId, adminId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
|
||||
pushControllerImpl?(channelAdminController(context: context, peerId: peerView.peerId, adminId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
|
||||
}, upgradedToSupergroup: { upgradedPeerId, f in
|
||||
upgradedToSupergroupImpl?(upgradedPeerId, f)
|
||||
}, transferedOwnership: { _ in }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, transferedOwnership: { _ in }))
|
||||
})
|
||||
}, restrictPeer: { participant in
|
||||
let _ = (peerView.get()
|
||||
@ -2257,8 +2257,8 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
|
||||
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) {
|
||||
arguments.pushController(infoController)
|
||||
}
|
||||
}, present: { c, a in
|
||||
presentControllerImpl?(c, a)
|
||||
}, pushController: { c in
|
||||
pushControllerImpl?(c)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -14,20 +14,20 @@ final class ChannelMembersSearchItem: ItemListControllerSearch {
|
||||
let searchContext: GroupMembersSearchContext?
|
||||
let cancel: () -> Void
|
||||
let openPeer: (Peer, RenderedChannelParticipant?) -> Void
|
||||
let present: (ViewController, Any?) -> Void
|
||||
let pushController: (ViewController) -> Void
|
||||
let searchMode: ChannelMembersSearchMode
|
||||
|
||||
private var updateActivity: ((Bool) -> Void)?
|
||||
private var activity: ValuePromise<Bool> = ValuePromise(ignoreRepeated: false)
|
||||
private let activityDisposable = MetaDisposable()
|
||||
|
||||
init(context: AccountContext, peerId: PeerId, searchContext: GroupMembersSearchContext?, searchMode: ChannelMembersSearchMode = .searchMembers, cancel: @escaping () -> Void, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
init(context: AccountContext, peerId: PeerId, searchContext: GroupMembersSearchContext?, searchMode: ChannelMembersSearchMode = .searchMembers, cancel: @escaping () -> Void, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, pushController: @escaping (ViewController) -> Void) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.searchContext = searchContext
|
||||
self.cancel = cancel
|
||||
self.openPeer = openPeer
|
||||
self.present = present
|
||||
self.pushController = pushController
|
||||
self.searchMode = searchMode
|
||||
activityDisposable.set((activity.get() |> mapToSignal { value -> Signal<Bool, NoError> in
|
||||
if value {
|
||||
@ -73,8 +73,8 @@ final class ChannelMembersSearchItem: ItemListControllerSearch {
|
||||
func node(current: ItemListControllerSearchNode?, titleContentNode: (NavigationBarContentNode & ItemListControllerSearchNavigationContentNode)?) -> ItemListControllerSearchNode {
|
||||
return ChannelMembersSearchItemNode(context: self.context, peerId: self.peerId, searchMode: self.searchMode, searchContext: self.searchContext, openPeer: self.openPeer, cancel: self.cancel, updateActivity: { [weak self] value in
|
||||
self?.activity.set(value)
|
||||
}, present: { [weak self] c, a in
|
||||
self?.present(c, a)
|
||||
}, pushController: { [weak self] c in
|
||||
self?.pushController(c)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -82,10 +82,10 @@ final class ChannelMembersSearchItem: ItemListControllerSearch {
|
||||
private final class ChannelMembersSearchItemNode: ItemListControllerSearchNode {
|
||||
private let containerNode: ChannelMembersSearchContainerNode
|
||||
|
||||
init(context: AccountContext, peerId: PeerId, searchMode: ChannelMembersSearchMode, searchContext: GroupMembersSearchContext?, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, cancel: @escaping () -> Void, updateActivity: @escaping(Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
init(context: AccountContext, peerId: PeerId, searchMode: ChannelMembersSearchMode, searchContext: GroupMembersSearchContext?, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, cancel: @escaping () -> Void, updateActivity: @escaping(Bool) -> Void, pushController: @escaping (ViewController) -> Void) {
|
||||
self.containerNode = ChannelMembersSearchContainerNode(context: context, peerId: peerId, mode: searchMode, filters: [], searchContext: searchContext, openPeer: { peer, participant in
|
||||
openPeer(peer, participant)
|
||||
}, updateActivity: updateActivity, present: present)
|
||||
}, updateActivity: updateActivity, pushController: pushController)
|
||||
self.containerNode.cancel = {
|
||||
cancel()
|
||||
}
|
||||
|
@ -126,6 +126,7 @@ public func phoneLabelController(context: AccountContext, currentLabel: String,
|
||||
let controller = ItemListController(context: context, state: signal
|
||||
|> afterDisposed {
|
||||
})
|
||||
controller.navigationPresentation = .modal
|
||||
controller.enableInteractiveDismiss = true
|
||||
|
||||
completeImpl = { [weak controller] in
|
||||
|
@ -766,7 +766,7 @@ private func getUserPeer(postbox: Postbox, peerId: PeerId) -> Signal<(Peer?, Cac
|
||||
}
|
||||
}
|
||||
|
||||
public func openAddPersonContactImpl(context: AccountContext, peerId: PeerId, present: @escaping (ViewController, Any?) -> Void) {
|
||||
public func openAddPersonContactImpl(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId)
|
||||
|> deliverOnMainQueue).start(next: { peer, cachedData in
|
||||
guard let user = peer as? TelegramUser, let contactData = DeviceContactExtendedData(peer: user) else {
|
||||
@ -778,7 +778,7 @@ public func openAddPersonContactImpl(context: AccountContext, peerId: PeerId, pr
|
||||
shareViaException = peerStatusSettings.contains(.addExceptionWhenAddingContact)
|
||||
}
|
||||
|
||||
present(deviceContactInfoController(context: context, subject: .create(peer: user, contactData: contactData, isSharing: true, shareViaException: shareViaException, completion: { peer, stableId, contactData in
|
||||
pushController(deviceContactInfoController(context: context, subject: .create(peer: user, contactData: contactData, isSharing: true, shareViaException: shareViaException, completion: { peer, stableId, contactData in
|
||||
if let peer = peer as? TelegramUser {
|
||||
if let phone = peer.phone, !phone.isEmpty {
|
||||
}
|
||||
@ -786,7 +786,7 @@ public func openAddPersonContactImpl(context: AccountContext, peerId: PeerId, pr
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
present(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.AddContact_StatusSuccess(peer.compactDisplayTitle).0, true)), nil)
|
||||
}
|
||||
}), completed: nil, cancelled: nil), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}), completed: nil, cancelled: nil))
|
||||
})
|
||||
}
|
||||
|
||||
@ -916,7 +916,9 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Pe
|
||||
}, openChat: {
|
||||
openChatImpl?()
|
||||
}, addContact: {
|
||||
openAddPersonContactImpl(context: context, peerId: peerId, present: { c, a in
|
||||
openAddPersonContactImpl(context: context, peerId: peerId, pushController: { c in
|
||||
pushControllerImpl?(c)
|
||||
}, present: { c, a in
|
||||
presentControllerImpl?(c, a)
|
||||
})
|
||||
}, shareContact: {
|
||||
|
@ -315,7 +315,7 @@ public func proxySettingsController(context: AccountContext, mode: ProxySettings
|
||||
}
|
||||
|
||||
public func proxySettingsController(accountManager: AccountManager, postbox: Postbox, network: Network, mode: ProxySettingsControllerMode, theme: PresentationTheme, strings: PresentationStrings, updatedPresentationData: Signal<(theme: PresentationTheme, strings: PresentationStrings), NoError>) -> ViewController {
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
let stateValue = Atomic(value: ProxySettingsControllerState())
|
||||
let statePromise = ValuePromise<ProxySettingsControllerState>(stateValue.with { $0 })
|
||||
@ -342,7 +342,7 @@ public func proxySettingsController(accountManager: AccountManager, postbox: Pos
|
||||
return current
|
||||
}).start()
|
||||
}, addNewServer: {
|
||||
presentControllerImpl?(proxyServerSettingsController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, accountManager: accountManager, postbox: postbox, network: network, currentSettings: nil), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
pushControllerImpl?(proxyServerSettingsController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, accountManager: accountManager, postbox: postbox, network: network, currentSettings: nil))
|
||||
}, activateServer: { server in
|
||||
let _ = updateProxySettingsInteractively(accountManager: accountManager, { current in
|
||||
var current = current
|
||||
@ -355,7 +355,7 @@ public func proxySettingsController(accountManager: AccountManager, postbox: Pos
|
||||
return current
|
||||
}).start()
|
||||
}, editServer: { server in
|
||||
presentControllerImpl?(proxyServerSettingsController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, accountManager: accountManager, postbox: postbox, network: network, currentSettings: server), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
pushControllerImpl?(proxyServerSettingsController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, accountManager: accountManager, postbox: postbox, network: network, currentSettings: server))
|
||||
}, removeServer: { server in
|
||||
let _ = updateProxySettingsInteractively(accountManager: accountManager, { current in
|
||||
var current = current
|
||||
@ -438,8 +438,9 @@ public func proxySettingsController(accountManager: AccountManager, postbox: Pos
|
||||
}
|
||||
|
||||
let controller = ItemListController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, state: signal, tabBarItem: nil)
|
||||
presentControllerImpl = { [weak controller] c, a in
|
||||
controller?.present(c, in: .window(.root), with: a)
|
||||
controller.navigationPresentation = .modal
|
||||
pushControllerImpl = { [weak controller] c in
|
||||
(controller?.navigationController as? NavigationController)?.pushViewController(c)
|
||||
}
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
|
@ -369,6 +369,7 @@ func proxyServerSettingsController(theme: PresentationTheme, strings: Presentati
|
||||
}
|
||||
|
||||
let controller = ItemListController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, state: signal, tabBarItem: nil)
|
||||
controller.navigationPresentation = .modal
|
||||
presentImpl = { [weak controller] c, d in
|
||||
controller?.present(c, in: .window(.root), with: d)
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
|
||||
}
|
||||
}
|
||||
arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
|
||||
arguments.pushController(controller)
|
||||
}))
|
||||
}
|
||||
items.append(ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in
|
||||
@ -225,7 +225,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
|
||||
}
|
||||
}
|
||||
arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
|
||||
arguments.pushController(controller)
|
||||
}))
|
||||
}
|
||||
|
||||
@ -272,7 +272,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
|
||||
}
|
||||
}
|
||||
arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
|
||||
arguments.pushController(controller)
|
||||
})
|
||||
})
|
||||
case let .sendCriticalLogs(theme):
|
||||
@ -301,7 +301,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
|
||||
}
|
||||
}
|
||||
arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
|
||||
arguments.pushController(controller)
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -211,7 +211,7 @@ private enum SettingsEntry: ItemListNodeEntry {
|
||||
})
|
||||
case let .username(theme, text, address):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: address, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
||||
arguments.presentController(usernameSetupController(context: arguments.context))
|
||||
arguments.pushController(usernameSetupController(context: arguments.context))
|
||||
})
|
||||
case let .addAccount(theme, text):
|
||||
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .center, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
||||
|
@ -894,7 +894,7 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
|
||||
})
|
||||
}
|
||||
dismissInputImpl?()
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
pushController(controller)
|
||||
}, updateRevealedPeerId: { peerId in
|
||||
updateState { current in
|
||||
return current.withUpdatedRevealedPeerId(peerId)
|
||||
|
@ -239,7 +239,7 @@ public func blockedPeersController(context: AccountContext, blockedPeersContext:
|
||||
strongController.dismiss()
|
||||
}))
|
||||
}
|
||||
presentControllerImpl?(controller, nil)
|
||||
pushControllerImpl?(controller)
|
||||
}, removePeer: { memberId in
|
||||
updateState {
|
||||
return $0.withUpdatedRemovingPeerId(memberId)
|
||||
|
@ -50,6 +50,8 @@ public final class ThemePreviewController: ViewController {
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.previewTheme, presentationStrings: self.presentationData.strings))
|
||||
|
||||
self.navigationPresentation = .modal
|
||||
|
||||
self.isModalWhenInOverlay = true
|
||||
|
||||
let themeName: String
|
||||
@ -333,13 +335,6 @@ public final class ThemePreviewController: ViewController {
|
||||
}))
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.controllerNode.animateOut(completion: { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
|
@ -345,6 +345,7 @@ public func usernameSetupController(context: AccountContext) -> ViewController {
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
controller.navigationPresentation = .modal
|
||||
controller.enableInteractiveDismiss = true
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.view.endEditing(true)
|
||||
|
@ -61,6 +61,13 @@ extension PeekControllerTheme {
|
||||
|
||||
public extension NavigationControllerTheme {
|
||||
convenience init(presentationTheme: PresentationTheme) {
|
||||
self.init(navigationBar: NavigationBarTheme(rootControllerTheme: presentationTheme), emptyAreaColor: presentationTheme.chatList.backgroundColor)
|
||||
let navigationStatusBar: NavigationStatusBarStyle
|
||||
switch presentationTheme.rootController.statusBarStyle {
|
||||
case .black:
|
||||
navigationStatusBar = .black
|
||||
case .white:
|
||||
navigationStatusBar = .white
|
||||
}
|
||||
self.init(statusBar: navigationStatusBar, navigationBar: NavigationBarTheme(rootControllerTheme: presentationTheme), emptyAreaColor: presentationTheme.chatList.backgroundColor)
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,15 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
self.theme = theme
|
||||
self.openUrl = openUrl
|
||||
|
||||
super.init(mode: .single, theme: NavigationControllerTheme(navigationBar: AuthorizationSequenceController.navigationBarTheme(theme), emptyAreaColor: .black))
|
||||
let navigationStatusBar: NavigationStatusBarStyle
|
||||
switch theme.rootController.statusBarStyle {
|
||||
case .black:
|
||||
navigationStatusBar = .black
|
||||
case .white:
|
||||
navigationStatusBar = .white
|
||||
}
|
||||
|
||||
super.init(mode: .single, theme: NavigationControllerTheme(statusBar: navigationStatusBar, navigationBar: AuthorizationSequenceController.navigationBarTheme(theme), emptyAreaColor: .black))
|
||||
|
||||
self.stateDisposable = (account.postbox.stateView()
|
||||
|> map { view -> InnerState in
|
||||
|
@ -987,7 +987,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}, openTheme: { [weak self] message in
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) {
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
openChatTheme(context: strongSelf.context, message: message, present: { [weak self] c, a in
|
||||
openChatTheme(context: strongSelf.context, message: message, pushController: { [weak self] c in
|
||||
(self?.navigationController as? NavigationController)?.pushViewController(c)
|
||||
}, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a, blockInteraction: true)
|
||||
})
|
||||
}
|
||||
@ -5265,7 +5267,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
let _ = legacyAssetPicker(context: strongSelf.context, presentationData: strongSelf.presentationData, editingMedia: editingMedia, fileMode: fileMode, peer: peer, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, selectionLimit: selectionLimit).start(next: { generator in
|
||||
if let strongSelf = self {
|
||||
let legacyController = LegacyController(presentation: .modal(animateIn: true), theme: strongSelf.presentationData.theme, initialLayout: strongSelf.validLayout)
|
||||
let legacyController = LegacyController(presentation: .navigation, theme: strongSelf.presentationData.theme, initialLayout: strongSelf.validLayout)
|
||||
legacyController.navigationPresentation = .modal
|
||||
legacyController.statusBar.statusBarStyle = strongSelf.presentationData.theme.rootController.statusBarStyle.style
|
||||
legacyController.controllerLoaded = { [weak legacyController] in
|
||||
legacyController?.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
@ -5290,7 +5293,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
})
|
||||
}))
|
||||
strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
}
|
||||
}, presentSelectionLimitExceeded: {
|
||||
guard let strongSelf = self else {
|
||||
@ -5330,7 +5333,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
strongSelf.present(legacyController, in: .window(.root))
|
||||
legacyController.navigationPresentation = .modal
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(legacyController)
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -5385,7 +5389,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
strongSelf.present(legacyLocationPickerController(context: strongSelf.context, selfPeer: selfPeer, peer: peer, sendLocation: { coordinate, venue, _ in
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(legacyLocationPickerController(context: strongSelf.context, selfPeer: selfPeer, peer: peer, sendLocation: { coordinate, venue, _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -5422,15 +5426,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
strongSelf.sendMessages([message])
|
||||
}
|
||||
}, theme: strongSelf.presentationData.theme, hasLiveLocation: !strongSelf.presentationInterfaceState.isScheduledMessages), in: .window(.root))
|
||||
}, theme: strongSelf.presentationData.theme, hasLiveLocation: !strongSelf.presentationInterfaceState.isScheduledMessages))
|
||||
})
|
||||
}
|
||||
|
||||
private func presentContactPicker() {
|
||||
let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: self.context, title: { $0.Contacts_Title }, displayDeviceContacts: true))
|
||||
contactsController.navigationPresentation = .modal
|
||||
self.chatDisplayNode.dismissInput()
|
||||
self.present(contactsController, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
self.controllerNavigationDisposable.set((contactsController.result |> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
(self.navigationController as? NavigationController)?.pushViewController(contactsController)
|
||||
self.controllerNavigationDisposable.set((contactsController.result
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
let dataSignal: Signal<(Peer?, DeviceContactExtendedData?), NoError>
|
||||
switch peer {
|
||||
@ -5488,7 +5494,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil)
|
||||
strongSelf.sendMessages([message])
|
||||
} else {
|
||||
strongSelf.present(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
|
||||
let contactController = strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
|
||||
guard let strongSelf = self, !contactData.basicData.phoneNumbers.isEmpty else {
|
||||
return
|
||||
}
|
||||
@ -5506,7 +5512,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil)
|
||||
strongSelf.sendMessages([message])
|
||||
}
|
||||
}), completed: nil, cancelled: nil), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}), completed: nil, cancelled: nil)
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(contactController)
|
||||
}
|
||||
}
|
||||
}))
|
||||
@ -5516,7 +5523,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
private func presentPollCreation() {
|
||||
if case let .peer(peerId) = self.chatLocation {
|
||||
self.present(createPollController(context: self.context, peerId: peerId, completion: { [weak self] message in
|
||||
(self.navigationController as? NavigationController)?.pushViewController(createPollController(context: self.context, peerId: peerId, completion: { [weak self] message in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -5529,7 +5536,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
})
|
||||
strongSelf.sendMessages([message.withUpdatedReplyToMessageId(replyMessageId)])
|
||||
}), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@ -6345,7 +6352,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
self.chatDisplayNode.dismissInput()
|
||||
self.present(controller, in: .window(.root), blockInteraction: true)
|
||||
(self.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
}
|
||||
|
||||
private func openPeer(peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer, fromMessage: Message?) {
|
||||
@ -6462,7 +6469,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
self.chatDisplayNode.dismissInput()
|
||||
self.present(controller, in: .window(.root))
|
||||
(self.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
}
|
||||
default:
|
||||
break
|
||||
@ -7698,17 +7705,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
private func openScheduledMessages() {
|
||||
let controller = ChatControllerImpl(context: self.context, chatLocation: self.chatLocation, subject: .scheduledMessages)
|
||||
controller.navigationPresentation = .modal
|
||||
(self.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
//self.present(controller, in: .window(.root))
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
if !self.isDismissed {
|
||||
self.isDismissed = true
|
||||
self.chatDisplayNode.animateOut(completion: { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
})
|
||||
self.updateTransitionWhenPresentedAsModal?(0.0, .animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,13 +40,15 @@ public class ComposeController: ViewController {
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
|
||||
self.navigationPresentation = .modal
|
||||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
|
||||
self.title = self.presentationData.strings.Compose_NewMessage
|
||||
|
||||
self.isModalWhenInOverlay = true
|
||||
|
||||
//self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
|
||||
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
|
||||
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(cancelPressed))
|
||||
|
||||
@ -263,6 +265,6 @@ public class ComposeController: ViewController {
|
||||
}
|
||||
|
||||
@objc private func cancelPressed() {
|
||||
self.presentingViewController?.dismiss(animated: true, completion: nil)
|
||||
(self.navigationController as? NavigationController)?.filterController(self, animated: true)
|
||||
}
|
||||
}
|
||||
|
@ -271,19 +271,6 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
}))
|
||||
}
|
||||
|
||||
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?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dismissSearch() {
|
||||
self.deactivateSearch()
|
||||
}
|
||||
|
@ -261,12 +261,8 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
||||
}, stopLiveLocation: {
|
||||
params.context.liveLocationManager?.cancelLiveLocation(peerId: params.message.id.peerId)
|
||||
}, openUrl: params.openUrl)
|
||||
|
||||
if params.modal {
|
||||
params.present(controller, nil)
|
||||
} else {
|
||||
params.navigationController?.pushViewController(controller)
|
||||
}
|
||||
controller.navigationPresentation = .modal
|
||||
params.navigationController?.pushViewController(controller)
|
||||
return true
|
||||
case let .stickerPack(reference):
|
||||
let controller = StickerPackPreviewController(context: params.context, stickerPack: reference, parentNavigationController: params.navigationController)
|
||||
@ -381,7 +377,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
||||
return false
|
||||
}
|
||||
let controller = ThemePreviewController(context: params.context, previewTheme: theme, source: .media(.message(message: MessageReference(params.message), media: media)))
|
||||
params.present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
params.navigationController?.pushViewController(controller)
|
||||
}
|
||||
}
|
||||
return false
|
||||
@ -430,6 +426,7 @@ func openChatInstantPage(context: AccountContext, message: Message, sourcePeerTy
|
||||
}
|
||||
|
||||
let pageController = InstantPageController(context: context, webPage: webpage, sourcePeerType: sourcePeerType ?? .channel, anchor: anchor)
|
||||
pageController.navigationPresentation = .modal
|
||||
navigationController.pushViewController(pageController)
|
||||
}
|
||||
break
|
||||
@ -459,7 +456,7 @@ func openChatWallpaper(context: AccountContext, message: Message, present: @esca
|
||||
}
|
||||
}
|
||||
|
||||
func openChatTheme(context: AccountContext, message: Message, present: @escaping (ViewController, Any?) -> Void) {
|
||||
func openChatTheme(context: AccountContext, message: Message, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
for media in message.media {
|
||||
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
||||
let _ = (context.sharedContext.resolveUrl(account: context.account, url: content.url)
|
||||
@ -474,7 +471,7 @@ func openChatTheme(context: AccountContext, message: Message, present: @escaping
|
||||
if case let .theme(slug) = resolvedUrl, let file = file {
|
||||
if let path = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let theme = makePresentationTheme(data: data) {
|
||||
let controller = ThemePreviewController(context: context, previewTheme: theme, source: .slug(slug, file))
|
||||
present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
pushController(controller)
|
||||
}
|
||||
} else {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
@ -81,7 +81,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
}
|
||||
}
|
||||
dismissInput()
|
||||
present(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
|
||||
navigationController?.pushViewController(controller)
|
||||
case let .channelMessage(peerId, messageId):
|
||||
openPeer(peerId, .chat(textInputState: nil, subject: .message(messageId)))
|
||||
case let .stickerPack(name):
|
||||
@ -203,10 +203,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
continueWithPeer(peerId)
|
||||
}
|
||||
}
|
||||
if let navigationController = navigationController {
|
||||
context.sharedContext.applicationBindings.dismissNativeController()
|
||||
(navigationController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
|
||||
}
|
||||
navigationController?.pushViewController(controller)
|
||||
}
|
||||
case let .wallpaper(parameter):
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
@ -59,6 +59,8 @@ public class PeerMediaCollectionController: TelegramBaseController {
|
||||
|
||||
super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme).withUpdatedSeparatorColor(self.presentationData.theme.rootController.navigationBar.backgroundColor), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)), mediaAccessoryPanelVisibility: .specific(size: .compact), locationBroadcastPanelSource: .none)
|
||||
|
||||
self.navigationPresentation = .modalInLargeLayout
|
||||
|
||||
self.title = self.presentationData.strings.SharedMedia_TitleAll
|
||||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
@ -105,7 +107,18 @@ public class PeerMediaCollectionController: TelegramBaseController {
|
||||
return nil
|
||||
}, addToTransitionSurface: { view in
|
||||
if let strongSelf = self {
|
||||
strongSelf.mediaCollectionDisplayNode.view.insertSubview(view, aboveSubview: strongSelf.mediaCollectionDisplayNode.historyNode.view)
|
||||
var belowSubview: UIView?
|
||||
if let historyNode = strongSelf.mediaCollectionDisplayNode.historyNode as? ChatHistoryGridNode {
|
||||
if let lowestSectionNode = historyNode.lowestSectionNode() {
|
||||
belowSubview = lowestSectionNode.view
|
||||
}
|
||||
}
|
||||
strongSelf.mediaCollectionDisplayNode.historyNode
|
||||
if let belowSubview = belowSubview {
|
||||
strongSelf.mediaCollectionDisplayNode.historyNode.view.insertSubview(view, belowSubview: belowSubview)
|
||||
} else {
|
||||
strongSelf.mediaCollectionDisplayNode.historyNode.view.addSubview(view)
|
||||
}
|
||||
}
|
||||
}, openUrl: { url in
|
||||
self?.openUrl(url)
|
||||
@ -823,7 +836,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
|
||||
})
|
||||
}
|
||||
}
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,9 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
|
||||
self.navigationPresentation = .modal
|
||||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
|
||||
self.customTitle = params.title
|
||||
@ -178,7 +181,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
self.peerSelectionNode.animateIn()
|
||||
//self.peerSelectionNode.animateIn()
|
||||
}
|
||||
|
||||
override public func viewDidDisappear(_ animated: Bool) {
|
||||
@ -215,9 +218,4 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.peerSelectionNode.view.endEditing(true)
|
||||
self.peerSelectionNode.animateOut(completion: completion)
|
||||
}
|
||||
}
|
||||
|
@ -924,7 +924,9 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}
|
||||
|
||||
public func makePeerInfoController(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode) -> ViewController? {
|
||||
return peerInfoControllerImpl(context: context, peer: peer, mode: mode)
|
||||
let controller = peerInfoControllerImpl(context: context, peer: peer, mode: mode)
|
||||
controller?.navigationPresentation = .modalInLargeLayout
|
||||
return controller
|
||||
}
|
||||
|
||||
public func openExternalUrl(context: AccountContext, urlContext: OpenURLContext, url: String, forceExternal: Bool, presentationData: PresentationData, navigationController: NavigationController?, dismissInput: @escaping () -> Void) {
|
||||
@ -995,8 +997,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
openAddContactImpl(context: context, firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, label: label, present: present, pushController: pushController, completed: completed)
|
||||
}
|
||||
|
||||
public func openAddPersonContact(context: AccountContext, peerId: PeerId, present: @escaping (ViewController, Any?) -> Void) {
|
||||
openAddPersonContactImpl(context: context, peerId: peerId, present: present)
|
||||
public func openAddPersonContact(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
openAddPersonContactImpl(context: context, peerId: peerId, pushController: pushController, present: present)
|
||||
}
|
||||
|
||||
public func makeCreateGroupController(context: AccountContext, peerIds: [PeerId], initialTitle: String?, mode: CreateGroupMode, completion: ((PeerId, @escaping () -> Void) -> Void)?) -> ViewController {
|
||||
|
@ -472,12 +472,4 @@ public final class WebSearchController: ViewController {
|
||||
|
||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition)
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.navigationContentNode?.deactivate()
|
||||
self.controllerNode.animateOut(completion: { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user