2020-11-11 20:26:25 +00:00
//
// A c c o u n t S w i t c h i n g C o n t a i n e r V i e w C o n t r o l l e r . s w i f t
// T u s k e r
//
// C r e a t e d b y S h a d o w f a c t s o n 1 1 / 1 1 / 2 0 .
// C o p y r i g h t © 2 0 2 0 S h a d o w f a c t s . A l l r i g h t s r e s e r v e d .
//
import UIKit
2022-08-02 01:29:24 +00:00
import ScreenCorners
2023-03-05 19:35:25 +00:00
import UserAccounts
2020-11-11 20:26:25 +00:00
class AccountSwitchingContainerViewController : UIViewController {
2023-02-25 22:59:35 +00:00
private var currentAccountID : String
2020-11-11 20:26:25 +00:00
private ( set ) var root : TuskerRootViewController
2023-02-25 22:59:35 +00:00
private var userActivities : [ String : NSUserActivity ] = [ : ]
2023-03-05 19:35:25 +00:00
init ( root : TuskerRootViewController , for account : UserAccountInfo ) {
2023-02-25 22:59:35 +00:00
self . currentAccountID = account . id
2020-11-11 20:26:25 +00:00
self . root = root
super . init ( nibName : nil , bundle : nil )
}
required init ? ( coder : NSCoder ) {
fatalError ( " init(coder:) has not been implemented " )
}
override func viewDidLoad ( ) {
super . viewDidLoad ( )
embedChild ( root )
}
2023-03-05 19:35:25 +00:00
func setRoot ( _ newRoot : TuskerRootViewController , for account : UserAccountInfo , animating direction : AnimationDirection ) {
2020-11-11 20:26:25 +00:00
let oldRoot = self . root
if direction = = . none {
oldRoot . removeViewAndController ( )
}
2023-02-25 22:59:35 +00:00
if let activity = oldRoot . stateRestorationActivity ( ) {
stateRestorationLogger . debug ( " AccountSwitchingContainer: saving \( activity . activityType , privacy : . public ) for \( self . currentAccountID , privacy : . public ) " )
userActivities [ currentAccountID ] = activity
}
self . currentAccountID = account . id
2020-11-11 20:26:25 +00:00
self . root = newRoot
embedChild ( newRoot )
2023-02-25 22:59:35 +00:00
if let activity = userActivities . removeValue ( forKey : account . id ) {
stateRestorationLogger . debug ( " AccountSwitchingContainer: restoring \( activity . activityType , privacy : . public ) for \( account . id , privacy : . public ) " )
let context = StateRestorationUserActivityHandlingContext ( root : newRoot )
_ = activity . handleResume ( manager : UserActivityManager ( scene : view . window ! . windowScene ! , context : context ) )
context . finalize ( activity : activity )
}
2020-11-11 20:26:25 +00:00
if direction != . none {
2021-02-06 18:47:45 +00:00
if UIAccessibility . prefersCrossFadeTransitions {
2020-11-11 20:26:25 +00:00
newRoot . view . alpha = 0
UIView . animate ( withDuration : 0.4 , delay : 0 , options : . curveEaseInOut ) {
newRoot . view . alpha = 1
oldRoot . view . alpha = 0
} completion : { ( _ ) in
oldRoot . removeViewAndController ( )
}
} else {
let sign : CGFloat = direction = = . downwards ? - 1 : 1
let newInitialOffset = sign * view . bounds . height
2022-08-02 01:29:24 +00:00
let scale : CGFloat = 0.75
2020-11-11 20:26:25 +00:00
2022-08-02 01:29:24 +00:00
newRoot . view . transform = CGAffineTransform ( translationX : 0 , y : newInitialOffset ) . scaledBy ( x : 0.9 , y : 0.9 )
newRoot . view . layer . masksToBounds = true
newRoot . view . layer . cornerCurve = . continuous
newRoot . view . layer . cornerRadius = view . window ? . screen . displayCornerRadius ? ? 0
2020-11-11 20:26:25 +00:00
2022-08-02 01:29:24 +00:00
oldRoot . view . layer . masksToBounds = true
oldRoot . view . layer . cornerCurve = . continuous
oldRoot . view . layer . cornerRadius = view . window ? . screen . displayCornerRadius ? ? 0
// o n l y o n e e d g e i s a f f e c t e d i n e a c h d i r e c t i o n , i h a v e n o i d e a w h y
if direction = = . upwards {
oldRoot . additionalSafeAreaInsets . bottom = view . safeAreaInsets . bottom
} else {
oldRoot . additionalSafeAreaInsets . top = view . safeAreaInsets . top
}
UIView . animate ( withDuration : 0.4 , delay : 0 , options : . curveEaseInOut ) {
oldRoot . view . transform = CGAffineTransform ( translationX : 0 , y : - newInitialOffset ) . scaledBy ( x : scale , y : scale )
2020-11-11 20:26:25 +00:00
newRoot . view . transform = . identity
} completion : { ( _ ) in
oldRoot . removeViewAndController ( )
2022-08-02 01:29:24 +00:00
newRoot . view . layer . masksToBounds = false
2020-11-11 20:26:25 +00:00
}
}
}
}
}
extension AccountSwitchingContainerViewController {
enum AnimationDirection {
case none , downwards , upwards
}
}
extension AccountSwitchingContainerViewController : TuskerRootViewController {
2022-11-23 16:35:25 +00:00
func stateRestorationActivity ( ) -> NSUserActivity ? {
loadViewIfNeeded ( )
return root . stateRestorationActivity ( )
}
2023-02-25 18:55:46 +00:00
func compose ( editing draft : Draft ? , animated : Bool , isDucked : Bool ) {
2022-11-23 16:35:25 +00:00
loadViewIfNeeded ( )
2023-02-25 18:55:46 +00:00
root . compose ( editing : draft , animated : animated , isDucked : isDucked )
2022-11-23 16:35:25 +00:00
}
2023-02-25 18:55:46 +00:00
func select ( route : TuskerRoute , animated : Bool ) {
2021-05-24 23:07:47 +00:00
loadViewIfNeeded ( )
2023-02-25 18:55:46 +00:00
root . select ( route : route , animated : animated )
2020-11-11 20:26:25 +00:00
}
2023-02-25 18:55:46 +00:00
func getTabController ( tab : MainTabBarViewController . Tab ) -> UIViewController ? {
2021-05-24 23:07:47 +00:00
loadViewIfNeeded ( )
2023-02-25 18:55:46 +00:00
return root . getTabController ( tab : tab )
2020-11-11 20:26:25 +00:00
}
2023-02-25 18:55:46 +00:00
func getNavigationDelegate ( ) -> TuskerNavigationDelegate ? {
2021-05-24 23:07:47 +00:00
loadViewIfNeeded ( )
2023-02-25 18:55:46 +00:00
return root . getNavigationDelegate ( )
}
func getNavigationController ( ) -> NavigationControllerProtocol {
loadViewIfNeeded ( )
return root . getNavigationController ( )
2021-05-24 23:07:47 +00:00
}
func performSearch ( query : String ) {
2022-04-30 15:46:14 +00:00
loadViewIfNeeded ( )
2021-05-24 23:07:47 +00:00
root . performSearch ( query : query )
2020-11-11 20:26:25 +00:00
}
2022-04-30 15:46:14 +00:00
func presentPreferences ( completion : ( ( ) -> Void ) ? ) {
loadViewIfNeeded ( )
root . presentPreferences ( completion : completion )
}
2022-11-02 00:49:07 +00:00
func handleStatusBarTapped ( xPosition : CGFloat ) -> StatusBarTapActionResult {
loadViewIfNeeded ( )
// TODO: c h e c k i f f a s t a c c o u n t s w i t c h e r i s b e i n g p r e s e n t e d ?
return root . handleStatusBarTapped ( xPosition : xPosition )
}
2020-11-11 20:26:25 +00:00
}
2021-08-15 14:37:04 +00:00
extension AccountSwitchingContainerViewController : BackgroundableViewController {
func sceneDidEnterBackground ( ) {
if let backgroundable = root as ? BackgroundableViewController {
backgroundable . sceneDidEnterBackground ( )
}
}
}