Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Modern account switcher #1202

Merged
merged 9 commits into from
Jul 3, 2024
27 changes: 23 additions & 4 deletions kDrive/AppRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ public protocol RouterFileNavigable {
navigationController: UINavigationController,
animated: Bool
)

/// Present the SwitchAccountViewController
/// - Parameters:
/// - navigationController: The navigation controller to use
/// - animated: Should be animated
@MainActor func presentAccountViewController(
navigationController: UINavigationController,
animated: Bool
)
}

/// Something that can set an arbitrary RootView controller
Expand Down Expand Up @@ -164,7 +173,9 @@ public struct AppRouter: AppNavigable {
@LazyInjectService private var reviewManager: ReviewManageable
@LazyInjectService private var availableOfflineManager: AvailableOfflineManageable
@LazyInjectService private var accountManager: AccountManageable
@LazyInjectService private var backgroundUploadSessionManager: BackgroundUploadSessionManager

@LazyInjectService var backgroundDownloadSessionManager: BackgroundDownloadSessionManager
@LazyInjectService var backgroundUploadSessionManager: BackgroundUploadSessionManager

/// Get the current window from the app scene
@MainActor private var window: UIWindow? {
Expand Down Expand Up @@ -521,7 +532,7 @@ public struct AppRouter: AppNavigable {
}

rootViewController.dismiss(animated: false)
rootViewController.selectedIndex = MainTabIndex.profile.rawValue
rootViewController.selectedIndex = MainTabBarIndex.profile.rawValue

guard let navController = rootViewController.selectedViewController as? UINavigationController else {
return
Expand Down Expand Up @@ -677,7 +688,7 @@ public struct AppRouter: AppNavigable {
}

rootViewController.dismiss(animated: false) {
rootViewController.selectedIndex = MainTabIndex.files.rawValue
rootViewController.selectedIndex = MainTabBarIndex.files.rawValue

guard let navController = rootViewController.selectedViewController as? UINavigationController,
let viewController = navController.topViewController as? FileListViewController else {
Expand Down Expand Up @@ -767,6 +778,14 @@ public struct AppRouter: AppNavigable {
animated: Bool
) {
let storeViewController = StoreViewController.instantiate(driveFileManager: driveFileManager)
navigationController.pushViewController(storeViewController, animated: false)
navigationController.pushViewController(storeViewController, animated: animated)
}

@MainActor public func presentAccountViewController(
navigationController: UINavigationController,
animated: Bool
) {
let accountViewController = SwitchUserViewController.instantiate()
navigationController.pushViewController(accountViewController, animated: animated)
}
}
2 changes: 1 addition & 1 deletion kDrive/UI/Controller/Files/FilePresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ final class FilePresenter {
viewController.navigationController?.popToRootViewController(animated: false)

rootViewController.dismiss(animated: false) {
rootViewController.selectedIndex = MainTabIndex.files.rawValue
rootViewController.selectedIndex = MainTabBarIndex.files.rawValue

guard let navigationController = rootViewController.selectedViewController as? UINavigationController else {
return
Expand Down
64 changes: 59 additions & 5 deletions kDrive/UI/Controller/MainTabViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,27 @@ import kDriveResources
import UIKit

/// Enum to explicit tab names
enum MainTabIndex: Int {
public enum MainTabBarIndex: Int {
case home = 0
case files = 1
case gallery = 3
case profile = 4
}

class MainTabViewController: UITabBarController, Restorable, PlusButtonObserver {
/// Tracking the last selection date to detect double tap
private var lastInteraction: Date?

/// Time between two tap events that feels alright for a double tap
private static let doubleTapInterval = TimeInterval(0.350)

// swiftlint:disable:next weak_delegate
var photoPickerDelegate = PhotoPickerDelegate()

@LazyInjectService var accountManager: AccountManageable
@LazyInjectService var uploadQueue: UploadQueue
@LazyInjectService var fileImportHelper: FileImportHelper
@LazyInjectService var router: AppNavigable

let driveFileManager: DriveFileManager

Expand Down Expand Up @@ -243,19 +250,66 @@ extension MainTabViewController: MainTabBarDelegate {
floatingPanelViewController.trackAndObserve(scrollView: plusButtonFloatingPanel.tableView)
present(floatingPanelViewController, animated: true)
}

func avatarLongTouch() {
let viewControllers = viewControllers
adrien-coye marked this conversation as resolved.
Show resolved Hide resolved
guard let rootNavigationController = viewControllers?[safe: MainTabBarIndex.profile.rawValue] as? UINavigationController
else {
return
}

let generator = UIImpactFeedbackGenerator(style: .light)
generator.impactOccurred()

selectedIndex = MainTabBarIndex.profile.rawValue

router.presentAccountViewController(navigationController: rootNavigationController, animated: true)
}

func avatarDoubleTap() {
accountManager.switchToNextAvailableAccount()
guard let accountManager = accountManager.currentDriveFileManager else {
return
}

let generator = UIImpactFeedbackGenerator(style: .light)
generator.impactOccurred()

@InjectService var router: AppNavigable
adrien-coye marked this conversation as resolved.
Show resolved Hide resolved
_ = router.showMainViewController(driveFileManager: accountManager,
selectedIndex: MainTabBarIndex.profile.rawValue)
}
}

// MARK: - Tab bar controller delegate

extension MainTabViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if let homeViewController = (viewController as? UINavigationController)?.topViewController as? HomeViewController {
guard let navigationController = viewController as? UINavigationController else {
return false
}

defer {
lastInteraction = Date()
}

let topViewController = navigationController.topViewController
if let homeViewController = topViewController as? HomeViewController {
homeViewController.presentedFromTabBar()
}

if tabBarController.selectedViewController == viewController,
let viewController = (viewController as? UINavigationController)?.topViewController as? TopScrollable {
viewController.scrollToTop()
if tabBarController.selectedViewController == viewController {
// Detect double tap on menu
valentinperignon marked this conversation as resolved.
Show resolved Hide resolved
if topViewController as? MenuViewController != nil,
let lastDate = lastInteraction,
Date().timeIntervalSince(lastDate) <= Self.doubleTapInterval {
avatarDoubleTap()
return true
}

if let viewController = topViewController as? TopScrollable {
viewController.scrollToTop()
}
}

return true
Expand Down
2 changes: 1 addition & 1 deletion kDrive/UI/Controller/Menu/StoreSuccessViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class StoreSuccessViewController: UIViewController {
if let rootViewController = sender.window?.rootViewController as? MainTabViewController {
rootViewController.dismiss(animated: true)
(rootViewController.selectedViewController as? UINavigationController)?.popToRootViewController(animated: true)
rootViewController.selectedIndex = MainTabIndex.home.rawValue
rootViewController.selectedIndex = MainTabBarIndex.home.rawValue
} else {
dismiss(animated: true)
}
Expand Down
25 changes: 25 additions & 0 deletions kDrive/UI/View/Menu/MainTabBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import InfomaniakDI
adrien-coye marked this conversation as resolved.
Show resolved Hide resolved
import kDriveCore
import kDriveResources
import UIKit

/// Delegation from MainTabBar towards MainTabViewController
protocol MainTabBarDelegate: AnyObject {
func plusButtonPressed()
func avatarLongTouch()
func avatarDoubleTap()
}

final class MainTabBar: UITabBar {
Expand Down Expand Up @@ -73,6 +77,7 @@ final class MainTabBar: UITabBar {
self.shapeLayer = shapeLayer
setupBackgroundGradient()
setupMiddleButton()
setupGestureRecognizer()
}

override func layoutSubviews() {
Expand Down Expand Up @@ -163,6 +168,26 @@ final class MainTabBar: UITabBar {
centerButton.addTarget(self, action: #selector(centerButtonAction), for: .touchUpInside)
}

private func setupGestureRecognizer() {
let longTouch = UILongPressGestureRecognizer(target: self,
action: #selector(Self.handleLongTouch(recognizer:)))
addGestureRecognizer(longTouch)
}

@objc func handleLongTouch(recognizer: UITapGestureRecognizer) {
guard recognizer.state == .began else {
return
}

// Touch is over the 5th button's x position
let touchPoint = recognizer.location(in: self)
adrien-coye marked this conversation as resolved.
Show resolved Hide resolved
guard touchPoint.x > bounds.width / 5 * 4 else {
return
}

tabDelegate?.avatarLongTouch()
}

@objc func centerButtonAction(sender: UIButton) {
tabDelegate?.plusButtonPressed()
}
Expand Down
31 changes: 31 additions & 0 deletions kDriveCore/Data/Cache/AccountManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public protocol AccountManageable: AnyObject {
func loadAccounts() -> [Account]
func saveAccounts()
func switchAccount(newAccount: Account)
func switchToNextAvailableAccount()
func setCurrentDriveForCurrentAccount(for driveId: Int, userId: Int)
func addAccount(account: Account, token: ApiToken)
func removeAccount(toDeleteAccount: Account)
Expand Down Expand Up @@ -374,6 +375,36 @@ public class AccountManager: RefreshTokenDelegate, AccountManageable {
saveAccounts()
}

public func switchToNextAvailableAccount() {
guard let nextAccount = nextAvailableAccount else {
return
}

switchAccount(newAccount: nextAccount)
}

private var nextAvailableAccount: Account? {
let allAccounts = accounts.values
guard allAccounts.count > 1 else {
return nil
}

guard let currentAccount else {
return nil
}

guard let currentIndex = allAccounts.firstIndex(of: currentAccount) else {
return nil
}

let nextIndex = currentIndex + 1
guard let nextAccount = allAccounts[safe: nextIndex] else {
return allAccounts.first
}

return nextAccount
}

private func setCurrentAccount(account: Account) {
currentAccount = account
currentUserId = account.userId
Expand Down
2 changes: 2 additions & 0 deletions kDriveTests/kDrive/Launch/MockAccountManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class MockAccountManager: AccountManageable, RefreshTokenDelegate {

func switchAccount(newAccount: Account) {}

func switchToNextAvailableAccount() {}

func setCurrentDriveForCurrentAccount(for driveId: Int, userId: Int) {}

func addAccount(account: Account, token apiToken: ApiToken) {}
Expand Down
Loading