Skip to content

Commit

Permalink
Merge pull request #1202 from Infomaniak/releaseAPIV3-modernAccountSw…
Browse files Browse the repository at this point in the history
…itch

feat: Modern account switcher
  • Loading branch information
adrien-coye committed Jul 3, 2024
2 parents 3b0b796 + 54cc427 commit 24a06dd
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 11 deletions.
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
62 changes: 57 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,64 @@ extension MainTabViewController: MainTabBarDelegate {
floatingPanelViewController.trackAndObserve(scrollView: plusButtonFloatingPanel.tableView)
present(floatingPanelViewController, animated: true)
}

func avatarLongTouch() {
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()

_ = 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
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
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)
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

0 comments on commit 24a06dd

Please sign in to comment.